diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 75f1707..edd46bb 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1 @@ -* @xcp-ng-rpms/storage -* @xcp-ng-rpms/os-platform-release +* @xcp-ng-rpms/storage @xcp-ng-rpms/os-platform-release diff --git a/SOURCES/0001-Add-an-option-to-never-resolve-parent-path-when-vhd-.patch b/SOURCES/0001-Add-an-option-to-never-resolve-parent-path-when-vhd-.patch deleted file mode 100644 index 4901067..0000000 --- a/SOURCES/0001-Add-an-option-to-never-resolve-parent-path-when-vhd-.patch +++ /dev/null @@ -1,144 +0,0 @@ -From e371dd87ad668e156309992bc83b96ad34fb4d38 Mon Sep 17 00:00:00 2001 -From: Ronan Abhamon -Date: Thu, 16 Mar 2023 15:55:07 +0100 -Subject: [PATCH 1/2] Add an option to never resolve parent path when vhd-util - query is called - -Signed-off-by: Ronan Abhamon ---- - include/libvhd.h | 1 + - vhd/lib/libvhd.c | 21 +++++++++++++++++++-- - vhd/lib/vhd-util-query.c | 36 +++++++++++++++++++++--------------- - 3 files changed, 41 insertions(+), 17 deletions(-) - -diff --git a/include/libvhd.h b/include/libvhd.h -index 7365918..3932547 100644 ---- a/include/libvhd.h -+++ b/include/libvhd.h -@@ -341,6 +341,7 @@ int vhd_initialize_header_parent_name(vhd_context_t *, const char *); - int vhd_write_parent_locators(vhd_context_t *, const char *); - int vhd_parent_locator_count(vhd_context_t *); - int vhd_parent_locator_get(vhd_context_t *, char **); -+int vhd_parent_locator_unresolved_get(vhd_context_t *, char **); - int vhd_custom_parent_set(vhd_context_t *vhd, const char *parent); - - int vhd_parent_locator_read(vhd_context_t *, vhd_parent_locator_t *, char **); -diff --git a/vhd/lib/libvhd.c b/vhd/lib/libvhd.c -index 73be3ee..830e95a 100644 ---- a/vhd/lib/libvhd.c -+++ b/vhd/lib/libvhd.c -@@ -1782,8 +1782,8 @@ out: - return err; - } - --int --vhd_parent_locator_get(vhd_context_t *ctx, char **parent) -+static int -+vhd_parent_locator_get_impl(vhd_context_t *ctx, char **parent, bool resolve_parent) - { - int i, n, err; - char *name, *location; -@@ -1807,6 +1807,11 @@ vhd_parent_locator_get(vhd_context_t *ctx, char **parent) - if (_err) - continue; - -+ if (!resolve_parent) { -+ *parent = name; -+ return 0; -+ } -+ - err = vhd_find_parent(ctx, name, &location); - if (err) - VHDLOG("%s: couldn't find parent %s (%d)\n", -@@ -1822,6 +1827,18 @@ vhd_parent_locator_get(vhd_context_t *ctx, char **parent) - return err; - } - -+int -+vhd_parent_locator_get(vhd_context_t *ctx, char **parent) -+{ -+ return vhd_parent_locator_get_impl(ctx, parent, true); -+} -+ -+int -+vhd_parent_locator_unresolved_get(vhd_context_t *ctx, char **parent) -+{ -+ return vhd_parent_locator_get_impl(ctx, parent, false); -+} -+ - /** - * Overrides the parent with the supplied one. - * -diff --git a/vhd/lib/vhd-util-query.c b/vhd/lib/vhd-util-query.c -index 9aa131e..0cd0416 100644 ---- a/vhd/lib/vhd-util-query.c -+++ b/vhd/lib/vhd-util-query.c -@@ -46,25 +46,26 @@ vhd_util_query(int argc, char **argv) - char *name; - vhd_context_t vhd; - off64_t currsize; -- int ret, err, c, size, physize, parent, fields, depth, fastresize, marker, allocated; -- -- name = NULL; -- size = 0; -- physize = 0; -- parent = 0; -- fields = 0; -- depth = 0; -- fastresize = 0; -- marker = 0; -- allocated = 0; -- -+ int ret, err, c, size, physize, parent, fields, depth, fastresize, marker, allocated, resolve_parent; -+ -+ name = NULL; -+ size = 0; -+ physize = 0; -+ parent = 0; -+ fields = 0; -+ depth = 0; -+ fastresize = 0; -+ marker = 0; -+ allocated = 0; -+ resolve_parent = 1; -+ - if (!argc || !argv) { - err = -EINVAL; - goto usage; - } - - optind = 0; -- while ((c = getopt(argc, argv, "n:vspfdSmah")) != -1) { -+ while ((c = getopt(argc, argv, "n:vspfdSmauh")) != -1) { - switch (c) { - case 'n': - name = optarg; -@@ -93,6 +94,9 @@ vhd_util_query(int argc, char **argv) - case 'a': - allocated = 1; - break; -+ case 'u': -+ resolve_parent = 0; -+ break; - case 'h': - err = 0; - goto usage; -@@ -132,7 +136,7 @@ vhd_util_query(int argc, char **argv) - else { - char *pname; - -- ret = vhd_parent_locator_get(&vhd, &pname); -+ ret = resolve_parent ? vhd_parent_locator_get(&vhd, &pname) : vhd_parent_locator_unresolved_get(&vhd, &pname); - if (ret) - printf("query failed\n"); - else { -@@ -212,6 +216,8 @@ usage: - "[-s print physical utilization (bytes)] [-p print parent] " - "[-f print fields] [-m print marker] [-d print chain depth] " - "[-S print max virtual size (MB) for fast resize] " -- "[-a print allocated block count] [-h help]\n"); -+ "[-a print allocated block count] " -+ "[-u don't resolve parent path] " -+ "[-h help]\n"); - return err; - } diff --git a/SOURCES/0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch b/SOURCES/0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch similarity index 81% rename from SOURCES/0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch rename to SOURCES/0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch index 3733839..751264d 100644 --- a/SOURCES/0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch +++ b/SOURCES/0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch @@ -1,7 +1,7 @@ -From 7c168ec7818aa74bf81ae27ddd808c227b1d2261 Mon Sep 17 00:00:00 2001 +From d8cda8eb7a7404102b10a05e1226129e5c424d0e Mon Sep 17 00:00:00 2001 From: Ronan Abhamon Date: Wed, 18 Dec 2024 16:55:26 +0100 -Subject: [PATCH 2/2] Add an option to use backup footer when vhd-util query is +Subject: [PATCH] Add an option to use backup footer when vhd-util query is called This option already exists on td-util but it was not implemented @@ -9,11 +9,11 @@ on vhd-util. Signed-off-by: Ronan Abhamon --- - vhd/lib/vhd-util-query.c | 36 ++++++++++++++++++++---------------- - 1 file changed, 20 insertions(+), 16 deletions(-) + vhd/lib/vhd-util-query.c | 38 +++++++++++++++++++++----------------- + 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/vhd/lib/vhd-util-query.c b/vhd/lib/vhd-util-query.c -index 0cd0416..13bd563 100644 +index 0cd04166..3aca668c 100644 --- a/vhd/lib/vhd-util-query.c +++ b/vhd/lib/vhd-util-query.c @@ -43,29 +43,29 @@ @@ -80,6 +80,15 @@ index 0cd0416..13bd563 100644 if (err) { printf("error opening %s: %d\n", name, err); return err; +@@ -207,7 +210,7 @@ vhd_util_query(int argc, char **argv) + max_size = vhd.header.max_bat_size << (VHD_BLOCK_SHIFT - 20); + printf("%"PRIu64"\n", max_size); + } +- ++ + vhd_close(&vhd); + return err; + @@ -218,6 +221,7 @@ usage: "[-S print max virtual size (MB) for fast resize] " "[-a print allocated block count] " diff --git a/SOURCES/0002-tapdisk-deduplicate-double-assignment-code.patch b/SOURCES/0002-tapdisk-deduplicate-double-assignment-code.patch new file mode 100644 index 0000000..87ee021 --- /dev/null +++ b/SOURCES/0002-tapdisk-deduplicate-double-assignment-code.patch @@ -0,0 +1,24 @@ +From da68419a7008aa37563ffe5b3c7fce5b455edb62 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:07 +0100 +Subject: [PATCH] tapdisk: deduplicate double assignment code + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-server.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/tapdisk-server.c b/drivers/tapdisk-server.c +index 27a42198..a165d2f2 100644 +--- a/drivers/tapdisk-server.c ++++ b/drivers/tapdisk-server.c +@@ -788,7 +788,6 @@ tapdisk_server_complete(void) + server.rw_backend = get_libaio_backend(); + server.ro_backend = get_libaio_backend(); + +- server.rw_backend = get_libaio_backend(); + err = tapdisk_server_init_aio(); + if (err) + goto fail; diff --git a/SOURCES/0003-blktap-fix-a-typo-in-libaio-backend.h-header.patch b/SOURCES/0003-blktap-fix-a-typo-in-libaio-backend.h-header.patch new file mode 100644 index 0000000..108b58a --- /dev/null +++ b/SOURCES/0003-blktap-fix-a-typo-in-libaio-backend.h-header.patch @@ -0,0 +1,31 @@ +From 45658a3d9f323a6f1bc512297bebf487287d5f67 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:11 +0100 +Subject: [PATCH] blktap: fix a typo in libaio-backend.h header + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/libaio-backend.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/libaio-backend.h b/drivers/libaio-backend.h +index 2759307d..21707c4a 100644 +--- a/drivers/libaio-backend.h ++++ b/drivers/libaio-backend.h +@@ -29,7 +29,7 @@ + */ + + #ifndef LIBAIO_BACKEND_H +-#define LIBAIO_BACLEND_H ++#define LIBAIO_BACKEND_H + + #include + +@@ -43,4 +43,4 @@ enum { + + struct backend* get_libaio_backend(); + +-#endif /* LIBAIO_BACLEND_H */ ++#endif /* LIBAIO_BACKEND_H */ diff --git a/SOURCES/0004-tapdisk-document-final-param-in-__tapdisk_xenblkif_r.patch b/SOURCES/0004-tapdisk-document-final-param-in-__tapdisk_xenblkif_r.patch new file mode 100644 index 0000000..889d1d9 --- /dev/null +++ b/SOURCES/0004-tapdisk-document-final-param-in-__tapdisk_xenblkif_r.patch @@ -0,0 +1,26 @@ +From 2de5a44347e224ec3be404860d5d98e6fba1b1ff Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Mon, 20 Jan 2025 14:45:35 +0100 +Subject: [PATCH] tapdisk: document final param in + __tapdisk_xenblkif_request_cb comment + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index e1ce9fd7..178985f3 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -591,7 +591,7 @@ out: + * @param vreq the completed request + * @param error status of the request + * @param token token previously associated with this request +- * @param final TODO ? ++ * @param final controls whether the other end should be notified + */ + static inline void + __tapdisk_xenblkif_request_cb(struct td_vbd_request * const vreq, diff --git a/SOURCES/0005-tapdisk-use-tapdisk_vbd_for_each_blkif-abstraction.patch b/SOURCES/0005-tapdisk-use-tapdisk_vbd_for_each_blkif-abstraction.patch new file mode 100644 index 0000000..e7064ee --- /dev/null +++ b/SOURCES/0005-tapdisk-use-tapdisk_vbd_for_each_blkif-abstraction.patch @@ -0,0 +1,25 @@ +From 27d453c5eb2f025b10e7c32ae9239e9d18ce66ab Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 15 Jan 2025 16:33:02 +0100 +Subject: [PATCH] tapdisk: use tapdisk_vbd_for_each_blkif abstraction + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-control.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index c8b9d826..506b203d 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -898,7 +898,7 @@ tapdisk_control_close_image(struct tapdisk_ctl_conn *conn, + tapdisk_nbdserver_pause(vbd->nbdserver_new, true); + + err = 0; +- list_for_each_entry_safe(blkif, _blkif, &vbd->rings, entry) { ++ tapdisk_vbd_for_each_blkif(vbd, blkif, _blkif) { + + DPRINTF("implicitly disconnecting ring %p domid=%d, devid=%d\n", + blkif, blkif->domid, blkif->devid); diff --git a/SOURCES/0006-blkif-Avoid-use-after-free-on-BLKIF_OP_WRITE_BARRIER.patch b/SOURCES/0006-blkif-Avoid-use-after-free-on-BLKIF_OP_WRITE_BARRIER.patch new file mode 100644 index 0000000..eea2e42 --- /dev/null +++ b/SOURCES/0006-blkif-Avoid-use-after-free-on-BLKIF_OP_WRITE_BARRIER.patch @@ -0,0 +1,77 @@ +From 6ed8043712dbc18635fa10d879a9a04eaba7eb2c Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Mon, 20 Jan 2025 18:22:07 +0100 +Subject: [PATCH] blkif: Avoid use-after-free on BLKIF_OP_WRITE_BARRIER request + +Function tapdisk_xenblkif_make_vbd_request will release +tapreq->msg and reinsert it in the reqs_free array if the +request is a BLKIF_OP_WRITE_BARRIER without any segment. +But tapdisk_xenblkif_queue_request will use it to check if +it must send the request to VBD layer. + +This bug triggers a crash when DEBUG is defined in td-reqs.c +because DEBUG will poison the msg memory. +Without DEBUG, the code reads freed memory that still contains +unchanged data. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index 178985f3..c35b02c6 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -190,17 +190,21 @@ static void + tapdisk_xenblkif_free_request(struct td_xenblkif * const blkif, + struct td_xenblkif_req * const tapreq) + { ++ int put_bufcache; ++ + ASSERT(blkif); + ASSERT(tapreq); + ASSERT(blkif->n_reqs_free < blkif->ring_size); + ++ put_bufcache = tapreq->msg.nr_segments != 0; ++ + #ifdef DEBUG + memset(&tapreq->msg, BLKIF_MSG_POISON, sizeof(tapreq->msg)); + #endif + + blkif->reqs_free[blkif->ring_size - (++blkif->n_reqs_free)] = &tapreq->msg; + +- if (likely(tapreq->msg.nr_segments)) ++ if (likely(put_bufcache)) + td_xenblkif_bufcache_put(blkif, tapreq->vma); + } + +@@ -829,11 +833,19 @@ tapdisk_xenblkif_queue_request(struct td_xenblkif * const blkif, + blkif_request_t *msg, struct td_xenblkif_req *tapreq) + { + int err; ++ int queue_request; + + ASSERT(blkif); + ASSERT(msg); + ASSERT(tapreq); + ++ queue_request = tapreq->msg.nr_segments != 0; ++ ++ /* ++ * Do not use tapreq after tapdisk_xenblkif_make_vbd_request ++ * because this function can release tapreq->msg and reinsert it ++ * in the reqs_free array. ++ */ + err = tapdisk_xenblkif_make_vbd_request(blkif, tapreq); + if (unlikely(err)) { + /* TODO log error */ +@@ -841,7 +853,7 @@ tapdisk_xenblkif_queue_request(struct td_xenblkif * const blkif, + return err; + } + +- if (likely(tapreq->msg.nr_segments)) { ++ if (likely(queue_request)) { + err = tapdisk_vbd_queue_request(blkif->vbd, &tapreq->vreq); + if (unlikely(err)) { + /* TODO log error */ diff --git a/SOURCES/0007-tapdisk-vbd-remove-double-assignment-of-error-variab.patch b/SOURCES/0007-tapdisk-vbd-remove-double-assignment-of-error-variab.patch new file mode 100644 index 0000000..03dccce --- /dev/null +++ b/SOURCES/0007-tapdisk-vbd-remove-double-assignment-of-error-variab.patch @@ -0,0 +1,33 @@ +From a7c7447ed112f6106cbd270ba8067b4df2378f08 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Mon, 20 Jan 2025 14:48:57 +0100 +Subject: [PATCH] tapdisk-vbd: remove double assignment of error variable + +fail label already set vreq->error with err, so remove the +first assignment in both paths that lead to fail label. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-vbd.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 84385704..433375d6 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -1527,13 +1527,11 @@ tapdisk_vbd_issue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + + err = tapdisk_vbd_check_queue(vbd); + if (err) { +- vreq->error = err; + goto fail; + } + + err = tapdisk_image_check_request(image, vreq); + if (err) { +- vreq->error = err; + goto fail; + } + diff --git a/SOURCES/0008-tapdisk-replace-flag-number-by-its-name.patch b/SOURCES/0008-tapdisk-replace-flag-number-by-its-name.patch new file mode 100644 index 0000000..6847f61 --- /dev/null +++ b/SOURCES/0008-tapdisk-replace-flag-number-by-its-name.patch @@ -0,0 +1,25 @@ +From a99dad47e85f999c9029d433e1b58a9a6fa09892 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Tue, 21 Jan 2025 10:34:16 +0100 +Subject: [PATCH] tapdisk: replace flag number by its name + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-disktype.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/tapdisk-disktype.h b/drivers/tapdisk-disktype.h +index 2490cce4..bb6a8d34 100644 +--- a/drivers/tapdisk-disktype.h ++++ b/drivers/tapdisk-disktype.h +@@ -61,7 +61,7 @@ extern const disk_info_t *tapdisk_disk_types[]; + extern const struct tap_disk *tapdisk_disk_drivers[]; + + /* one single controller for all instances of disk type */ +-#define DISK_TYPE_SINGLE_CONTROLLER (1<<0) ++/*#define DISK_TYPE_SINGLE_CONTROLLER (1<<0) - Deprecated */ + + /* filter driver without physical image data */ + #define DISK_TYPE_FILTER (1<<1) diff --git a/SOURCES/0009-tapdisk-set-generic-TAPDISK_MESSAGE_MAX-limit-inside.patch b/SOURCES/0009-tapdisk-set-generic-TAPDISK_MESSAGE_MAX-limit-inside.patch new file mode 100644 index 0000000..8d36bf3 --- /dev/null +++ b/SOURCES/0009-tapdisk-set-generic-TAPDISK_MESSAGE_MAX-limit-inside.patch @@ -0,0 +1,45 @@ +From 1a8b94001cde20402e268e33263b8fb3a407a9a7 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 13 Feb 2025 14:52:10 +0100 +Subject: [PATCH] tapdisk: set generic TAPDISK_MESSAGE_MAX limit inside the + enum + +This avoids to forget to update the TAPDISK_MESSAGE_MAX define. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-control.c | 2 +- + include/tapdisk-message.h | 3 +-- + 2 files changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index 506b203d..91ffe097 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -1382,7 +1382,7 @@ tapdisk_control_receive_request(struct tapdisk_ctl_conn *conn) + if (err) + goto invalid; + +- if (conn->request.type > TAPDISK_MESSAGE_EXIT) ++ if (conn->request.type >= TAPDISK_MESSAGE_MAX) + goto invalid; + + conn->info = &message_infos[conn->request.type]; +diff --git a/include/tapdisk-message.h b/include/tapdisk-message.h +index dceddbbe..0f73e0c9 100644 +--- a/include/tapdisk-message.h ++++ b/include/tapdisk-message.h +@@ -233,10 +233,9 @@ enum tapdisk_message_id { + TAPDISK_MESSAGE_DISK_INFO, + TAPDISK_MESSAGE_DISK_INFO_RSP, + TAPDISK_MESSAGE_EXIT, ++ TAPDISK_MESSAGE_MAX /* This value must be the last. */ + }; + +-#define TAPDISK_MESSAGE_MAX TAPDISK_MESSAGE_EXIT +- + static inline char * + tapdisk_message_name(enum tapdisk_message_id id) + { diff --git a/SOURCES/0010-tapdisk-remove-unused-file-tapdisk-diff.c.patch b/SOURCES/0010-tapdisk-remove-unused-file-tapdisk-diff.c.patch new file mode 100644 index 0000000..a75cc93 --- /dev/null +++ b/SOURCES/0010-tapdisk-remove-unused-file-tapdisk-diff.c.patch @@ -0,0 +1,851 @@ +From b90bdb551e1605ac319ed3024d0c5d26297d1e24 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Oct 2025 18:43:42 +0200 +Subject: [PATCH] tapdisk: remove unused file tapdisk-diff.c + +Also remove request_list attribute of VBD structure only used in +tapdisk-diff.c. This file is not compiled anymore. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-diff.c | 815 ----------------------------------------- + drivers/tapdisk-vbd.h | 2 - + 2 files changed, 817 deletions(-) + delete mode 100644 drivers/tapdisk-diff.c + +diff --git a/drivers/tapdisk-diff.c b/drivers/tapdisk-diff.c +deleted file mode 100644 +index a616dd58..00000000 +--- a/drivers/tapdisk-diff.c ++++ /dev/null +@@ -1,815 +0,0 @@ +-/* +- * Copyright (c) 2016, Citrix Systems, Inc. +- * +- * All rights reserved. +- * +- * Redistribution and use in source and binary forms, with or without +- * modification, are permitted provided that the following conditions are met: +- * +- * 1. Redistributions of source code must retain the above copyright +- * notice, this list of conditions and the following disclaimer. +- * 2. Redistributions in binary form must reproduce the above copyright +- * notice, this list of conditions and the following disclaimer in the +- * documentation and/or other materials provided with the distribution. +- * 3. Neither the name of the copyright holder nor the names of its +- * contributors may be used to endorse or promote products derived from +- * this software without specific prior written permission. +- * +- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +- */ +- +-#ifdef HAVE_CONFIG_H +-#include "config.h" +-#endif +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "list.h" +-#include "scheduler.h" +-#include "tapdisk-vbd.h" +-#include "tapdisk-server.h" +-#include "tapdisk-disktype.h" +-#include "timeout-math.h" +-#include "libvhd.h" +- +-#define POLL_READ 0 +-#define POLL_WRITE 1 +- +-#define SPB_SHIFT (VHD_BLOCK_SHIFT - SECTOR_SHIFT) +- +-/* +- * we have to use half the max number of requests because we're using the same +- * tapdisk server for both streams and all the parents will be shared. If we +- * issue more than MAX_REQUESTS/2 requests, the vhd_state will run out of +- * vhd_request's and return EBUSY, which we don't handle here. However, even +- * with MAX_REQUESTS/2 we can still run out of vhd_request's because of +- * splitting: if some sectors spanned by a segment are in a parent, a segment +- * could be split into at most N/2 vhd_request's, where N is the number of +- * sectors per segment. Therefore, if we use 11 segments, we need to divide +- * MAX_REQUESTS by 11/2=6 on top of that. If we don't, we'd have to handle +- * EBUSY by retrying here. +- */ +-#define MAX_SEGMENTS 8 +-#define MAX_STREAM_REQUESTS (MAX_REQUESTS / 2 / (MAX_SEGMENTS / 2)) +- +-struct tapdisk_stream_poll { +- int pipe[2]; +- int set; +-}; +- +-struct tapdisk_stream_request { +- uint64_t sec; +- uint32_t secs; +- uint64_t seqno; +- blkif_request_t blkif_req; +- struct list_head next; +-}; +- +-struct tapdisk_stream { +- td_vbd_t *vbd; +- +- unsigned int id; +- +- int err; +- +- uint64_t cur; +- uint64_t start; +- uint64_t end; +- +- uint64_t started; +- uint64_t completed; +- +- struct tapdisk_stream_poll poll; +- event_id_t enqueue_event_id; +- +- struct list_head free_list; +- struct list_head pending_list; +- struct list_head completed_list; +- +- struct tapdisk_stream_request requests[MAX_STREAM_REQUESTS]; +-}; +- +-static unsigned int tapdisk_stream_count; +- +-static void tapdisk_stream_close_image(struct tapdisk_stream *); +- +-static char *program; +-static struct tapdisk_stream stream1, stream2; +-static vhd_context_t vhd1; +- +-static void +-usage(FILE *stream) +-{ +- printf("usage: %s <-n type:/path/to/image> <-m type:/path/to/image>\n", +- program); +-} +- +-static int +-open_vhd(const char *path, vhd_context_t *vhd) +-{ +- int err; +- +- err = vhd_open(vhd, path, VHD_OPEN_RDONLY); +- if (err) { +- printf("error opening %s: %d\n", path, err); +- return err; +- } +- +- err = vhd_get_bat(vhd); +- if (err) +- { +- printf("error reading BAT for %s: %d\n", path, err); +- vhd_close(vhd); +- return err; +- } +- +- return 0; +-} +- +-static inline void +-tapdisk_stream_poll_initialize(struct tapdisk_stream_poll *p) +-{ +- p->set = 0; +- p->pipe[POLL_READ] = p->pipe[POLL_WRITE] = -1; +-} +- +-static int +-tapdisk_stream_poll_open(struct tapdisk_stream_poll *p) +-{ +- int err; +- +- tapdisk_stream_poll_initialize(p); +- +- err = pipe(p->pipe); +- if (err) +- return -errno; +- +- err = fcntl(p->pipe[POLL_READ], F_SETFL, O_NONBLOCK); +- if (err) +- goto out; +- +- err = fcntl(p->pipe[POLL_WRITE], F_SETFL, O_NONBLOCK); +- if (err) +- goto out; +- +- return 0; +- +-out: +- close(p->pipe[POLL_READ]); +- close(p->pipe[POLL_WRITE]); +- tapdisk_stream_poll_initialize(p); +- return -errno; +-} +- +-static void +-tapdisk_stream_poll_close(struct tapdisk_stream_poll *p) +-{ +- if (p->pipe[POLL_READ] != -1) +- close(p->pipe[POLL_READ]); +- if (p->pipe[POLL_WRITE] != -1) +- close(p->pipe[POLL_WRITE]); +- tapdisk_stream_poll_initialize(p); +-} +- +-static inline void +-tapdisk_stream_poll_clear(struct tapdisk_stream_poll *p) +-{ +- int gcc, dummy; +- +- gcc = read(p->pipe[POLL_READ], &dummy, sizeof(dummy)); +- p->set = 0; +-} +- +-static inline void +-tapdisk_stream_poll_set(struct tapdisk_stream_poll *p) +-{ +- int dummy = 0; +- +- if (!p->set) { +- int gcc = write(p->pipe[POLL_WRITE], &dummy, sizeof(dummy)); +- p->set = 1; +- } +-} +- +-static inline int +-tapdisk_stream_stop(struct tapdisk_stream *s) +-{ +- return ((s->cur == s->end || s->err) && +- list_empty(&s->pending_list) && +- list_empty(&s->completed_list)); +-} +- +-static inline void +-tapdisk_stream_initialize_request(struct tapdisk_stream_request *req) +-{ +- memset(req, 0, sizeof(*req)); +- INIT_LIST_HEAD(&req->next); +-} +- +-static inline int +-tapdisk_stream_request_idx(struct tapdisk_stream *s, +- struct tapdisk_stream_request *req) +-{ +- return (req - s->requests); +-} +- +-static inline struct tapdisk_stream_request * +-tapdisk_stream_get_request(struct tapdisk_stream *s) +-{ +- struct tapdisk_stream_request *req; +- +- if (list_empty(&s->free_list)) +- return NULL; +- +- req = list_entry(s->free_list.next, +- struct tapdisk_stream_request, next); +- +- list_del_init(&req->next); +- tapdisk_stream_initialize_request(req); +- +- return req; +-} +- +-static inline void +-tapdisk_stream_queue_completed(struct tapdisk_stream *s, +- struct tapdisk_stream_request *sreq) +-{ +- struct tapdisk_stream_request *itr; +- +- list_for_each_entry(itr, &s->completed_list, next) +- if (sreq->seqno < itr->seqno) { +- list_add_tail(&sreq->next, &itr->next); +- return; +- } +- +- list_add_tail(&sreq->next, &s->completed_list); +-} +- +-static int +-tapdisk_result_compare(struct tapdisk_stream_request *sreq1, +- struct tapdisk_stream_request *sreq2) +-{ +- unsigned long idx1, idx2; +- char *buf1, *buf2; +- int result; +- +- assert(sreq1->seqno == sreq2->seqno); +- assert(sreq1->secs == sreq2->secs); +- idx1 = (unsigned long)tapdisk_stream_request_idx(&stream1, +- sreq1); +- idx2 = (unsigned long)tapdisk_stream_request_idx(&stream2, +- sreq2); +- buf1 = (char *)MMAP_VADDR(stream1.vbd->ring.vstart, idx1, 0); +- buf2 = (char *)MMAP_VADDR(stream2.vbd->ring.vstart, idx2, 0); +- +- result = memcmp(buf1, buf2, sreq1->secs << SECTOR_SHIFT); +- return result; +-} +- +-static int +-tapdisk_stream_process_data(void) +-{ +- struct tapdisk_stream_request *sreq1, *sreq2, *tmp1, *tmp2; +- int advance_both; +- int result = 0; +- +- sreq1 = list_entry(stream1.completed_list.next, +- struct tapdisk_stream_request, next); +- sreq2 = list_entry(stream2.completed_list.next, +- struct tapdisk_stream_request, next); +- tmp1 = list_entry(sreq1->next.next, +- struct tapdisk_stream_request, next); +- tmp2 = list_entry(sreq2->next.next, +- struct tapdisk_stream_request, next); +- while (result == 0 && +- &sreq1->next != &stream1.completed_list && +- &sreq2->next != &stream2.completed_list) { +- //printf("checking: %llu|%llu\n", sreq1->seqno, sreq2->seqno); +- advance_both = 1; +- if (sreq1->seqno < sreq2->seqno) { +- advance_both = 0; +- goto advance1; +- } +- if (sreq1->seqno > sreq2->seqno) +- goto advance2; +- +- result = tapdisk_result_compare(sreq1, sreq2); +- +- stream1.completed++; +- stream2.completed++; +- +- list_del_init(&sreq1->next); +- list_add_tail(&sreq1->next, &stream1.free_list); +- list_del_init(&sreq2->next); +- list_add_tail(&sreq2->next, &stream2.free_list); +- +-advance1: +- sreq1 = tmp1; +- tmp1 = list_entry(tmp1->next.next, +- struct tapdisk_stream_request, next); +- if (!advance_both) +- continue; +-advance2: +- sreq2 = tmp2; +- tmp2 = list_entry(tmp2->next.next, +- struct tapdisk_stream_request, next); +- } +- +- return result; +-} +- +-static void +-tapdisk_stream_dequeue(void *arg, blkif_response_t *rsp) +-{ +- struct tapdisk_stream *s = (struct tapdisk_stream *)arg; +- struct tapdisk_stream_request *sreq = s->requests + rsp->id; +- +- list_del_init(&sreq->next); +- +- if (rsp->status == BLKIF_RSP_OKAY) +- tapdisk_stream_queue_completed(s, sreq); +- else { +- s->err = EIO; +- list_add_tail(&sreq->next, &s->free_list); +- fprintf(stderr, "error reading sector %llu (stream %d)\n", +- sreq->sec, (s == &stream2) + 1); +- } +- +- if (tapdisk_stream_process_data()) { +- fprintf(stderr, "mismatch at sector %llu\n", +- sreq->sec); +- stream1.err = EINVAL; +- stream2.err = EINVAL; +- } +- +- tapdisk_stream_poll_set(&stream1.poll); +- tapdisk_stream_poll_set(&stream2.poll); +-} +- +-static inline int +-tapdisk_stream_enqueue_copy(struct tapdisk_stream *s, +- struct tapdisk_stream_request *r) +-{ +- td_vbd_t *vbd; +- blkif_request_t *breq; +- td_vbd_request_t *vreq; +- struct tapdisk_stream_request *sreq; +- int idx; +- +- vbd = stream2.vbd; +- sreq = tapdisk_stream_get_request(s); +- if (!sreq) +- return 1; +- +- idx = tapdisk_stream_request_idx(s, sreq); +- +- sreq->sec = r->sec; +- sreq->secs = r->secs; +- sreq->seqno = r->seqno; +- +- breq = &sreq->blkif_req; +- breq->id = idx; +- breq->nr_segments = r->blkif_req.nr_segments; +- breq->sector_number = r->blkif_req.sector_number; +- breq->operation = BLKIF_OP_READ; +- +- for (int i = 0; i < r->blkif_req.nr_segments; i++) { +- struct blkif_request_segment *seg = breq->seg + i; +- seg->first_sect = r->blkif_req.seg[i].first_sect; +- seg->last_sect = r->blkif_req.seg[i].last_sect; +- } +- s->cur += sreq->secs; +- +- vreq = vbd->request_list + idx; +- assert(list_empty(&vreq->next)); +- assert(vreq->secs_pending == 0); +- +- memcpy(&vreq->req, breq, sizeof(*breq)); +- s->started++; +- vbd->received++; +- vreq->vbd = vbd; +- +- tapdisk_vbd_move_request(vreq, &vbd->new_requests); +- list_add_tail(&sreq->next, &s->pending_list); +- +- return 0; +-} +- +-static void +-tapdisk_stream_enqueue1(void) +-{ +- td_vbd_t *vbd; +- int i, idx, psize, blk; +- struct tapdisk_stream *s = &stream1; +- +- vbd = s->vbd; +- psize = getpagesize(); +- +- while (s->cur < s->end && !s->err) { +- blkif_request_t *breq; +- td_vbd_request_t *vreq; +- struct tapdisk_stream_request *sreq; +- +- /* skip any blocks that are not present in this image */ +- blk = s->cur >> SPB_SHIFT; +- while (s->cur < s->end && vhd1.bat.bat[blk] == DD_BLK_UNUSED) { +- //printf("skipping block %d\n", blk); +- blk++; +- s->cur = blk << SPB_SHIFT; +- } +- +- if (s->cur >= s->end) +- break; +- +- sreq = tapdisk_stream_get_request(s); +- if (!sreq) +- break; +- +- idx = tapdisk_stream_request_idx(s, sreq); +- +- sreq->sec = s->cur; +- sreq->secs = 0; +- sreq->seqno = s->started++; +- +- breq = &sreq->blkif_req; +- breq->id = idx; +- breq->nr_segments = 0; +- breq->sector_number = sreq->sec; +- breq->operation = BLKIF_OP_READ; +- +- for (i = 0; i < MAX_SEGMENTS; i++) { +- uint32_t secs; +- struct blkif_request_segment *seg = breq->seg + i; +- +- secs = MIN(s->end - s->cur, psize >> SECTOR_SHIFT); +- secs = MIN(((blk + 1) << SPB_SHIFT) - s->cur, secs); +- if (!secs) +- break; +- +- sreq->secs += secs; +- s->cur += secs; +- +- seg->first_sect = 0; +- seg->last_sect = secs - 1; +- breq->nr_segments++; +- } +- +- vreq = vbd->request_list + idx; +- +- assert(list_empty(&vreq->next)); +- assert(vreq->secs_pending == 0); +- +- memcpy(&vreq->req, breq, sizeof(*breq)); +- vbd->received++; +- vreq->vbd = vbd; +- +- tapdisk_vbd_move_request(vreq, &vbd->new_requests); +- list_add_tail(&sreq->next, &s->pending_list); +- } +- +- tapdisk_vbd_issue_requests(vbd); +-} +- +-static void +-tapdisk_stream_enqueue2(void) +-{ +- td_vbd_t *vbd; +- int i, blk; +- struct tapdisk_stream_request *itr; +- struct tapdisk_stream *s = &stream2; +- +- vbd = s->vbd; +- +- /* issue the same requests that we issued on stream1 */ +- list_for_each_entry(itr, &stream1.completed_list, next) { +- if (itr->sec < s->cur) +- continue; +- if (tapdisk_stream_enqueue_copy(s, itr)) +- goto done; +- } +- +- list_for_each_entry(itr, &stream1.pending_list, next) { +- if (itr->sec < s->cur) +- continue; +- if (tapdisk_stream_enqueue_copy(s, itr)) +- goto done; +- } +- +- stream2.cur = stream1.cur; +- +-done: +- tapdisk_vbd_issue_requests(vbd); +-} +- +-static inline int +-tapdisk_diff_done(void) +-{ +- return (tapdisk_stream_stop(&stream1) && tapdisk_stream_stop(&stream2)); +-} +- +-static void +-tapdisk_diff_stop(void) +-{ +- tapdisk_stream_close_image(&stream1); +- tapdisk_stream_close_image(&stream2); +-} +- +-static void +-tapdisk_stream_enqueue(event_id_t id, char mode, void *arg) +-{ +- struct tapdisk_stream *s = (struct tapdisk_stream *)arg; +- +- tapdisk_stream_poll_clear(&s->poll); +- +- if (tapdisk_diff_done()) { +- tapdisk_diff_stop(); +- return; +- } +- +- if (s == &stream1) +- tapdisk_stream_enqueue1(); +- else if (s == &stream2) +- tapdisk_stream_enqueue2(); +- else +- assert(0); +- +- if (tapdisk_diff_done()) { +- // we have to check again for the case when stream1 had no +- // blocks at all +- tapdisk_diff_stop(); +- return; +- } +-} +- +-static int +-tapdisk_stream_open_image(struct tapdisk_stream *s, const char *name) +-{ +- int err; +- td_disk_info_t info; +- +- s->id = tapdisk_stream_count++; +- +- err = tapdisk_vbd_initialize(-1, -1, s->id); +- if (err) +- goto out; +- +- s->vbd = tapdisk_server_get_vbd(s->id); +- if (!s->vbd) { +- err = ENODEV; +- goto out; +- } +- +- tapdisk_vbd_set_callback(s->vbd, tapdisk_stream_dequeue, s); +- +- err = tapdisk_vbd_open_vdi(s->vbd, name, TD_OPEN_RDONLY, -1); +- if (err) +- goto out; +- +- err = tapdisk_vbd_get_disk_info(s->vbd, &info); +- if (err) { +- fprintf(stderr, "failed getting image size: %d\n", err); +- return err; +- } +- +- s->start = 0; +- s->cur = s->start; +- s->end = info.size; +- +- err = 0; +- +-out: +- if (err) +- fprintf(stderr, "failed to open image %s: %d\n", name, err); +- return err; +-} +- +-static void +-tapdisk_stream_close_image(struct tapdisk_stream *s) +-{ +- td_vbd_t *vbd; +- +- vbd = tapdisk_server_get_vbd(s->id); +- if (vbd) { +- tapdisk_vbd_close_vdi(vbd); +- tapdisk_server_remove_vbd(vbd); +- free((void *)vbd->ring.vstart); +- tapdisk_vbd_free(vbd); +- s->vbd = NULL; +- } +-} +- +-static int +-tapdisk_stream_initialize_requests(struct tapdisk_stream *s) +-{ +- size_t size; +- td_ring_t *ring; +- int err, i, psize; +- +- ring = &s->vbd->ring; +- psize = getpagesize(); +- size = psize * BLKTAP_MMAP_REGION_SIZE; +- +- /* sneaky -- set up ring->vstart so tapdisk_vbd will use our buffers */ +- err = posix_memalign((void **)&ring->vstart, psize, size); +- if (err) { +- fprintf(stderr, "failed to allocate buffers: %d\n", err); +- ring->vstart = 0; +- return err; +- } +- +- for (i = 0; i < MAX_STREAM_REQUESTS; i++) { +- struct tapdisk_stream_request *req = s->requests + i; +- tapdisk_stream_initialize_request(req); +- list_add_tail(&req->next, &s->free_list); +- } +- +- return 0; +-} +- +-static int +-tapdisk_stream_register_enqueue_event(struct tapdisk_stream *s) +-{ +- int err; +- struct tapdisk_stream_poll *p = &s->poll; +- +- err = tapdisk_stream_poll_open(p); +- if (err) +- goto out; +- +- err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD, +- p->pipe[POLL_READ], TV_ZERO, +- tapdisk_stream_enqueue, s); +- if (err < 0) +- goto out; +- +- s->enqueue_event_id = err; +- err = 0; +- +-out: +- if (err) +- fprintf(stderr, "failed to register event: %d\n", err); +- return err; +-} +- +-static void +-tapdisk_stream_unregister_enqueue_event(struct tapdisk_stream *s) +-{ +- if (s->enqueue_event_id) { +- tapdisk_server_unregister_event(s->enqueue_event_id); +- s->enqueue_event_id = 0; +- } +- tapdisk_stream_poll_close(&s->poll); +-} +- +-static inline void +-tapdisk_stream_initialize(struct tapdisk_stream *s) +-{ +- memset(s, 0, sizeof(*s)); +- INIT_LIST_HEAD(&s->free_list); +- INIT_LIST_HEAD(&s->pending_list); +- INIT_LIST_HEAD(&s->completed_list); +-} +- +-static int +-tapdisk_stream_open(struct tapdisk_stream *s, const char *arg) +-{ +- int err; +- +- tapdisk_stream_initialize(s); +- +- err = tapdisk_stream_open_image(s, arg); +- if (err) +- return err; +- +- err = tapdisk_stream_initialize_requests(s); +- if (err) +- return err; +- +- err = tapdisk_stream_register_enqueue_event(s); +- if (err) +- return err; +- +- tapdisk_stream_enqueue(s->enqueue_event_id, +- SCHEDULER_POLL_READ_FD, s); +- +- return 0; +-} +- +-static void +-tapdisk_stream_release(struct tapdisk_stream *s) +-{ +- tapdisk_stream_close_image(s); +- tapdisk_stream_unregister_enqueue_event(s); +-} +- +-static int +-tapdisk_stream_run(struct tapdisk_stream *s) +-{ +- tapdisk_stream_enqueue(s->enqueue_event_id, SCHEDULER_POLL_READ_FD, s); +- tapdisk_server_run(); +- return s->err; +-} +- +-int +-main(int argc, char *argv[]) +-{ +- int c, err, type1; +- const char *arg1 = NULL, *arg2 = NULL; +- const disk_info_t *info; +- const char *path1; +- +- err = 0; +- +- program = basename(argv[0]); +- +- while ((c = getopt(argc, argv, "n:m:h")) != -1) { +- switch (c) { +- case 'n': +- arg1 = optarg; +- break; +- case 'm': +- arg2 = optarg; +- break; +- case 'h': +- usage(stdout); +- return 0; +- default: +- goto fail_usage; +- } +- } +- +- if (!arg1 || !arg2) +- goto fail_usage; +- +- type1 = tapdisk_disktype_parse_params(arg1, &path1); +- if (type1 < 0) +- return type1; +- +- if (type1 != DISK_TYPE_VHD) { +- printf("error: first VDI is not VHD\n"); +- return EINVAL; +- } +- +- err = open_vhd(path1, &vhd1); +- if (err) +- return err; +- +- tapdisk_start_logging("tapdisk-diff", "daemon"); +- +- err = tapdisk_server_initialize(NULL, NULL); +- if (err) +- goto out; +- +- err = tapdisk_stream_open(&stream1, arg1); +- if (err) { +- fprintf(stderr, "Failed to open %s: %s\n", +- arg1, strerror(-err)); +- goto out; +- } +- +- err = tapdisk_stream_open(&stream2, arg2); +- if (err) { +- fprintf(stderr, "Failed to open %s: %s\n", +- arg2, strerror(-err)); +- goto out1; +- } +- +- if (stream1.end != stream2.end) { +- fprintf(stderr, "Image sizes differ: %"PRIu64" != %"PRIu64"\n", +- stream1.end, stream2.end); +- err = EINVAL; +- goto out2; +- } +- +- tapdisk_server_run(); +- +-out2: +- tapdisk_stream_release(&stream2); +-out1: +- tapdisk_stream_release(&stream1); +-out: +- vhd_close(&vhd1); +- tapdisk_stop_logging(); +- +- return err ? : stream1.err; +- +-fail_usage: +- usage(stderr); +- return 1; +-} +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index f877b51a..23ca7289 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -135,8 +135,6 @@ struct td_vbd_handle { + struct list_head failed_requests; + struct list_head completed_requests; + +- td_vbd_request_t request_list[MAX_REQUESTS]; /* XXX */ +- + struct list_head next; + + uint16_t req_timeout; /* in seconds */ diff --git a/SOURCES/0011-blkif-add-a-comment-on-memory-barrier-usage.patch b/SOURCES/0011-blkif-add-a-comment-on-memory-barrier-usage.patch new file mode 100644 index 0000000..3d69a77 --- /dev/null +++ b/SOURCES/0011-blkif-add-a-comment-on-memory-barrier-usage.patch @@ -0,0 +1,28 @@ +From badd991784d27dc6cb07b0cc034bf31a0f07de93 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 27 Feb 2025 17:41:36 +0100 +Subject: [PATCH] blkif: add a comment on memory barrier usage + +The memory barrier is mandatory to see the queued requests from blkif +frontend. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-ctx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/td-ctx.c b/drivers/td-ctx.c +index a1da30f6..8b75dc48 100644 +--- a/drivers/td-ctx.c ++++ b/drivers/td-ctx.c +@@ -220,7 +220,7 @@ __xenio_blkif_get_requests(struct td_xenblkif * const blkif, + ring = &blkif->rings.common; + + rp = ring->sring->req_prod; +- xen_rmb(); /* TODO why? */ ++ xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ + + for (rc = ring->req_cons, n = 0, barrier = false; + rc != rp && n < count && !barrier; diff --git a/SOURCES/0012-tapback-Synchronise-usage-with-code.patch b/SOURCES/0012-tapback-Synchronise-usage-with-code.patch new file mode 100644 index 0000000..c5b3424 --- /dev/null +++ b/SOURCES/0012-tapback-Synchronise-usage-with-code.patch @@ -0,0 +1,42 @@ +From a088cdc3040c5c627c6a089384aa007c8865d59b Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Sat, 15 Mar 2025 12:25:11 +0100 +Subject: [PATCH] tapback: Synchronise usage with code + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + tapback/tapback.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/tapback/tapback.c b/tapback/tapback.c +index fe04d177..71dc72f4 100644 +--- a/tapback/tapback.c ++++ b/tapback/tapback.c +@@ -510,10 +510,12 @@ usage(FILE * const stream, const char * const prog) + fprintf(stream, + "usage: %s\n" + "\t[-d|--debug]\n" +- "\t[-h|--help]\n" ++ "\t[-h|--help]\n" + "\t[-v|--verbose]\n" +- "\t[-b]--nobarrier]\n" +- "\t[-n|--name]\n", prog); ++ "\t[-p|--pidfile PIDfile]\n" ++ "\t[-x|--domain domainID]\n" ++ "\t[-b|--nobarrier]\n" ++ "\t[-n|--name backend_name (default: vbd3)]\n", prog); + } + + extern char *optarg; +@@ -612,8 +614,7 @@ int main(int argc, char **argv) + {"name", 0, NULL, 'n'}, + {"pidfile", 0, NULL, 'p'}, + {"domain", 0, NULL, 'x'}, +- {"nobarrier", 0, NULL, 'b'}, +- ++ {"nobarrier", 0, NULL, 'b'}, + }; + int c; + diff --git a/SOURCES/0013-tap-ctl-fix-comments-of-tap_ctl_info-function.patch b/SOURCES/0013-tap-ctl-fix-comments-of-tap_ctl_info-function.patch new file mode 100644 index 0000000..c57fd66 --- /dev/null +++ b/SOURCES/0013-tap-ctl-fix-comments-of-tap_ctl_info-function.patch @@ -0,0 +1,26 @@ +From a74e38ffa8b049bd5a2bf2ef121737664eda3366 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Sat, 15 Mar 2025 12:26:27 +0100 +Subject: [PATCH] tap-ctl: fix comments of tap_ctl_info function + +No functional change. + +Signed-off-by: Anthoine Bourgeois +--- + include/tap-ctl.h | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/include/tap-ctl.h b/include/tap-ctl.h +index adf75695..f2db49fe 100644 +--- a/include/tap-ctl.h ++++ b/include/tap-ctl.h +@@ -186,7 +186,8 @@ int tap_ctl_disconnect_xenblkif(const pid_t pid, const domid_t domid, + * @param pid the process ID of the tapdisk process + * @param sectors output parameter that receives the number of sectors + * @param sector_size output parameter that receives the size of the sector +- * @param info TODO ? ++ * @param info output parameter that receives the vdisk info flags VDISK_???, ++ * defined in include/xen/interface/io/blkif.h + * @param minor + * + */ diff --git a/SOURCES/0014-tapdisk-fix-hardcoded-array-size-with-a-macro.patch b/SOURCES/0014-tapdisk-fix-hardcoded-array-size-with-a-macro.patch new file mode 100644 index 0000000..136fc71 --- /dev/null +++ b/SOURCES/0014-tapdisk-fix-hardcoded-array-size-with-a-macro.patch @@ -0,0 +1,59 @@ +From ea5a1543e987283abac46a62222243b179c9da19 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Mon, 17 Mar 2025 14:48:06 +0100 +Subject: [PATCH] tapdisk: fix hardcoded array size with a macro + +The macro MAX_RING_PAGES is already defined. The comments are +fixed accordingly. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-blkif.h | 6 +++--- + include/tapdisk-message.h | 6 ++++-- + 2 files changed, 7 insertions(+), 5 deletions(-) + +diff --git a/drivers/td-blkif.h b/drivers/td-blkif.h +index b50fa8a5..d96888bd 100644 +--- a/drivers/td-blkif.h ++++ b/drivers/td-blkif.h +@@ -99,10 +99,10 @@ struct td_xenblkif { + blkif_back_rings_t rings; + + /** +- * TODO Why 8 specifically? +- * TODO Do we really need to keep it around? ++ * Grant references of the ring that holds the request descriptors. ++ * See ring_n_pages below to know the number of used refs in this array. + */ +- grant_ref_t ring_ref[8]; ++ grant_ref_t ring_ref[MAX_RING_PAGES]; + + /** + * Number of pages in the ring that holds the request descriptors. +diff --git a/include/tapdisk-message.h b/include/tapdisk-message.h +index 0f73e0c9..fe1b3066 100644 +--- a/include/tapdisk-message.h ++++ b/include/tapdisk-message.h +@@ -34,6 +34,8 @@ + #include + #include + ++#include "xen_blkif.h" ++ + /* + * TODO This is quite small since we don't allow path bigger than 256 chars. If + * we ever increase this, make sure tapdisk_message_t structures are not +@@ -128,9 +130,9 @@ typedef struct tapdisk_message_blkif { + + /** + * Grant references for the shared ring. +- * TODO Why 8 specifically? ++ * See order below to know the number of used refs in this array. + */ +- uint32_t gref[8]; ++ uint32_t gref[MAX_RING_PAGES]; + + /** + * Number of pages in the ring, expressed as a page order. diff --git a/SOURCES/0015-tapdisk-Replace-structure-name-in-sizeof.patch b/SOURCES/0015-tapdisk-Replace-structure-name-in-sizeof.patch new file mode 100644 index 0000000..cd87172 --- /dev/null +++ b/SOURCES/0015-tapdisk-Replace-structure-name-in-sizeof.patch @@ -0,0 +1,28 @@ +From c889ec008cc58053db48262c8a9ee8730a5cd14a Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Tue, 18 Mar 2025 19:28:22 +0100 +Subject: [PATCH] tapdisk: Replace structure name in sizeof + +xenio_blkif_req structure doesn't exist anymore, blkif_request_t is the +right structure here. Hopefully a pointer is a pointer in any case. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index c35b02c6..7b421d87 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -943,7 +943,7 @@ tapdisk_xenblkif_reqs_init(struct td_xenblkif *td_blkif) + } + + td_blkif->reqs_free = +- malloc(td_blkif->ring_size * sizeof(struct xenio_blkif_req *)); ++ malloc(td_blkif->ring_size * sizeof(struct blkif_request_t *)); + if (!td_blkif->reqs_free) { + err = -errno; + goto fail; diff --git a/SOURCES/0016-tapdisk-check-if-RD-macros-are-defined-in-ring.h-sin.patch b/SOURCES/0016-tapdisk-check-if-RD-macros-are-defined-in-ring.h-sin.patch new file mode 100644 index 0000000..ba41697 --- /dev/null +++ b/SOURCES/0016-tapdisk-check-if-RD-macros-are-defined-in-ring.h-sin.patch @@ -0,0 +1,34 @@ +From 636c4a76d8032b29e7f6b9299e602fd61bdd5811 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Fri, 14 Mar 2025 15:10:25 +0100 +Subject: [PATCH] tapdisk: check if RD macros are defined in ring.h (since + xen-4.18) + +If the RD* macros are defined, use the upstream definitions introduced +in xen-4.18. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-blktap.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/tapdisk-blktap.c b/drivers/tapdisk-blktap.c +index d706724c..cf72b095 100644 +--- a/drivers/tapdisk-blktap.c ++++ b/drivers/tapdisk-blktap.c +@@ -61,11 +61,13 @@ + #define WARN(_f, _a...) tlog_syslog(TLOG_WARN, "WARNING: "_f "in %s:%d", \ + ##_a, __func__, __LINE__) + ++#ifndef __RD2 + #define __RD2(_x) (((_x) & 0x00000002) ? 0x2 : ((_x) & 0x1)) + #define __RD4(_x) (((_x) & 0x0000000c) ? __RD2((_x)>>2)<<2 : __RD2(_x)) + #define __RD8(_x) (((_x) & 0x000000f0) ? __RD4((_x)>>4)<<4 : __RD4(_x)) + #define __RD16(_x) (((_x) & 0x0000ff00) ? __RD8((_x)>>8)<<8 : __RD8(_x)) + #define __RD32(_x) (((_x) & 0xffff0000) ? __RD16((_x)>>16)<<16 : __RD16(_x)) ++#endif + + #define BLKTAP_RD32(_n) __RD32(_n) + #define BLKTAP_RING_SIZE __BLKTAP_RING_SIZE(PAGE_SIZE) diff --git a/SOURCES/0017-tapdisk-Fix-a-typo-in-util.h-header.patch b/SOURCES/0017-tapdisk-Fix-a-typo-in-util.h-header.patch new file mode 100644 index 0000000..b20b9ef --- /dev/null +++ b/SOURCES/0017-tapdisk-Fix-a-typo-in-util.h-header.patch @@ -0,0 +1,23 @@ +From 04283cb976ac46341687704b790cf571cb2d1680 Mon Sep 17 00:00:00 2001 +From: Emmanuel Varagnat +Date: Thu, 8 Jan 2026 19:06:03 +0100 +Subject: [PATCH] tapdisk: Fix a typo in util.h header + +Signed-off-by: Emmanuel Varagnat +--- + include/util.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/util.h b/include/util.h +index e2529a51..06cce42d 100644 +--- a/include/util.h ++++ b/include/util.h +@@ -29,7 +29,7 @@ + */ + + #ifndef __TAPDISK_UTIL_H__ +-#define __TAPDISK_UTIL_H_ ++#define __TAPDISK_UTIL_H__ + + #include + #include diff --git a/SOURCES/0018-tapdisk-rename-field-pool-to-pool_name.patch b/SOURCES/0018-tapdisk-rename-field-pool-to-pool_name.patch new file mode 100644 index 0000000..5e89066 --- /dev/null +++ b/SOURCES/0018-tapdisk-rename-field-pool-to-pool_name.patch @@ -0,0 +1,212 @@ +From 2e4ee8039cc272d2abb0dbe589a98e6ed271a373 Mon Sep 17 00:00:00 2001 +From: Emmanuel Varagnat +Date: Mon, 5 Jan 2026 16:17:57 +0100 +Subject: [PATCH] tapdisk: rename field pool to pool_name + +Signed-off-by: Emmanuel Varagnat +--- + control/tap-ctl-xen.c | 12 ++++++------ + drivers/tapdisk-control.c | 14 +++++++------- + drivers/td-ctx.c | 22 +++++++++++----------- + drivers/td-ctx.h | 4 ++-- + drivers/td-stats.c | 2 +- + include/tapdisk-message.h | 4 ++-- + 6 files changed, 29 insertions(+), 29 deletions(-) + +diff --git a/control/tap-ctl-xen.c b/control/tap-ctl-xen.c +index f09b76ea..14c60ec4 100644 +--- a/control/tap-ctl-xen.c ++++ b/control/tap-ctl-xen.c +@@ -53,7 +53,7 @@ int + tap_ctl_connect_xenblkif(const pid_t pid, const domid_t domid, const int devid, int poll_duration, + int poll_idle_threshold, + const grant_ref_t * grefs, const int order, const evtchn_port_t port, +- int proto, const char *pool, const int minor) ++ int proto, const char *pool_name, const int minor) + { + tapdisk_message_t message; + int i, err; +@@ -71,14 +71,14 @@ tap_ctl_connect_xenblkif(const pid_t pid, const domid_t domid, const int devid, + message.u.blkif.proto = proto; + message.u.blkif.poll_duration = poll_duration; + message.u.blkif.poll_idle_threshold = poll_idle_threshold; +- if (pool) { +- if (unlikely(strlen(pool) > (sizeof(message.u.blkif.pool) - 1))) { +- EPRINTF("pool name too long: %s\n", pool); ++ if (pool_name) { ++ if (unlikely(strlen(pool_name) > (sizeof(message.u.blkif.pool_name) - 1))) { ++ EPRINTF("pool name too long: %s\n", pool_name); + return -ENAMETOOLONG; + } +- safe_strncpy(message.u.blkif.pool, pool, sizeof(message.u.blkif.pool)); ++ safe_strncpy(message.u.blkif.pool_name, pool_name, sizeof(message.u.blkif.pool_name)); + } else { +- message.u.blkif.pool[0] = 0; ++ message.u.blkif.pool_name[0] = '\0'; + } + + err = tap_ctl_connect_send_and_receive(pid, &message, NULL); +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index 91ffe097..39e7ab0e 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -1201,7 +1201,7 @@ tapdisk_control_xenblkif_connect( + tapdisk_message_blkif_t *blkif; + + td_vbd_t *vbd = NULL; +- const char *pool; ++ const char *pool_name; + size_t len; + int err; + int minor = -1; +@@ -1219,20 +1219,20 @@ tapdisk_control_xenblkif_connect( + } + + blkif = &request->u.blkif; +- len = strnlen(blkif->pool, sizeof(blkif->pool)); ++ len = strnlen(blkif->pool_name, sizeof(blkif->pool_name)); + if (!len) +- pool = NULL; +- else if (len >= sizeof(blkif->pool)) { ++ pool_name = NULL; ++ else if (len >= sizeof(blkif->pool_name)) { + err = -EINVAL; + goto out; + } else +- pool = blkif->pool; ++ pool_name = blkif->pool_name; + + DPRINTF("connecting VBD %d domid=%d, devid=%d, pool %s, evt %d, poll duration %d, poll idle threshold %d\n", +- vbd->uuid, blkif->domid, blkif->devid, pool, blkif->port, blkif->poll_duration, blkif->poll_idle_threshold); ++ vbd->uuid, blkif->domid, blkif->devid, pool_name, blkif->port, blkif->poll_duration, blkif->poll_idle_threshold); + + err = tapdisk_xenblkif_connect(blkif->domid, blkif->devid, blkif->gref, +- blkif->order, blkif->port, blkif->proto, blkif->poll_duration, blkif->poll_idle_threshold, pool, vbd); ++ blkif->order, blkif->port, blkif->proto, blkif->poll_duration, blkif->poll_idle_threshold, pool_name, vbd); + + out: + response->cookie = request->cookie; +diff --git a/drivers/td-ctx.c b/drivers/td-ctx.c +index 8b75dc48..e344a62f 100644 +--- a/drivers/td-ctx.c ++++ b/drivers/td-ctx.c +@@ -407,13 +407,13 @@ tapdisk_xenio_ctx_ring_event(event_id_t id __attribute__((unused)), + * TODO The pool is ignored, we always open the default pool. + */ + static inline int +-tapdisk_xenio_ctx_open(const char *pool) ++tapdisk_xenio_ctx_open(const char *pool_name) + { + struct td_xenio_ctx *ctx; + int fd, err; + + /* zero-length pool names are not allowed */ +- if (pool && !strlen(pool)) ++ if (pool_name && !strlen(pool_name)) + return -EINVAL; + + ctx = calloc(1, sizeof(*ctx)); +@@ -425,8 +425,8 @@ tapdisk_xenio_ctx_open(const char *pool) + + ctx->ring_event = -1; /* TODO is there a special value? */ + ctx->gntdev_fd = -1; +- ctx->pool = TD_XENBLKIF_DEFAULT_POOL; +- INIT_LIST_HEAD(&ctx->blkifs); ++ ctx->pool_name = TD_XENBLKIF_DEFAULT_POOL; ++ INIT_LIST_HEAD(&ctx->blkifs); + list_add(&ctx->entry, &_td_xenio_ctxs); + + ctx->gntdev_fd = open("/dev/xen/gntdev", O_NONBLOCK); +@@ -483,14 +483,14 @@ fail: + * against the default pool. Note that NULL is valid pool name value. + */ + static inline int +-__td_xenio_ctx_match(struct td_xenio_ctx * ctx, const char *pool) ++__td_xenio_ctx_match(struct td_xenio_ctx * ctx, const char *pool_name) + { +- if (unlikely(!pool)) { ++ if (unlikely(!pool_name)) { + assert(TD_XENBLKIF_DEFAULT_POOL); +- return !strcmp(ctx->pool, TD_XENBLKIF_DEFAULT_POOL); ++ return !strcmp(ctx->pool_name, TD_XENBLKIF_DEFAULT_POOL); + } + +- return !strcmp(ctx->pool, pool); ++ return !strcmp(ctx->pool_name, pool_name); + } + + #define tapdisk_xenio_find_ctx(_ctx, _cond) \ +@@ -507,19 +507,19 @@ __td_xenio_ctx_match(struct td_xenio_ctx * ctx, const char *pool) + } while (0) + + int +-tapdisk_xenio_ctx_get(const char *pool, struct td_xenio_ctx ** _ctx) ++tapdisk_xenio_ctx_get(const char *pool_name, struct td_xenio_ctx ** _ctx) + { + struct td_xenio_ctx *ctx; + int err = 0; + + do { +- tapdisk_xenio_find_ctx(ctx, __td_xenio_ctx_match(ctx, pool)); ++ tapdisk_xenio_find_ctx(ctx, __td_xenio_ctx_match(ctx, pool_name)); + if (ctx) { + *_ctx = ctx; + return 0; + } + +- err = tapdisk_xenio_ctx_open(pool); ++ err = tapdisk_xenio_ctx_open(pool_name); + } while (!err); + + return err; +diff --git a/drivers/td-ctx.h b/drivers/td-ctx.h +index c03912e9..b58edbbe 100644 +--- a/drivers/td-ctx.h ++++ b/drivers/td-ctx.h +@@ -50,7 +50,7 @@ + * unnecessary. + */ + struct td_xenio_ctx { +- char *pool; /* TODO rename to pool_name */ ++ char *pool_name; + + /** + * Handle to the grant table driver. +@@ -88,7 +88,7 @@ struct td_xenio_ctx { + * @returns 0 on success, -errno on error + */ + int +-tapdisk_xenio_ctx_get(const char *pool, struct td_xenio_ctx ** _ctx); ++tapdisk_xenio_ctx_get(const char *pool_name, struct td_xenio_ctx ** _ctx); + + /** + * Releases the pool, only if there is no block interface using it. +diff --git a/drivers/td-stats.c b/drivers/td-stats.c +index e75752d8..f8982624 100644 +--- a/drivers/td-stats.c ++++ b/drivers/td-stats.c +@@ -42,7 +42,7 @@ tapdisk_xenblkif_stats(struct td_xenblkif * blkif, td_stats_t * st) + ASSERT(st); + ASSERT(blkif->ctx); + +- tapdisk_stats_field(st, "pool", "s", blkif->ctx->pool); ++ tapdisk_stats_field(st, "pool", "s", blkif->ctx->pool_name); + tapdisk_stats_field(st, "domid", "d", blkif->domid); + tapdisk_stats_field(st, "devid", "d", blkif->devid); + +diff --git a/include/tapdisk-message.h b/include/tapdisk-message.h +index fe1b3066..7c54fb3b 100644 +--- a/include/tapdisk-message.h ++++ b/include/tapdisk-message.h +@@ -146,9 +146,9 @@ typedef struct tapdisk_message_blkif { + uint32_t proto; + + /** +- * TODO Page pool? Can be NULL. ++ * Page pool name? Can be empty (ie "") + */ +- char pool[TAPDISK_MESSAGE_STRING_LENGTH]; ++ char pool_name[TAPDISK_MESSAGE_STRING_LENGTH]; + + /** + * The event channel port. diff --git a/SOURCES/0019-td-req-remove-unused-field-gref.patch b/SOURCES/0019-td-req-remove-unused-field-gref.patch new file mode 100644 index 0000000..636aed3 --- /dev/null +++ b/SOURCES/0019-td-req-remove-unused-field-gref.patch @@ -0,0 +1,37 @@ +From d49e83a893bee7b183a697bdd4ef750c93d2ca5b Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 27 Nov 2025 11:16:51 +0100 +Subject: [PATCH] td-req: remove unused field gref + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 1 - + drivers/td-req.h | 1 - + 2 files changed, 2 deletions(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index 7b421d87..52b014ba 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -645,7 +645,6 @@ tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, + + for (i = 0; i < req->msg.nr_segments; i++) { + struct blkif_request_segment *seg = &req->msg.seg[i]; +- req->gref[i] = seg->gref; + + /* + * Note that first and last may be equal, which means only one sector +diff --git a/drivers/td-req.h b/drivers/td-req.h +index dad40f29..25eeb503 100644 +--- a/drivers/td-req.h ++++ b/drivers/td-req.h +@@ -82,7 +82,6 @@ struct td_xenblkif_req { + */ + struct td_iovec iov[BLKIF_MAX_BUFFER_SEGMENTS_PER_REQUEST]; + +- grant_ref_t gref[BLKIF_MAX_BUFFER_SEGMENTS_PER_REQUEST]; + int prot; + + struct gntdev_grant_copy_segment diff --git a/SOURCES/0020-td-req-rename-tapreq-as-req.patch b/SOURCES/0020-td-req-rename-tapreq-as-req.patch new file mode 100644 index 0000000..70689b9 --- /dev/null +++ b/SOURCES/0020-td-req-rename-tapreq-as-req.patch @@ -0,0 +1,433 @@ +From 2e709e20086ab1b77d57a28c436b237d42dc7abd Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Fri, 28 Nov 2025 12:45:43 +0100 +Subject: [PATCH] td-req: rename tapreq as req + +Remove the associated TODO statements. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 150 +++++++++++++++++++++++------------------------ + 1 file changed, 75 insertions(+), 75 deletions(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index 52b014ba..b271d3bf 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -184,28 +184,28 @@ td_xenblkif_bufcache_put(struct td_xenblkif * const blkif, void *buf) + * Puts the request back to the free list of this block interface. + * + * @param blkif the block interface +- * @param tapreq the request to give back ++ * @param req the request to give back + */ + static void + tapdisk_xenblkif_free_request(struct td_xenblkif * const blkif, +- struct td_xenblkif_req * const tapreq) ++ struct td_xenblkif_req * const req) + { + int put_bufcache; + + ASSERT(blkif); +- ASSERT(tapreq); ++ ASSERT(req); + ASSERT(blkif->n_reqs_free < blkif->ring_size); + +- put_bufcache = tapreq->msg.nr_segments != 0; ++ put_bufcache = req->msg.nr_segments != 0; + + #ifdef DEBUG +- memset(&tapreq->msg, BLKIF_MSG_POISON, sizeof(tapreq->msg)); ++ memset(&req->msg, BLKIF_MSG_POISON, sizeof(req->msg)); + #endif + +- blkif->reqs_free[blkif->ring_size - (++blkif->n_reqs_free)] = &tapreq->msg; ++ blkif->reqs_free[blkif->ring_size - (++blkif->n_reqs_free)] = &req->msg; + + if (likely(put_bufcache)) +- td_xenblkif_bufcache_put(blkif, tapreq->vma); ++ td_xenblkif_bufcache_put(blkif, req->vma); + } + + /** +@@ -359,7 +359,7 @@ blkif_rq_data(blkif_request_t const * const msg) + + static int + guest_copy2(struct td_xenblkif * const blkif, +- struct td_xenblkif_req * const tapreq /* TODO rename to req */) { ++ struct td_xenblkif_req * const req) { + + int i = 0; + long err = 0; +@@ -367,18 +367,18 @@ guest_copy2(struct td_xenblkif * const blkif, + + ASSERT(blkif); + ASSERT(blkif->ctx); +- ASSERT(tapreq); +- ASSERT(blkif_rq_data(&tapreq->msg)); +- ASSERT(tapreq->msg.nr_segments > 0); +- ASSERT(tapreq->msg.nr_segments <= ARRAY_SIZE(tapreq->gcopy_segs)); +- +- for (i = 0; i < tapreq->msg.nr_segments; i++) { +- struct blkif_request_segment *blkif_seg = &tapreq->msg.seg[i]; +- struct gntdev_grant_copy_segment *gcopy_seg = &tapreq->gcopy_segs[i]; ++ ASSERT(req); ++ ASSERT(blkif_rq_data(&req->msg)); ++ ASSERT(req->msg.nr_segments > 0); ++ ASSERT(req->msg.nr_segments <= ARRAY_SIZE(req->gcopy_segs)); ++ ++ for (i = 0; i < req->msg.nr_segments; i++) { ++ struct blkif_request_segment *blkif_seg = &req->msg.seg[i]; ++ struct gntdev_grant_copy_segment *gcopy_seg = &req->gcopy_segs[i]; + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) +- if (blkif_rq_wr(&tapreq->msg)) { ++ if (blkif_rq_wr(&req->msg)) { + /* copy from guest */ +- gcopy_seg->dest.virt = tapreq->vma + (i << PAGE_SHIFT) ++ gcopy_seg->dest.virt = req->vma + (i << PAGE_SHIFT) + + (blkif_seg->first_sect << SECTOR_SHIFT); + gcopy_seg->source.foreign.ref = blkif_seg->gref; + gcopy_seg->source.foreign.offset = blkif_seg->first_sect << SECTOR_SHIFT; +@@ -386,7 +386,7 @@ guest_copy2(struct td_xenblkif * const blkif, + gcopy_seg->flags = GNTCOPY_source_gref; + } else { + /* copy to guest */ +- gcopy_seg->source.virt = tapreq->vma + (i << PAGE_SHIFT) ++ gcopy_seg->source.virt = req->vma + (i << PAGE_SHIFT) + + (blkif_seg->first_sect << SECTOR_SHIFT); + gcopy_seg->dest.foreign.ref = blkif_seg->gref; + gcopy_seg->dest.foreign.offset = blkif_seg->first_sect << SECTOR_SHIFT; +@@ -400,7 +400,7 @@ guest_copy2(struct td_xenblkif * const blkif, + << SECTOR_SHIFT; + } + #else +- gcopy_seg->iov.iov_base = tapreq->vma + (i << PAGE_SHIFT) ++ gcopy_seg->iov.iov_base = req->vma + (i << PAGE_SHIFT) + + (blkif_seg->first_sect << SECTOR_SHIFT); + gcopy_seg->iov.iov_len = (blkif_seg->last_sect + - blkif_seg->first_sect +@@ -410,23 +410,23 @@ guest_copy2(struct td_xenblkif * const blkif, + gcopy_seg->offset = blkif_seg->first_sect << SECTOR_SHIFT; + } + +- gcopy.dir = blkif_rq_wr(&tapreq->msg); ++ gcopy.dir = blkif_rq_wr(&req->msg); + gcopy.domid = blkif->domid; + #endif +- gcopy.count = tapreq->msg.nr_segments; +- gcopy.segments = tapreq->gcopy_segs; ++ gcopy.count = req->msg.nr_segments; ++ gcopy.segments = req->gcopy_segs; + + err = -ioctl(blkif->ctx->gntdev_fd, IOCTL_GNTDEV_GRANT_COPY, &gcopy); + if (err) { + err = -errno; + RING_ERR(blkif, "failed to grant-copy request %"PRIu64" " +- "(%d segments): %s\n", tapreq->msg.id, +- tapreq->msg.nr_segments, strerror(-err)); ++ "(%d segments): %s\n", req->msg.id, ++ req->msg.nr_segments, strerror(-err)); + goto out; + } + +- for (i = 0; i < tapreq->msg.nr_segments; i++) { +- struct gntdev_grant_copy_segment *gcopy_seg = &tapreq->gcopy_segs[i]; ++ for (i = 0; i < req->msg.nr_segments; i++) { ++ struct gntdev_grant_copy_segment *gcopy_seg = &req->gcopy_segs[i]; + if (gcopy_seg->status != GNTST_okay) { + /* + * TODO use gnttabop_error for reporting errors, defined in +@@ -434,7 +434,7 @@ guest_copy2(struct td_xenblkif * const blkif, + * user space) + */ + RING_ERR(blkif, "req %lu: failed to grant-copy segment %d: %d\n", +- tapreq->msg.id, i, gcopy_seg->status); ++ req->msg.id, i, gcopy_seg->status); + err = -EIO; + goto out; + } +@@ -451,13 +451,13 @@ out: + * any more. + * + * @blkif the VBD the request belongs belongs to +- * @tapreq the request to complete TODO rename to req ++ * @req the request to complete + * @error completion status of the request + * @final controls whether the other end should be notified + */ + void + tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, +- struct td_xenblkif_req* tapreq, int err, const int final) ++ struct td_xenblkif_req* req, int err, const int final) + { + int _err; + long long *max = NULL, *sum = NULL, *cnt = NULL; +@@ -466,13 +466,13 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + uint64_t *ticks = NULL; + + ASSERT(blkif); +- ASSERT(tapreq); ++ ASSERT(req); + ASSERT(depth >= 0); + + depth++; + + processing_barrier_message = +- tapreq->msg.operation == BLKIF_OP_WRITE_BARRIER; ++ req->msg.operation == BLKIF_OP_WRITE_BARRIER; + + /* + * If a barrier request completes, check whether it's an I/O completion +@@ -484,8 +484,8 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + * called again passing the barrier request. + */ + if (unlikely(processing_barrier_message)) { +- ASSERT(blkif->barrier.msg == &tapreq->msg); +- if (tapreq->msg.nr_segments && !blkif->barrier.io_done) { ++ ASSERT(blkif->barrier.msg == &req->msg); ++ if (req->msg.nr_segments && !blkif->barrier.io_done) { + blkif->barrier.io_err = err; + blkif->barrier.io_done = true; + } +@@ -494,7 +494,7 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + } + + if (likely(!blkif->dead)) { +- if (blkif_rq_rd(&tapreq->msg)) { ++ if (blkif_rq_rd(&req->msg)) { + /* + * TODO stats should be collected after grant-copy for better + * accuracy +@@ -507,14 +507,14 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + blkif->vbd_stats.stats->read_reqs_completed++; + ticks = &blkif->vbd_stats.stats->read_total_ticks; + if (likely(!err)) { +- _err = guest_copy2(blkif, tapreq); ++ _err = guest_copy2(blkif, req); + if (unlikely(_err)) { + err = _err; + RING_ERR(blkif, "req %lu: failed to copy from/to guest: " +- "%s\n", tapreq->msg.id, strerror(-err)); ++ "%s\n", req->msg.id, strerror(-err)); + } + } +- } else if (blkif_rq_wr(&tapreq->msg)) { ++ } else if (blkif_rq_wr(&req->msg)) { + if (likely(blkif->stats.xenvbd)) { + cnt = &blkif->stats.xenvbd->st_wr_cnt; + sum = &blkif->stats.xenvbd->st_wr_sum_usecs; +@@ -528,7 +528,7 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + struct timeval now; + long long interval; + gettimeofday(&now, NULL); +- interval = timeval_to_us(&now) - timeval_to_us(&tapreq->ts); ++ interval = timeval_to_us(&now) - timeval_to_us(&req->ts); + *ticks += interval; + if (interval > *max) + *max = interval; +@@ -542,10 +542,10 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + else + _err = BLKIF_RSP_ERROR; + +- xenio_blkif_put_response(blkif, tapreq, _err, final); ++ xenio_blkif_put_response(blkif, req, _err, final); + } + +- tapdisk_xenblkif_free_request(blkif, tapreq); ++ tapdisk_xenblkif_free_request(blkif, req); + + blkif->stats.reqs.out++; + if (final) +@@ -601,13 +601,13 @@ static inline void + __tapdisk_xenblkif_request_cb(struct td_vbd_request * const vreq, + const int error, void * const token, const int final) + { +- struct td_xenblkif_req *tapreq; ++ struct td_xenblkif_req *req; + struct td_xenblkif * const blkif = token; + + ASSERT(vreq); + ASSERT(blkif); + +- tapreq = container_of(vreq, struct td_xenblkif_req, vreq); ++ req = container_of(vreq, struct td_xenblkif_req, vreq); + + if (error) { + if (likely(!blkif->dead)) { +@@ -616,7 +616,7 @@ __tapdisk_xenblkif_request_cb(struct td_vbd_request * const vreq, + } + } + +- tapdisk_xenblkif_complete_request(blkif, tapreq, error, final); ++ tapdisk_xenblkif_complete_request(blkif, req, error, final); + } + + +@@ -659,7 +659,7 @@ tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, + } + + /* +- * Vectorises the request: creates the struct iovec (in tapreq->iov) that ++ * Vectorises the request: creates the struct iovec (in req->iov) that + * describes each segment to be transferred. Also, merges consecutive + * segments. + * +@@ -736,32 +736,32 @@ out: + * processing. + * + * @param blkif the block interface +- * @param tapreq the request to prepare TODO rename to req ++ * @param req the request to prepare + * @returns 0 on success + * + * XXX only called by tapdisk_xenblkif_queue_request + */ + static inline int + tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, +- struct td_xenblkif_req * const tapreq) ++ struct td_xenblkif_req * const req) + { + int err = 0; + td_vbd_request_t *vreq; + +- ASSERT(tapreq); ++ ASSERT(req); + +- vreq = &tapreq->vreq; ++ vreq = &req->vreq; + ASSERT(vreq); + memset(vreq, 0, sizeof(*vreq)); + +- tapreq->vma = NULL; +- switch (tapreq->msg.operation) { ++ req->vma = NULL; ++ switch (req->msg.operation) { + case BLKIF_OP_READ: + if (likely(blkif->stats.xenvbd)) + blkif->stats.xenvbd->st_rd_req++; + if (likely(blkif->vbd_stats.stats)) + blkif->vbd_stats.stats->read_reqs_submitted++; +- tapreq->prot = PROT_WRITE; ++ req->prot = PROT_WRITE; + vreq->op = TD_OP_READ; + break; + case BLKIF_OP_WRITE: +@@ -770,32 +770,32 @@ tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, + blkif->stats.xenvbd->st_wr_req++; + if (likely(blkif->vbd_stats.stats)) + blkif->vbd_stats.stats->write_reqs_submitted++; +- tapreq->prot = PROT_READ; ++ req->prot = PROT_READ; + vreq->op = TD_OP_WRITE; + break; + default: + RING_ERR(blkif, "req %lu: invalid request type %d\n", +- tapreq->msg.id, tapreq->msg.operation); ++ req->msg.id, req->msg.operation); + err = EOPNOTSUPP; + goto out; + } + /* Timestamp before the requests leave the blkif layer */ +- gettimeofday(&tapreq->ts, NULL); ++ gettimeofday(&req->ts, NULL); + + /* + * Check that the number of segments is sane. + */ +- if (unlikely((tapreq->msg.nr_segments == 0 && +- tapreq->msg.operation != BLKIF_OP_WRITE_BARRIER) || +- tapreq->msg.nr_segments > BLKIF_MAX_BUFFER_SEGMENTS_PER_REQUEST)) { ++ if (unlikely((req->msg.nr_segments == 0 && ++ req->msg.operation != BLKIF_OP_WRITE_BARRIER) || ++ req->msg.nr_segments > BLKIF_MAX_BUFFER_SEGMENTS_PER_REQUEST)) { + RING_ERR(blkif, "req %lu: bad number of segments in request (%d)\n", +- tapreq->msg.id, tapreq->msg.nr_segments); ++ req->msg.id, req->msg.nr_segments); + err = EINVAL; + goto out; + } + +- if (likely(tapreq->msg.nr_segments)) +- err = tapdisk_xenblkif_parse_request(blkif, tapreq); ++ if (likely(req->msg.nr_segments)) ++ err = tapdisk_xenblkif_parse_request(blkif, req); + /* + * If we only got one request from the ring and that was a barrier one, + * check whether the barrier requests completion conditions are satisfied +@@ -820,32 +820,32 @@ out: + * + * @param blkif the block interface + * @param msg the ring request +- * @param tapreq the intermediate request TODO rename to req ++ * @param req the intermediate request + * + * TODO don't really need to supply the ring request since it's either way +- * contained in the tapreq ++ * contained in the req + * + * XXX only called by tapdisk_xenblkif_queue_requests + */ + static inline int + tapdisk_xenblkif_queue_request(struct td_xenblkif * const blkif, +- blkif_request_t *msg, struct td_xenblkif_req *tapreq) ++ blkif_request_t *msg, struct td_xenblkif_req *req) + { + int err; + int queue_request; + + ASSERT(blkif); + ASSERT(msg); +- ASSERT(tapreq); ++ ASSERT(req); + +- queue_request = tapreq->msg.nr_segments != 0; ++ queue_request = req->msg.nr_segments != 0; + + /* +- * Do not use tapreq after tapdisk_xenblkif_make_vbd_request +- * because this function can release tapreq->msg and reinsert it ++ * Do not use req after tapdisk_xenblkif_make_vbd_request ++ * because this function can release req->msg and reinsert it + * in the reqs_free array. + */ +- err = tapdisk_xenblkif_make_vbd_request(blkif, tapreq); ++ err = tapdisk_xenblkif_make_vbd_request(blkif, req); + if (unlikely(err)) { + /* TODO log error */ + blkif->stats.errors.map++; +@@ -853,7 +853,7 @@ tapdisk_xenblkif_queue_request(struct td_xenblkif * const blkif, + } + + if (likely(queue_request)) { +- err = tapdisk_vbd_queue_request(blkif->vbd, &tapreq->vreq); ++ err = tapdisk_vbd_queue_request(blkif->vbd, &req->vreq); + if (unlikely(err)) { + /* TODO log error */ + blkif->stats.errors.vbd++; +@@ -879,19 +879,19 @@ tapdisk_xenblkif_queue_requests(struct td_xenblkif * const blkif, + + for (i = 0; i < nr_reqs; i++) { /* for each request in the ring... */ + blkif_request_t *msg = reqs[i]; +- struct td_xenblkif_req *tapreq; ++ struct td_xenblkif_req *req; + + ASSERT(msg); + +- tapreq = msg_to_tapreq(msg); ++ req = msg_to_tapreq(msg); + +- ASSERT(tapreq); ++ ASSERT(req); + +- err = tapdisk_xenblkif_queue_request(blkif, msg, tapreq); ++ err = tapdisk_xenblkif_queue_request(blkif, msg, req); + if (err) { + /* TODO log error */ + nr_errors++; +- tapdisk_xenblkif_complete_request(blkif, tapreq, err, 1); ++ tapdisk_xenblkif_complete_request(blkif, req, err, 1); + } + } + diff --git a/SOURCES/0021-td-req-remove-old-code.patch b/SOURCES/0021-td-req-remove-old-code.patch new file mode 100644 index 0000000..5210873 --- /dev/null +++ b/SOURCES/0021-td-req-remove-old-code.patch @@ -0,0 +1,45 @@ +From d97bbee2474337f722262e3366a86f71b6f1813e Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Fri, 28 Nov 2025 12:44:55 +0100 +Subject: [PATCH] td-req: remove old code + +Remove code running on linux kernel 4.4 or below. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 15 --------------- + 1 file changed, 15 deletions(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index b271d3bf..68760dd0 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -375,7 +375,6 @@ guest_copy2(struct td_xenblkif * const blkif, + for (i = 0; i < req->msg.nr_segments; i++) { + struct blkif_request_segment *blkif_seg = &req->msg.seg[i]; + struct gntdev_grant_copy_segment *gcopy_seg = &req->gcopy_segs[i]; +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + if (blkif_rq_wr(&req->msg)) { + /* copy from guest */ + gcopy_seg->dest.virt = req->vma + (i << PAGE_SHIFT) +@@ -399,20 +398,6 @@ guest_copy2(struct td_xenblkif * const blkif, + + 1) + << SECTOR_SHIFT; + } +-#else +- gcopy_seg->iov.iov_base = req->vma + (i << PAGE_SHIFT) +- + (blkif_seg->first_sect << SECTOR_SHIFT); +- gcopy_seg->iov.iov_len = (blkif_seg->last_sect +- - blkif_seg->first_sect +- + 1) +- << SECTOR_SHIFT; +- gcopy_seg->ref = blkif_seg->gref; +- gcopy_seg->offset = blkif_seg->first_sect << SECTOR_SHIFT; +- } +- +- gcopy.dir = blkif_rq_wr(&req->msg); +- gcopy.domid = blkif->domid; +-#endif + gcopy.count = req->msg.nr_segments; + gcopy.segments = req->gcopy_segs; + diff --git a/SOURCES/0022-td-req-fix-typo-in-a-comment.patch b/SOURCES/0022-td-req-fix-typo-in-a-comment.patch new file mode 100644 index 0000000..00dda3a --- /dev/null +++ b/SOURCES/0022-td-req-fix-typo-in-a-comment.patch @@ -0,0 +1,27 @@ +From 462b92fa2c2d64ce558c6a3dd5ce3dcf7456e09c Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Tue, 17 Mar 2026 16:52:55 +0100 +Subject: [PATCH] td-req: fix typo in a comment + +Fix 'fist_sect' to 'first_sect'. + +No functional change. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index 68760dd0..028e4bf6 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -669,7 +669,7 @@ tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, + iov++; + iov->base = next; + iov->secs = size; +- } else /* The "else" is true if fist_sect is 0. */ ++ } else /* The "else" is true if first_sect is 0. */ + iov->secs += size; + + last = iov->base + (iov->secs << SECTOR_SHIFT); diff --git a/SOURCES/0023-libqcow2-manage-libqcow2-sources-import.patch b/SOURCES/0023-libqcow2-manage-libqcow2-sources-import.patch new file mode 100644 index 0000000..2f35d44 --- /dev/null +++ b/SOURCES/0023-libqcow2-manage-libqcow2-sources-import.patch @@ -0,0 +1,403 @@ +From 37ddec3d3d0b08715b0a8e341a5b1238df55e4c1 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:16 +0100 +Subject: [PATCH] libqcow2: manage libqcow2 sources import + +This commit includes a list of files imported from qemu to build +the qcow2 library for tapdisk. +A small script helps to import files and diff them to ease the +sources management. + +Signed-off-by: Anthoine Bourgeois +--- + manage-qemu-sources.sh | 73 ++++++++++ + qemu-files.lst | 300 +++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 373 insertions(+) + create mode 100755 manage-qemu-sources.sh + create mode 100644 qemu-files.lst + +diff --git a/manage-qemu-sources.sh b/manage-qemu-sources.sh +new file mode 100755 +index 00000000..35c1bb32 +--- /dev/null ++++ b/manage-qemu-sources.sh +@@ -0,0 +1,73 @@ ++#!/bin/bash ++ ++usage_function () { ++ echo "Usage: $0 qemu_directory [diff|sync]" ++ exit 0 ++} ++ ++if [ $# -ne 2 ]; then ++ usage_function ++fi ++ ++qemudir=${1} ++cmd=${2} ++ ++if [ ${cmd} != "diff" -a ${cmd} != "sync" ]; then ++ echo "Wrong command: choose 'diff' or 'sync'" ++ usage_function ++fi ++ ++diff_function () { ++ if [ ! -f ${qemudir}/${src_prefix}/${1} ]; then ++ echo "${qemudir}/${src_prefix}/${1} doesn't exist." ++ exit 0 ++ fi ++ if [ ! -f ${dst_prefix}/${1} ]; then ++ echo "${dst_prefix}/${1} doesn't exist." ++ exit 0 ++ fi ++ if ! diff -q ${qemudir}/${src_prefix}/${1} ${dst_prefix}/${1}; then ++ diff -Npur ${qemudir}/${src_prefix}/${1} ${dst_prefix}/${1} ++ fi ++} ++ ++sync_warning () { ++ echo "This command will erase all the modifications you did" ++ echo "in all the qemu files you imported." ++ echo -n "Are you sure you want to sync all the files ? (N/y) " ++ read ok ++ if [ "$ok" == "y" ]; then ++ echo "Fine! Start in:" ++ for i in $(seq 5); do ++ echo -n "$((6 - i)) " ++ sleep 1 ++ done ++ echo "Let's go!" ++ else ++ echo "Abort!" ++ exit 0 ++ fi ++} ++ ++sync_function () { ++ echo "cp ${qemudir}/${src_prefix}/${1} ${dst_prefix}/${1}" ++ mkdir -p $(dirname "${dst_prefix}/${1}") ++ cp -f "${qemudir}/${src_prefix}/${1}" "${dst_prefix}/${1}" ++} ++ ++if [ ${cmd} == "sync" ]; then ++ sync_warning ++fi ++ ++while read f; do ++ if [ ${f:0:1} == '#' ]; then ++ src_prefix=$(echo ${f:1} | awk -F ':' '{print $1}') ++ dst_prefix=$(echo ${f:1} | awk -F ':' '{print $2}') ++ continue ++ fi ++ if [ ${cmd} == "diff" ]; then ++ diff_function ${f} ${src_prefix} ${dst_prefix} ++ elif [ ${cmd} == "sync" ]; then ++ sync_function ${f} ${src_prefix} ${dst_prefix} ++ fi ++done < qemu-files.lst +diff --git a/qemu-files.lst b/qemu-files.lst +new file mode 100644 +index 00000000..1b3e4a2c +--- /dev/null ++++ b/qemu-files.lst +@@ -0,0 +1,300 @@ ++#.:. ++include/authz/base.h ++include/block/accounting.h ++include/block/aio.h ++include/block/aio-wait.h ++include/block/aio_task.h ++include/block/block-common.h ++include/block/block-global-state.h ++include/block/block-io.h ++include/block/block.h ++include/block/block_int-common.h ++include/block/block_int-global-state.h ++include/block/block_int-io.h ++include/block/block_int.h ++include/block/blockjob.h ++include/block/blockjob_int.h ++include/block/dirty-bitmap.h ++include/block/export.h ++include/block/graph-lock.h ++include/block/nbd.h ++include/block/qapi.h ++include/block/qdict.h ++include/block/raw-aio.h ++include/block/snapshot.h ++include/block/thread-pool.h ++include/crypto/hash.h ++include/crypto/tlscreds.h ++include/crypto/tlscredsanon.h ++include/crypto/tlscredspsk.h ++include/crypto/tlscredsx509.h ++include/crypto/tlssession.h ++include/exec/hwaddr.h ++include/glib-compat.h ++include/hw/block/block.h ++include/hw/qdev-core.h ++include/io/channel.h ++include/io/channel-file.h ++include/io/channel-socket.h ++include/io/channel-tls.h ++include/io/channel-util.h ++include/io/channel-watch.h ++include/io/task.h ++include/qapi/clone-visitor.h ++include/qapi/compat-policy.h ++include/qapi/dealloc-visitor.h ++include/qapi/error.h ++include/qapi/qmp-event.h ++include/qapi/qmp/dispatch.h ++include/qapi/qmp/json-parser.h ++include/qapi/qmp/json-writer.h ++include/qapi/qmp/qbool.h ++include/qapi/qmp/qdict.h ++include/qapi/qmp/qerror.h ++include/qapi/qmp/qjson.h ++include/qapi/qmp/qlist.h ++include/qapi/qmp/qnull.h ++include/qapi/qmp/qnum.h ++include/qapi/qmp/qobject.h ++include/qapi/qmp/qstring.h ++include/qapi/qobject-input-visitor.h ++include/qapi/qobject-output-visitor.h ++include/qapi/string-input-visitor.h ++include/qapi/string-output-visitor.h ++include/qapi/util.h ++include/qapi/visitor-impl.h ++include/qapi/visitor.h ++include/qemu/atomic.h ++include/qemu/bitmap.h ++include/qemu/bitops.h ++include/qemu/bswap.h ++include/qemu/clang-tsa.h ++include/qemu/compiler.h ++include/qemu/config-file.h ++include/qemu/coroutine-core.h ++include/qemu/coroutine-tls.h ++include/qemu/coroutine.h ++include/qemu/coroutine_int.h ++include/qemu/ctype.h ++include/qemu/cutils.h ++include/qemu/defer-call.h ++include/qemu/error-report.h ++include/qemu/event_notifier.h ++include/qemu/futex.h ++include/qemu/hbitmap.h ++include/qemu/help_option.h ++include/qemu/host-utils.h ++include/qemu/id.h ++include/qemu/iov.h ++include/qemu/job.h ++include/qemu/lockable.h ++include/qemu/main-loop.h ++include/qemu/memalign.h ++include/qemu/module.h ++include/qemu/notify.h ++include/qemu/option.h ++include/qemu/option_int.h ++include/qemu/osdep.h ++include/qemu/processor.h ++include/qemu/progress_meter.h ++include/qemu/qdist.h ++include/qemu/qemu-print.h ++include/qemu/qemu-progress.h ++include/qemu/qsp.h ++include/qemu/queue.h ++include/qemu/range.h ++include/qemu/ratelimit.h ++include/qemu/rcu.h ++include/qemu/rcu_queue.h ++include/qemu/sockets.h ++include/qemu/stats64.h ++include/qemu/sys_membarrier.h ++include/qemu/thread-posix.h ++include/qemu/thread.h ++include/qemu/timed-average.h ++include/qemu/timer.h ++include/qemu/transactions.h ++include/qemu/typedefs.h ++include/qemu/unicode.h ++include/qemu/units.h ++include/qemu/uri.h ++include/qemu/yank.h ++include/qom/object.h ++include/qom/qom-qobject.h ++include/sysemu/block-backend-common.h ++include/sysemu/block-backend-global-state.h ++include/sysemu/block-backend-io.h ++include/sysemu/block-backend.h ++include/sysemu/blockdev.h ++include/sysemu/cpu-timers.h ++include/sysemu/os-posix.h ++#block:include ++qcow2.h ++#build:include ++block/module_block.h ++qapi/qapi-builtin-types.h ++qapi/qapi-builtin-visit.h ++qapi/qapi-commands-block.h ++qapi/qapi-commands-block-core.h ++qapi/qapi-commands-common.h ++qapi/qapi-commands-job.h ++qapi/qapi-events-block-core.h ++qapi/qapi-events-common.h ++qapi/qapi-events-job.h ++qapi/qapi-types-block.h ++qapi/qapi-types-block-core.h ++qapi/qapi-types-block-export.h ++qapi/qapi-types-common.h ++qapi/qapi-types-compat.h ++qapi/qapi-types-crypto.h ++qapi/qapi-types-error.h ++qapi/qapi-types-job.h ++qapi/qapi-types-run-state.h ++qapi/qapi-types-sockets.h ++qapi/qapi-types-yank.h ++qapi/qapi-visit-block-core.h ++qapi/qapi-visit-common.h ++qapi/qapi-visit-crypto.h ++qapi/qapi-visit-job.h ++qapi/qapi-visit-sockets.h ++qapi/qapi-visit-yank.h ++#.:qcow2/lib ++authz/base.c ++block.c ++blockdev.c ++blockjob.c ++block/accounting.c ++block/aio_task.c ++block/block-backend.c ++block/block-gen.h ++block/commit.c ++block/coroutines.h ++block/dirty-bitmap.c ++block/file-posix.c ++block/graph-lock.c ++block/io.c ++block/linux-aio.c ++block/mirror.c ++block/monitor/bitmap-qmp-cmds.c ++block/progress_meter.c ++block/qapi.c ++block/raw-format.c ++block/snapshot.c ++hw/block/block.c ++hw/block/hd-geometry.c ++io/channel.c ++io/channel-file.c ++io/channel-socket.c ++io/channel-util.c ++io/channel-watch.c ++io/task.c ++job.c ++job-qmp.c ++qapi/qapi-clone-visitor.c ++qapi/qapi-dealloc-visitor.c ++qapi/qapi-util.c ++qapi/qapi-visit-core.c ++qapi/qobject-input-visitor.c ++qapi/qobject-output-visitor.c ++qapi/string-input-visitor.c ++qapi/string-output-visitor.c ++qobject/block-qdict.c ++qobject/json-lexer.c ++qobject/json-parser.c ++qobject/json-parser-int.h ++qobject/json-streamer.c ++qobject/json-writer.c ++qobject/qbool.c ++qobject/qdict.c ++qobject/qjson.c ++qobject/qlist.c ++qobject/qnull.c ++qobject/qnum.c ++qobject/qobject-internal.h ++qobject/qobject.c ++qobject/qstring.c ++qom/container.c ++qom/object.c ++qom/qom-qobject.c ++system/cpus.c ++util/aio-posix.c ++util/aio-posix.h ++util/aio-wait.c ++util/aiocb.c ++util/async.c ++util/bitmap.c ++util/bitops.c ++util/bufferiszero.c ++util/compatfd.c ++util/coroutine-ucontext.c ++util/cutils.c ++util/defer-call.c ++util/error-report.c ++util/error.c ++util/event_notifier-posix.c ++util/fdmon-epoll.c ++util/fdmon-poll.c ++util/hbitmap.c ++util/host-utils.c ++util/id.c ++util/iov.c ++util/lockcnt.c ++util/main-loop.c ++util/memalign.c ++util/module.c ++util/notify.c ++util/osdep.c ++util/oslib-posix.c ++util/qemu-coroutine-lock.c ++util/qemu-coroutine.c ++util/qemu-option.c ++util/qemu-print.c ++util/qemu-sockets.c ++util/qemu-thread-common.h ++util/qemu-thread-posix.c ++util/qemu-timer-common.c ++util/qemu-timer.c ++util/qsp.c ++util/range.c ++util/rcu.c ++util/stats64.c ++util/thread-pool.c ++util/timed-average.c ++util/transactions.c ++util/unicode.c ++util/uri.c ++util/yank.c ++qapi/qmp-dispatch.c ++qapi/qmp-event.c ++#block:qcow2/lib ++qcow2-bitmap.c ++qcow2-cache.c ++qcow2-cluster.c ++qcow2-refcount.c ++qcow2-snapshot.c ++qcow2-threads.c ++qcow2.c ++#build:qcow2/lib ++block/block-gen.c ++qapi/qapi-emit-events.h ++qapi/qapi-events-block-core.c ++qapi/qapi-events-job.c ++qapi/qapi-types-block-core.c ++qapi/qapi-types-common.c ++qapi/qapi-types-crypto.c ++qapi/qapi-types-job.c ++qapi/qapi-types-sockets.c ++qapi/qapi-types-yank.c ++qapi/qapi-visit-block-core.c ++qapi/qapi-visit-common.c ++qapi/qapi-visit-crypto.c ++qapi/qapi-visit-job.c ++qapi/qapi-visit-sockets.c ++qapi/qapi-visit-yank.c ++#.:qcow2 ++qemu-img.c ++#util:qcow2 ++qemu-config.c ++qemu-progress.c ++#build:qcow2 ++qemu-img-cmds.h diff --git a/SOURCES/0024-libqcow2-import-vanilla-sources-from-qemu-9.1.1.patch b/SOURCES/0024-libqcow2-import-vanilla-sources-from-qemu-9.1.1.patch new file mode 100644 index 0000000..ea8141a --- /dev/null +++ b/SOURCES/0024-libqcow2-import-vanilla-sources-from-qemu-9.1.1.patch @@ -0,0 +1,146462 @@ +From 0ac816d9f87c57748214366691a73928fbab76e2 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:22 +0100 +Subject: [PATCH] libqcow2: import vanilla sources from qemu 9.1.1 + +All libqcow2 sources come from qemu-9.1.1. +This code is GPLv2. + +Signed-off-by: Anthoine Bourgeois +--- + include/authz/base.h | 101 + + include/block/accounting.h | 124 + + include/block/aio-wait.h | 152 + + include/block/aio.h | 723 ++ + include/block/aio_task.h | 52 + + include/block/block-common.h | 567 ++ + include/block/block-global-state.h | 305 + + include/block/block-io.h | 455 + + include/block/block.h | 32 + + include/block/block_int-common.h | 1318 +++ + include/block/block_int-global-state.h | 326 + + include/block/block_int-io.h | 194 + + include/block/block_int.h | 33 + + include/block/blockjob.h | 238 + + include/block/blockjob_int.h | 168 + + include/block/dirty-bitmap.h | 130 + + include/block/export.h | 91 + + include/block/graph-lock.h | 285 + + include/block/module_block.h | 19 + + include/block/nbd.h | 509 + + include/block/qapi.h | 52 + + include/block/qdict.h | 35 + + include/block/raw-aio.h | 94 + + include/block/snapshot.h | 113 + + include/block/thread-pool.h | 43 + + include/crypto/hash.h | 192 + + include/crypto/tlscreds.h | 67 + + include/crypto/tlscredsanon.h | 100 + + include/crypto/tlscredspsk.h | 95 + + include/crypto/tlscredsx509.h | 104 + + include/crypto/tlssession.h | 356 + + include/exec/hwaddr.h | 26 + + include/glib-compat.h | 131 + + include/hw/block/block.h | 110 + + include/hw/qdev-core.h | 1111 +++ + include/io/channel-file.h | 110 + + include/io/channel-socket.h | 265 + + include/io/channel-tls.h | 146 + + include/io/channel-util.h | 75 + + include/io/channel-watch.h | 90 + + include/io/channel.h | 1005 ++ + include/io/task.h | 350 + + include/qapi/clone-visitor.h | 62 + + include/qapi/compat-policy.h | 45 + + include/qapi/dealloc-visitor.h | 28 + + include/qapi/error.h | 540 ++ + include/qapi/qapi-builtin-types.h | 184 + + include/qapi/qapi-builtin-visit.h | 68 + + include/qapi/qapi-commands-block-core.h | 103 + + include/qapi/qapi-commands-block.h | 38 + + include/qapi/qapi-commands-common.h | 19 + + include/qapi/qapi-commands-job.h | 33 + + include/qapi/qapi-events-block-core.h | 43 + + include/qapi/qapi-events-common.h | 19 + + include/qapi/qapi-events-job.h | 21 + + include/qapi/qapi-types-block-core.h | 3626 +++++++ + include/qapi/qapi-types-block-export.h | 274 + + include/qapi/qapi-types-block.h | 161 + + include/qapi/qapi-types-common.h | 163 + + include/qapi/qapi-types-compat.h | 57 + + include/qapi/qapi-types-crypto.h | 538 ++ + include/qapi/qapi-types-error.h | 32 + + include/qapi/qapi-types-job.h | 140 + + include/qapi/qapi-types-run-state.h | 312 + + include/qapi/qapi-types-sockets.h | 220 + + include/qapi/qapi-types-yank.h | 83 + + include/qapi/qapi-visit-block-core.h | 960 ++ + include/qapi/qapi-visit-common.h | 55 + + include/qapi/qapi-visit-crypto.h | 165 + + include/qapi/qapi-visit-job.h | 51 + + include/qapi/qapi-visit-sockets.h | 91 + + include/qapi/qapi-visit-yank.h | 45 + + include/qapi/qmp-event.h | 18 + + include/qapi/qmp/dispatch.h | 67 + + include/qapi/qmp/json-parser.h | 46 + + include/qapi/qmp/json-writer.h | 35 + + include/qapi/qmp/qbool.h | 31 + + include/qapi/qmp/qdict.h | 71 + + include/qapi/qmp/qerror.h | 32 + + include/qapi/qmp/qjson.h | 31 + + include/qapi/qmp/qlist.h | 69 + + include/qapi/qmp/qnull.h | 33 + + include/qapi/qmp/qnum.h | 75 + + include/qapi/qmp/qobject.h | 144 + + include/qapi/qmp/qstring.h | 33 + + include/qapi/qobject-input-visitor.h | 82 + + include/qapi/qobject-output-visitor.h | 56 + + include/qapi/string-input-visitor.h | 27 + + include/qapi/string-output-visitor.h | 35 + + include/qapi/util.h | 72 + + include/qapi/visitor-impl.h | 137 + + include/qapi/visitor.h | 713 ++ + include/qcow2.h | 1074 +++ + include/qemu/atomic.h | 284 + + include/qemu/bitmap.h | 281 + + include/qemu/bitops.h | 634 ++ + include/qemu/bswap.h | 436 + + include/qemu/clang-tsa.h | 114 + + include/qemu/compiler.h | 276 + + include/qemu/config-file.h | 31 + + include/qemu/coroutine-core.h | 154 + + include/qemu/coroutine-tls.h | 165 + + include/qemu/coroutine.h | 311 + + include/qemu/coroutine_int.h | 77 + + include/qemu/ctype.h | 27 + + include/qemu/cutils.h | 308 + + include/qemu/defer-call.h | 16 + + include/qemu/error-report.h | 77 + + include/qemu/event_notifier.h | 46 + + include/qemu/futex.h | 41 + + include/qemu/hbitmap.h | 353 + + include/qemu/help_option.h | 33 + + include/qemu/host-utils.h | 861 ++ + include/qemu/id.h | 15 + + include/qemu/iov.h | 250 + + include/qemu/job.h | 731 ++ + include/qemu/lockable.h | 184 + + include/qemu/main-loop.h | 411 + + include/qemu/memalign.h | 61 + + include/qemu/module.h | 192 + + include/qemu/notify.h | 78 + + include/qemu/option.h | 151 + + include/qemu/option_int.h | 54 + + include/qemu/osdep.h | 831 ++ + include/qemu/processor.h | 25 + + include/qemu/progress_meter.h | 62 + + include/qemu/qdist.h | 61 + + include/qemu/qemu-print.h | 23 + + include/qemu/qemu-progress.h | 8 + + include/qemu/qsp.h | 27 + + include/qemu/queue.h | 576 ++ + include/qemu/range.h | 247 + + include/qemu/ratelimit.h | 97 + + include/qemu/rcu.h | 198 + + include/qemu/rcu_queue.h | 309 + + include/qemu/sockets.h | 156 + + include/qemu/stats64.h | 199 + + include/qemu/sys_membarrier.h | 27 + + include/qemu/thread-posix.h | 48 + + include/qemu/thread.h | 407 + + include/qemu/timed-average.h | 63 + + include/qemu/timer.h | 1038 ++ + include/qemu/transactions.h | 66 + + include/qemu/typedefs.h | 134 + + include/qemu/unicode.h | 7 + + include/qemu/units.h | 20 + + include/qemu/uri.h | 99 + + include/qemu/yank.h | 87 + + include/qom/object.h | 2058 ++++ + include/qom/qom-qobject.h | 43 + + include/sysemu/block-backend-common.h | 103 + + include/sysemu/block-backend-global-state.h | 133 + + include/sysemu/block-backend-io.h | 230 + + include/sysemu/block-backend.h | 21 + + include/sysemu/blockdev.h | 64 + + include/sysemu/cpu-timers.h | 104 + + include/sysemu/os-posix.h | 101 + + qcow2/LICENSE-gpl-2.0.txt | 338 + + qcow2/lib/authz/base.c | 83 + + qcow2/lib/block.c | 8432 +++++++++++++++++ + qcow2/lib/block/accounting.c | 298 + + qcow2/lib/block/aio_task.c | 126 + + qcow2/lib/block/block-backend.c | 2899 ++++++ + qcow2/lib/block/block-gen.c | 2636 ++++++ + qcow2/lib/block/block-gen.h | 46 + + qcow2/lib/block/commit.c | 600 ++ + qcow2/lib/block/coroutines.h | 92 + + qcow2/lib/block/dirty-bitmap.c | 890 ++ + qcow2/lib/block/file-posix.c | 4565 +++++++++ + qcow2/lib/block/graph-lock.c | 281 + + qcow2/lib/block/io.c | 3755 ++++++++ + qcow2/lib/block/linux-aio.c | 507 + + qcow2/lib/block/mirror.c | 2082 ++++ + qcow2/lib/block/monitor/bitmap-qmp-cmds.c | 317 + + qcow2/lib/block/progress_meter.c | 66 + + qcow2/lib/block/qapi.c | 1012 ++ + qcow2/lib/block/raw-format.c | 682 ++ + qcow2/lib/block/snapshot.c | 780 ++ + qcow2/lib/blockdev.c | 3649 +++++++ + qcow2/lib/blockjob.c | 630 ++ + qcow2/lib/hw/block/block.c | 278 + + qcow2/lib/hw/block/hd-geometry.c | 168 + + qcow2/lib/io/channel-file.c | 324 + + qcow2/lib/io/channel-socket.c | 985 ++ + qcow2/lib/io/channel-util.c | 62 + + qcow2/lib/io/channel-watch.c | 347 + + qcow2/lib/io/channel.c | 808 ++ + qcow2/lib/io/task.c | 241 + + qcow2/lib/job-qmp.c | 188 + + qcow2/lib/job.c | 1264 +++ + qcow2/lib/qapi/qapi-clone-visitor.c | 182 + + qcow2/lib/qapi/qapi-dealloc-visitor.c | 143 + + qcow2/lib/qapi/qapi-emit-events.h | 83 + + qcow2/lib/qapi/qapi-events-block-core.c | 323 + + qcow2/lib/qapi/qapi-events-job.c | 53 + + qcow2/lib/qapi/qapi-types-block-core.c | 2484 +++++ + qcow2/lib/qapi/qapi-types-common.c | 141 + + qcow2/lib/qapi/qapi-types-crypto.c | 413 + + qcow2/lib/qapi/qapi-types-job.c | 91 + + qcow2/lib/qapi/qapi-types-sockets.c | 209 + + qcow2/lib/qapi/qapi-types-yank.c | 80 + + qcow2/lib/qapi/qapi-util.c | 154 + + qcow2/lib/qapi/qapi-visit-block-core.c | 9398 +++++++++++++++++++ + qcow2/lib/qapi/qapi-visit-common.c | 176 + + qcow2/lib/qapi/qapi-visit-core.c | 444 + + qcow2/lib/qapi/qapi-visit-crypto.c | 1150 +++ + qcow2/lib/qapi/qapi-visit-job.c | 186 + + qcow2/lib/qapi/qapi-visit-sockets.c | 569 ++ + qcow2/lib/qapi/qapi-visit-yank.c | 186 + + qcow2/lib/qapi/qmp-dispatch.c | 296 + + qcow2/lib/qapi/qmp-event.c | 42 + + qcow2/lib/qapi/qobject-input-visitor.c | 776 ++ + qcow2/lib/qapi/qobject-output-visitor.c | 280 + + qcow2/lib/qapi/string-input-visitor.c | 414 + + qcow2/lib/qapi/string-output-visitor.c | 451 + + qcow2/lib/qcow2-bitmap.c | 1809 ++++ + qcow2/lib/qcow2-cache.c | 462 + + qcow2/lib/qcow2-cluster.c | 2562 +++++ + qcow2/lib/qcow2-refcount.c | 3750 ++++++++ + qcow2/lib/qcow2-snapshot.c | 1076 +++ + qcow2/lib/qcow2-threads.c | 527 ++ + qcow2/lib/qcow2.c | 6214 ++++++++++++ + qcow2/lib/qobject/block-qdict.c | 738 ++ + qcow2/lib/qobject/json-lexer.c | 365 + + qcow2/lib/qobject/json-parser-int.h | 54 + + qcow2/lib/qobject/json-parser.c | 590 ++ + qcow2/lib/qobject/json-streamer.c | 134 + + qcow2/lib/qobject/json-writer.c | 247 + + qcow2/lib/qobject/qbool.c | 63 + + qcow2/lib/qobject/qdict.c | 449 + + qcow2/lib/qobject/qjson.c | 232 + + qcow2/lib/qobject/qlist.c | 189 + + qcow2/lib/qobject/qnull.c | 36 + + qcow2/lib/qobject/qnum.c | 246 + + qcow2/lib/qobject/qobject-internal.h | 39 + + qcow2/lib/qobject/qobject.c | 72 + + qcow2/lib/qobject/qstring.c | 107 + + qcow2/lib/qom/container.c | 52 + + qcow2/lib/qom/object.c | 2899 ++++++ + qcow2/lib/qom/qom-qobject.c | 45 + + qcow2/lib/system/.dirstamp | 0 + qcow2/lib/system/cpus.c | 878 ++ + qcow2/lib/util/aio-posix.c | 789 ++ + qcow2/lib/util/aio-posix.h | 82 + + qcow2/lib/util/aio-wait.c | 86 + + qcow2/lib/util/aiocb.c | 55 + + qcow2/lib/util/async.c | 760 ++ + qcow2/lib/util/bitmap.c | 534 ++ + qcow2/lib/util/bitops.c | 157 + + qcow2/lib/util/bufferiszero.c | 126 + + qcow2/lib/util/compatfd.c | 89 + + qcow2/lib/util/coroutine-ucontext.c | 359 + + qcow2/lib/util/cutils.c | 1218 +++ + qcow2/lib/util/defer-call.c | 156 + + qcow2/lib/util/error-report.c | 394 + + qcow2/lib/util/error.c | 308 + + qcow2/lib/util/event_notifier-posix.c | 142 + + qcow2/lib/util/fdmon-epoll.c | 156 + + qcow2/lib/util/fdmon-poll.c | 107 + + qcow2/lib/util/hbitmap.c | 955 ++ + qcow2/lib/util/host-utils.c | 448 + + qcow2/lib/util/id.c | 69 + + qcow2/lib/util/iov.c | 712 ++ + qcow2/lib/util/lockcnt.c | 399 + + qcow2/lib/util/main-loop.c | 653 ++ + qcow2/lib/util/memalign.c | 92 + + qcow2/lib/util/module.c | 405 + + qcow2/lib/util/notify.c | 77 + + qcow2/lib/util/osdep.c | 615 ++ + qcow2/lib/util/oslib-posix.c | 933 ++ + qcow2/lib/util/qemu-coroutine-lock.c | 469 + + qcow2/lib/util/qemu-coroutine.c | 401 + + qcow2/lib/util/qemu-option.c | 1226 +++ + qcow2/lib/util/qemu-print.c | 70 + + qcow2/lib/util/qemu-sockets.c | 1475 +++ + qcow2/lib/util/qemu-thread-common.h | 54 + + qcow2/lib/util/qemu-thread-posix.c | 686 ++ + qcow2/lib/util/qemu-timer-common.c | 63 + + qcow2/lib/util/qemu-timer.c | 713 ++ + qcow2/lib/util/qsp.c | 813 ++ + qcow2/lib/util/range.c | 123 + + qcow2/lib/util/rcu.c | 472 + + qcow2/lib/util/stats64.c | 148 + + qcow2/lib/util/thread-pool.c | 381 + + qcow2/lib/util/timed-average.c | 231 + + qcow2/lib/util/transactions.c | 100 + + qcow2/lib/util/unicode.c | 156 + + qcow2/lib/util/uri.c | 1466 +++ + qcow2/lib/util/yank.c | 199 + + qcow2/qemu-config.c | 295 + + qcow2/qemu-img-cmds.h | 46 + + qcow2/qemu-img.c | 5621 +++++++++++ + qcow2/qemu-progress.c | 162 + + 293 files changed, 144109 insertions(+) + create mode 100644 include/authz/base.h + create mode 100644 include/block/accounting.h + create mode 100644 include/block/aio-wait.h + create mode 100644 include/block/aio.h + create mode 100644 include/block/aio_task.h + create mode 100644 include/block/block-common.h + create mode 100644 include/block/block-global-state.h + create mode 100644 include/block/block-io.h + create mode 100644 include/block/block.h + create mode 100644 include/block/block_int-common.h + create mode 100644 include/block/block_int-global-state.h + create mode 100644 include/block/block_int-io.h + create mode 100644 include/block/block_int.h + create mode 100644 include/block/blockjob.h + create mode 100644 include/block/blockjob_int.h + create mode 100644 include/block/dirty-bitmap.h + create mode 100644 include/block/export.h + create mode 100644 include/block/graph-lock.h + create mode 100644 include/block/module_block.h + create mode 100644 include/block/nbd.h + create mode 100644 include/block/qapi.h + create mode 100644 include/block/qdict.h + create mode 100644 include/block/raw-aio.h + create mode 100644 include/block/snapshot.h + create mode 100644 include/block/thread-pool.h + create mode 100644 include/crypto/hash.h + create mode 100644 include/crypto/tlscreds.h + create mode 100644 include/crypto/tlscredsanon.h + create mode 100644 include/crypto/tlscredspsk.h + create mode 100644 include/crypto/tlscredsx509.h + create mode 100644 include/crypto/tlssession.h + create mode 100644 include/exec/hwaddr.h + create mode 100644 include/glib-compat.h + create mode 100644 include/hw/block/block.h + create mode 100644 include/hw/qdev-core.h + create mode 100644 include/io/channel-file.h + create mode 100644 include/io/channel-socket.h + create mode 100644 include/io/channel-tls.h + create mode 100644 include/io/channel-util.h + create mode 100644 include/io/channel-watch.h + create mode 100644 include/io/channel.h + create mode 100644 include/io/task.h + create mode 100644 include/qapi/clone-visitor.h + create mode 100644 include/qapi/compat-policy.h + create mode 100644 include/qapi/dealloc-visitor.h + create mode 100644 include/qapi/error.h + create mode 100644 include/qapi/qapi-builtin-types.h + create mode 100644 include/qapi/qapi-builtin-visit.h + create mode 100644 include/qapi/qapi-commands-block-core.h + create mode 100644 include/qapi/qapi-commands-block.h + create mode 100644 include/qapi/qapi-commands-common.h + create mode 100644 include/qapi/qapi-commands-job.h + create mode 100644 include/qapi/qapi-events-block-core.h + create mode 100644 include/qapi/qapi-events-common.h + create mode 100644 include/qapi/qapi-events-job.h + create mode 100644 include/qapi/qapi-types-block-core.h + create mode 100644 include/qapi/qapi-types-block-export.h + create mode 100644 include/qapi/qapi-types-block.h + create mode 100644 include/qapi/qapi-types-common.h + create mode 100644 include/qapi/qapi-types-compat.h + create mode 100644 include/qapi/qapi-types-crypto.h + create mode 100644 include/qapi/qapi-types-error.h + create mode 100644 include/qapi/qapi-types-job.h + create mode 100644 include/qapi/qapi-types-run-state.h + create mode 100644 include/qapi/qapi-types-sockets.h + create mode 100644 include/qapi/qapi-types-yank.h + create mode 100644 include/qapi/qapi-visit-block-core.h + create mode 100644 include/qapi/qapi-visit-common.h + create mode 100644 include/qapi/qapi-visit-crypto.h + create mode 100644 include/qapi/qapi-visit-job.h + create mode 100644 include/qapi/qapi-visit-sockets.h + create mode 100644 include/qapi/qapi-visit-yank.h + create mode 100644 include/qapi/qmp-event.h + create mode 100644 include/qapi/qmp/dispatch.h + create mode 100644 include/qapi/qmp/json-parser.h + create mode 100644 include/qapi/qmp/json-writer.h + create mode 100644 include/qapi/qmp/qbool.h + create mode 100644 include/qapi/qmp/qdict.h + create mode 100644 include/qapi/qmp/qerror.h + create mode 100644 include/qapi/qmp/qjson.h + create mode 100644 include/qapi/qmp/qlist.h + create mode 100644 include/qapi/qmp/qnull.h + create mode 100644 include/qapi/qmp/qnum.h + create mode 100644 include/qapi/qmp/qobject.h + create mode 100644 include/qapi/qmp/qstring.h + create mode 100644 include/qapi/qobject-input-visitor.h + create mode 100644 include/qapi/qobject-output-visitor.h + create mode 100644 include/qapi/string-input-visitor.h + create mode 100644 include/qapi/string-output-visitor.h + create mode 100644 include/qapi/util.h + create mode 100644 include/qapi/visitor-impl.h + create mode 100644 include/qapi/visitor.h + create mode 100644 include/qcow2.h + create mode 100644 include/qemu/atomic.h + create mode 100644 include/qemu/bitmap.h + create mode 100644 include/qemu/bitops.h + create mode 100644 include/qemu/bswap.h + create mode 100644 include/qemu/clang-tsa.h + create mode 100644 include/qemu/compiler.h + create mode 100644 include/qemu/config-file.h + create mode 100644 include/qemu/coroutine-core.h + create mode 100644 include/qemu/coroutine-tls.h + create mode 100644 include/qemu/coroutine.h + create mode 100644 include/qemu/coroutine_int.h + create mode 100644 include/qemu/ctype.h + create mode 100644 include/qemu/cutils.h + create mode 100644 include/qemu/defer-call.h + create mode 100644 include/qemu/error-report.h + create mode 100644 include/qemu/event_notifier.h + create mode 100644 include/qemu/futex.h + create mode 100644 include/qemu/hbitmap.h + create mode 100644 include/qemu/help_option.h + create mode 100644 include/qemu/host-utils.h + create mode 100644 include/qemu/id.h + create mode 100644 include/qemu/iov.h + create mode 100644 include/qemu/job.h + create mode 100644 include/qemu/lockable.h + create mode 100644 include/qemu/main-loop.h + create mode 100644 include/qemu/memalign.h + create mode 100644 include/qemu/module.h + create mode 100644 include/qemu/notify.h + create mode 100644 include/qemu/option.h + create mode 100644 include/qemu/option_int.h + create mode 100644 include/qemu/osdep.h + create mode 100644 include/qemu/processor.h + create mode 100644 include/qemu/progress_meter.h + create mode 100644 include/qemu/qdist.h + create mode 100644 include/qemu/qemu-print.h + create mode 100644 include/qemu/qemu-progress.h + create mode 100644 include/qemu/qsp.h + create mode 100644 include/qemu/queue.h + create mode 100644 include/qemu/range.h + create mode 100644 include/qemu/ratelimit.h + create mode 100644 include/qemu/rcu.h + create mode 100644 include/qemu/rcu_queue.h + create mode 100644 include/qemu/sockets.h + create mode 100644 include/qemu/stats64.h + create mode 100644 include/qemu/sys_membarrier.h + create mode 100644 include/qemu/thread-posix.h + create mode 100644 include/qemu/thread.h + create mode 100644 include/qemu/timed-average.h + create mode 100644 include/qemu/timer.h + create mode 100644 include/qemu/transactions.h + create mode 100644 include/qemu/typedefs.h + create mode 100644 include/qemu/unicode.h + create mode 100644 include/qemu/units.h + create mode 100644 include/qemu/uri.h + create mode 100644 include/qemu/yank.h + create mode 100644 include/qom/object.h + create mode 100644 include/qom/qom-qobject.h + create mode 100644 include/sysemu/block-backend-common.h + create mode 100644 include/sysemu/block-backend-global-state.h + create mode 100644 include/sysemu/block-backend-io.h + create mode 100644 include/sysemu/block-backend.h + create mode 100644 include/sysemu/blockdev.h + create mode 100644 include/sysemu/cpu-timers.h + create mode 100644 include/sysemu/os-posix.h + create mode 100644 qcow2/LICENSE-gpl-2.0.txt + create mode 100644 qcow2/lib/authz/base.c + create mode 100644 qcow2/lib/block.c + create mode 100644 qcow2/lib/block/accounting.c + create mode 100644 qcow2/lib/block/aio_task.c + create mode 100644 qcow2/lib/block/block-backend.c + create mode 100644 qcow2/lib/block/block-gen.c + create mode 100644 qcow2/lib/block/block-gen.h + create mode 100644 qcow2/lib/block/commit.c + create mode 100644 qcow2/lib/block/coroutines.h + create mode 100644 qcow2/lib/block/dirty-bitmap.c + create mode 100644 qcow2/lib/block/file-posix.c + create mode 100644 qcow2/lib/block/graph-lock.c + create mode 100644 qcow2/lib/block/io.c + create mode 100644 qcow2/lib/block/linux-aio.c + create mode 100644 qcow2/lib/block/mirror.c + create mode 100644 qcow2/lib/block/monitor/bitmap-qmp-cmds.c + create mode 100644 qcow2/lib/block/progress_meter.c + create mode 100644 qcow2/lib/block/qapi.c + create mode 100644 qcow2/lib/block/raw-format.c + create mode 100644 qcow2/lib/block/snapshot.c + create mode 100644 qcow2/lib/blockdev.c + create mode 100644 qcow2/lib/blockjob.c + create mode 100644 qcow2/lib/hw/block/block.c + create mode 100644 qcow2/lib/hw/block/hd-geometry.c + create mode 100644 qcow2/lib/io/channel-file.c + create mode 100644 qcow2/lib/io/channel-socket.c + create mode 100644 qcow2/lib/io/channel-util.c + create mode 100644 qcow2/lib/io/channel-watch.c + create mode 100644 qcow2/lib/io/channel.c + create mode 100644 qcow2/lib/io/task.c + create mode 100644 qcow2/lib/job-qmp.c + create mode 100644 qcow2/lib/job.c + create mode 100644 qcow2/lib/qapi/qapi-clone-visitor.c + create mode 100644 qcow2/lib/qapi/qapi-dealloc-visitor.c + create mode 100644 qcow2/lib/qapi/qapi-emit-events.h + create mode 100644 qcow2/lib/qapi/qapi-events-block-core.c + create mode 100644 qcow2/lib/qapi/qapi-events-job.c + create mode 100644 qcow2/lib/qapi/qapi-types-block-core.c + create mode 100644 qcow2/lib/qapi/qapi-types-common.c + create mode 100644 qcow2/lib/qapi/qapi-types-crypto.c + create mode 100644 qcow2/lib/qapi/qapi-types-job.c + create mode 100644 qcow2/lib/qapi/qapi-types-sockets.c + create mode 100644 qcow2/lib/qapi/qapi-types-yank.c + create mode 100644 qcow2/lib/qapi/qapi-util.c + create mode 100644 qcow2/lib/qapi/qapi-visit-block-core.c + create mode 100644 qcow2/lib/qapi/qapi-visit-common.c + create mode 100644 qcow2/lib/qapi/qapi-visit-core.c + create mode 100644 qcow2/lib/qapi/qapi-visit-crypto.c + create mode 100644 qcow2/lib/qapi/qapi-visit-job.c + create mode 100644 qcow2/lib/qapi/qapi-visit-sockets.c + create mode 100644 qcow2/lib/qapi/qapi-visit-yank.c + create mode 100644 qcow2/lib/qapi/qmp-dispatch.c + create mode 100644 qcow2/lib/qapi/qmp-event.c + create mode 100644 qcow2/lib/qapi/qobject-input-visitor.c + create mode 100644 qcow2/lib/qapi/qobject-output-visitor.c + create mode 100644 qcow2/lib/qapi/string-input-visitor.c + create mode 100644 qcow2/lib/qapi/string-output-visitor.c + create mode 100644 qcow2/lib/qcow2-bitmap.c + create mode 100644 qcow2/lib/qcow2-cache.c + create mode 100644 qcow2/lib/qcow2-cluster.c + create mode 100644 qcow2/lib/qcow2-refcount.c + create mode 100644 qcow2/lib/qcow2-snapshot.c + create mode 100644 qcow2/lib/qcow2-threads.c + create mode 100644 qcow2/lib/qcow2.c + create mode 100644 qcow2/lib/qobject/block-qdict.c + create mode 100644 qcow2/lib/qobject/json-lexer.c + create mode 100644 qcow2/lib/qobject/json-parser-int.h + create mode 100644 qcow2/lib/qobject/json-parser.c + create mode 100644 qcow2/lib/qobject/json-streamer.c + create mode 100644 qcow2/lib/qobject/json-writer.c + create mode 100644 qcow2/lib/qobject/qbool.c + create mode 100644 qcow2/lib/qobject/qdict.c + create mode 100644 qcow2/lib/qobject/qjson.c + create mode 100644 qcow2/lib/qobject/qlist.c + create mode 100644 qcow2/lib/qobject/qnull.c + create mode 100644 qcow2/lib/qobject/qnum.c + create mode 100644 qcow2/lib/qobject/qobject-internal.h + create mode 100644 qcow2/lib/qobject/qobject.c + create mode 100644 qcow2/lib/qobject/qstring.c + create mode 100644 qcow2/lib/qom/container.c + create mode 100644 qcow2/lib/qom/object.c + create mode 100644 qcow2/lib/qom/qom-qobject.c + create mode 100644 qcow2/lib/system/.dirstamp + create mode 100644 qcow2/lib/system/cpus.c + create mode 100644 qcow2/lib/util/aio-posix.c + create mode 100644 qcow2/lib/util/aio-posix.h + create mode 100644 qcow2/lib/util/aio-wait.c + create mode 100644 qcow2/lib/util/aiocb.c + create mode 100644 qcow2/lib/util/async.c + create mode 100644 qcow2/lib/util/bitmap.c + create mode 100644 qcow2/lib/util/bitops.c + create mode 100644 qcow2/lib/util/bufferiszero.c + create mode 100644 qcow2/lib/util/compatfd.c + create mode 100644 qcow2/lib/util/coroutine-ucontext.c + create mode 100644 qcow2/lib/util/cutils.c + create mode 100644 qcow2/lib/util/defer-call.c + create mode 100644 qcow2/lib/util/error-report.c + create mode 100644 qcow2/lib/util/error.c + create mode 100644 qcow2/lib/util/event_notifier-posix.c + create mode 100644 qcow2/lib/util/fdmon-epoll.c + create mode 100644 qcow2/lib/util/fdmon-poll.c + create mode 100644 qcow2/lib/util/hbitmap.c + create mode 100644 qcow2/lib/util/host-utils.c + create mode 100644 qcow2/lib/util/id.c + create mode 100644 qcow2/lib/util/iov.c + create mode 100644 qcow2/lib/util/lockcnt.c + create mode 100644 qcow2/lib/util/main-loop.c + create mode 100644 qcow2/lib/util/memalign.c + create mode 100644 qcow2/lib/util/module.c + create mode 100644 qcow2/lib/util/notify.c + create mode 100644 qcow2/lib/util/osdep.c + create mode 100644 qcow2/lib/util/oslib-posix.c + create mode 100644 qcow2/lib/util/qemu-coroutine-lock.c + create mode 100644 qcow2/lib/util/qemu-coroutine.c + create mode 100644 qcow2/lib/util/qemu-option.c + create mode 100644 qcow2/lib/util/qemu-print.c + create mode 100644 qcow2/lib/util/qemu-sockets.c + create mode 100644 qcow2/lib/util/qemu-thread-common.h + create mode 100644 qcow2/lib/util/qemu-thread-posix.c + create mode 100644 qcow2/lib/util/qemu-timer-common.c + create mode 100644 qcow2/lib/util/qemu-timer.c + create mode 100644 qcow2/lib/util/qsp.c + create mode 100644 qcow2/lib/util/range.c + create mode 100644 qcow2/lib/util/rcu.c + create mode 100644 qcow2/lib/util/stats64.c + create mode 100644 qcow2/lib/util/thread-pool.c + create mode 100644 qcow2/lib/util/timed-average.c + create mode 100644 qcow2/lib/util/transactions.c + create mode 100644 qcow2/lib/util/unicode.c + create mode 100644 qcow2/lib/util/uri.c + create mode 100644 qcow2/lib/util/yank.c + create mode 100644 qcow2/qemu-config.c + create mode 100644 qcow2/qemu-img-cmds.h + create mode 100644 qcow2/qemu-img.c + create mode 100644 qcow2/qemu-progress.c + +diff --git a/include/authz/base.h b/include/authz/base.h +new file mode 100644 +index 00000000..b53e4e45 +--- /dev/null ++++ b/include/authz/base.h +@@ -0,0 +1,101 @@ ++/* ++ * QEMU authorization framework base class ++ * ++ * Copyright (c) 2018 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QAUTHZ_BASE_H ++#define QAUTHZ_BASE_H ++ ++#include "qapi/error.h" ++#include "qom/object.h" ++ ++ ++#define TYPE_QAUTHZ "authz" ++ ++OBJECT_DECLARE_TYPE(QAuthZ, QAuthZClass, ++ QAUTHZ) ++ ++ ++/** ++ * QAuthZ: ++ * ++ * The QAuthZ class defines an API contract to be used ++ * for providing an authorization driver for services ++ * with user identities. ++ */ ++ ++struct QAuthZ { ++ Object parent_obj; ++}; ++ ++ ++struct QAuthZClass { ++ ObjectClass parent_class; ++ ++ bool (*is_allowed)(QAuthZ *authz, ++ const char *identity, ++ Error **errp); ++}; ++ ++ ++/** ++ * qauthz_is_allowed: ++ * @authz: the authorization object ++ * @identity: the user identity to authorize ++ * @errp: pointer to a NULL initialized error object ++ * ++ * Check if a user @identity is authorized. If an error ++ * occurs this method will return false to indicate ++ * denial, as well as setting @errp to contain the details. ++ * Callers are recommended to treat the denial and error ++ * scenarios identically. Specifically the error info in ++ * @errp should never be fed back to the user being ++ * authorized, it is merely for benefit of administrator ++ * debugging. ++ * ++ * Returns: true if @identity is authorized, false if denied or if ++ * an error occurred. ++ */ ++bool qauthz_is_allowed(QAuthZ *authz, ++ const char *identity, ++ Error **errp); ++ ++ ++/** ++ * qauthz_is_allowed_by_id: ++ * @authzid: ID of the authorization object ++ * @identity: the user identity to authorize ++ * @errp: pointer to a NULL initialized error object ++ * ++ * Check if a user @identity is authorized. If an error ++ * occurs this method will return false to indicate ++ * denial, as well as setting @errp to contain the details. ++ * Callers are recommended to treat the denial and error ++ * scenarios identically. Specifically the error info in ++ * @errp should never be fed back to the user being ++ * authorized, it is merely for benefit of administrator ++ * debugging. ++ * ++ * Returns: true if @identity is authorized, false if denied or if ++ * an error occurred. ++ */ ++bool qauthz_is_allowed_by_id(const char *authzid, ++ const char *identity, ++ Error **errp); ++ ++#endif /* QAUTHZ_BASE_H */ +diff --git a/include/block/accounting.h b/include/block/accounting.h +new file mode 100644 +index 00000000..a59e39f4 +--- /dev/null ++++ b/include/block/accounting.h +@@ -0,0 +1,124 @@ ++/* ++ * QEMU System Emulator block accounting ++ * ++ * Copyright (c) 2011 Christoph Hellwig ++ * Copyright (c) 2015 Igalia, S.L. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_ACCOUNTING_H ++#define BLOCK_ACCOUNTING_H ++ ++#include "qemu/timed-average.h" ++#include "qemu/thread.h" ++#include "qapi/qapi-types-common.h" ++ ++typedef struct BlockAcctTimedStats BlockAcctTimedStats; ++typedef struct BlockAcctStats BlockAcctStats; ++ ++enum BlockAcctType { ++ BLOCK_ACCT_NONE = 0, ++ BLOCK_ACCT_READ, ++ BLOCK_ACCT_WRITE, ++ BLOCK_ACCT_FLUSH, ++ BLOCK_ACCT_ZONE_APPEND, ++ BLOCK_ACCT_UNMAP, ++ BLOCK_MAX_IOTYPE, ++}; ++ ++struct BlockAcctTimedStats { ++ BlockAcctStats *stats; ++ TimedAverage latency[BLOCK_MAX_IOTYPE]; ++ unsigned interval_length; /* in seconds */ ++ QSLIST_ENTRY(BlockAcctTimedStats) entries; ++}; ++ ++typedef struct BlockLatencyHistogram { ++ /* The following histogram is represented like this: ++ * ++ * 5| * ++ * 4| * ++ * 3| * * ++ * 2| * * * ++ * 1| * * * * ++ * +------------------ ++ * 10 50 100 ++ * ++ * BlockLatencyHistogram histogram = { ++ * .nbins = 4, ++ * .boundaries = {10, 50, 100}, ++ * .bins = {3, 1, 5, 2}, ++ * }; ++ * ++ * @boundaries array define histogram intervals as follows: ++ * [0, boundaries[0]), [boundaries[0], boundaries[1]), ... ++ * [boundaries[nbins-2], +inf) ++ * ++ * So, for example above, histogram intervals are: ++ * [0, 10), [10, 50), [50, 100), [100, +inf) ++ */ ++ int nbins; ++ uint64_t *boundaries; /* @nbins-1 numbers here ++ (all boundaries, except 0 and +inf) */ ++ uint64_t *bins; ++} BlockLatencyHistogram; ++ ++struct BlockAcctStats { ++ QemuMutex lock; ++ uint64_t nr_bytes[BLOCK_MAX_IOTYPE]; ++ uint64_t nr_ops[BLOCK_MAX_IOTYPE]; ++ uint64_t invalid_ops[BLOCK_MAX_IOTYPE]; ++ uint64_t failed_ops[BLOCK_MAX_IOTYPE]; ++ uint64_t total_time_ns[BLOCK_MAX_IOTYPE]; ++ uint64_t merged[BLOCK_MAX_IOTYPE]; ++ int64_t last_access_time_ns; ++ QSLIST_HEAD(, BlockAcctTimedStats) intervals; ++ bool account_invalid; ++ bool account_failed; ++ BlockLatencyHistogram latency_histogram[BLOCK_MAX_IOTYPE]; ++}; ++ ++typedef struct BlockAcctCookie { ++ int64_t bytes; ++ int64_t start_time_ns; ++ enum BlockAcctType type; ++} BlockAcctCookie; ++ ++void block_acct_init(BlockAcctStats *stats); ++void block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid, ++ enum OnOffAuto account_failed); ++void block_acct_cleanup(BlockAcctStats *stats); ++void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length); ++BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats, ++ BlockAcctTimedStats *s); ++void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, ++ int64_t bytes, enum BlockAcctType type); ++void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie); ++void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie); ++void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type); ++void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, ++ int num_requests); ++int64_t block_acct_idle_time_ns(BlockAcctStats *stats); ++double block_acct_queue_depth(BlockAcctTimedStats *stats, ++ enum BlockAcctType type); ++int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, ++ uint64List *boundaries); ++void block_latency_histograms_clear(BlockAcctStats *stats); ++ ++#endif +diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h +new file mode 100644 +index 00000000..cf5e8bde +--- /dev/null ++++ b/include/block/aio-wait.h +@@ -0,0 +1,152 @@ ++/* ++ * AioContext wait support ++ * ++ * Copyright (C) 2018 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_AIO_WAIT_H ++#define QEMU_AIO_WAIT_H ++ ++#include "block/aio.h" ++#include "qemu/main-loop.h" ++ ++/** ++ * AioWait: ++ * ++ * An object that facilitates synchronous waiting on a condition. A single ++ * global AioWait object (global_aio_wait) is used internally. ++ * ++ * The main loop can wait on an operation running in an IOThread as follows: ++ * ++ * AioContext *ctx = ...; ++ * MyWork work = { .done = false }; ++ * schedule_my_work_in_iothread(ctx, &work); ++ * AIO_WAIT_WHILE(ctx, !work.done); ++ * ++ * The IOThread must call aio_wait_kick() to notify the main loop when ++ * work.done changes: ++ * ++ * static void do_work(...) ++ * { ++ * ... ++ * work.done = true; ++ * aio_wait_kick(); ++ * } ++ */ ++typedef struct { ++ /* Number of waiting AIO_WAIT_WHILE() callers. Accessed with atomic ops. */ ++ unsigned num_waiters; ++} AioWait; ++ ++extern AioWait global_aio_wait; ++ ++/** ++ * AIO_WAIT_WHILE_INTERNAL: ++ * @ctx: the aio context, or NULL if multiple aio contexts (for which the ++ * caller does not hold a lock) are involved in the polling condition. ++ * @cond: wait while this conditional expression is true ++ * ++ * Wait while a condition is true. Use this to implement synchronous ++ * operations that require event loop activity. ++ * ++ * The caller must be sure that something calls aio_wait_kick() when the value ++ * of @cond might have changed. ++ * ++ * The caller's thread must be the IOThread that owns @ctx or the main loop ++ * thread (with @ctx acquired exactly once). This function cannot be used to ++ * wait on conditions between two IOThreads since that could lead to deadlock, ++ * go via the main loop instead. ++ */ ++#define AIO_WAIT_WHILE_INTERNAL(ctx, cond) ({ \ ++ bool waited_ = false; \ ++ AioWait *wait_ = &global_aio_wait; \ ++ AioContext *ctx_ = (ctx); \ ++ /* Increment wait_->num_waiters before evaluating cond. */ \ ++ qatomic_inc(&wait_->num_waiters); \ ++ /* Paired with smp_mb in aio_wait_kick(). */ \ ++ smp_mb__after_rmw(); \ ++ if (ctx_ && in_aio_context_home_thread(ctx_)) { \ ++ while ((cond)) { \ ++ aio_poll(ctx_, true); \ ++ waited_ = true; \ ++ } \ ++ } else { \ ++ assert(qemu_get_current_aio_context() == \ ++ qemu_get_aio_context()); \ ++ while ((cond)) { \ ++ aio_poll(qemu_get_aio_context(), true); \ ++ waited_ = true; \ ++ } \ ++ } \ ++ qatomic_dec(&wait_->num_waiters); \ ++ waited_; }) ++ ++#define AIO_WAIT_WHILE(ctx, cond) \ ++ AIO_WAIT_WHILE_INTERNAL(ctx, cond) ++ ++/* TODO replace this with AIO_WAIT_WHILE() in a future patch */ ++#define AIO_WAIT_WHILE_UNLOCKED(ctx, cond) \ ++ AIO_WAIT_WHILE_INTERNAL(ctx, cond) ++ ++/** ++ * aio_wait_kick: ++ * Wake up the main thread if it is waiting on AIO_WAIT_WHILE(). During ++ * synchronous operations performed in an IOThread, the main thread lets the ++ * IOThread's event loop run, waiting for the operation to complete. A ++ * aio_wait_kick() call will wake up the main thread. ++ */ ++void aio_wait_kick(void); ++ ++/** ++ * aio_wait_bh_oneshot: ++ * @ctx: the aio context ++ * @cb: the BH callback function ++ * @opaque: user data for the BH callback function ++ * ++ * Run a BH in @ctx and wait for it to complete. ++ * ++ * Must be called from the main loop thread without @ctx acquired. ++ * Note that main loop event processing may occur. ++ */ ++void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque); ++ ++/** ++ * in_aio_context_home_thread: ++ * @ctx: the aio context ++ * ++ * Return whether we are running in the thread that normally runs @ctx. Note ++ * that acquiring/releasing ctx does not affect the outcome, each AioContext ++ * still only has one home thread that is responsible for running it. ++ */ ++static inline bool in_aio_context_home_thread(AioContext *ctx) ++{ ++ if (ctx == qemu_get_current_aio_context()) { ++ return true; ++ } ++ ++ if (ctx == qemu_get_aio_context()) { ++ return bql_locked(); ++ } else { ++ return false; ++ } ++} ++ ++#endif /* QEMU_AIO_WAIT_H */ +diff --git a/include/block/aio.h b/include/block/aio.h +new file mode 100644 +index 00000000..4ee81936 +--- /dev/null ++++ b/include/block/aio.h +@@ -0,0 +1,723 @@ ++/* ++ * QEMU aio implementation ++ * ++ * Copyright IBM, Corp. 2008 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_AIO_H ++#define QEMU_AIO_H ++ ++#ifdef CONFIG_LINUX_IO_URING ++#include ++#endif ++#include "qemu/coroutine-core.h" ++#include "qemu/queue.h" ++#include "qemu/event_notifier.h" ++#include "qemu/thread.h" ++#include "qemu/timer.h" ++#include "block/graph-lock.h" ++#include "hw/qdev-core.h" ++ ++ ++typedef struct BlockAIOCB BlockAIOCB; ++typedef void BlockCompletionFunc(void *opaque, int ret); ++ ++typedef struct AIOCBInfo { ++ void (*cancel_async)(BlockAIOCB *acb); ++ size_t aiocb_size; ++} AIOCBInfo; ++ ++struct BlockAIOCB { ++ const AIOCBInfo *aiocb_info; ++ BlockDriverState *bs; ++ BlockCompletionFunc *cb; ++ void *opaque; ++ int refcnt; ++}; ++ ++void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs, ++ BlockCompletionFunc *cb, void *opaque); ++void qemu_aio_unref(void *p); ++void qemu_aio_ref(void *p); ++ ++typedef struct AioHandler AioHandler; ++typedef QLIST_HEAD(, AioHandler) AioHandlerList; ++typedef void QEMUBHFunc(void *opaque); ++typedef bool AioPollFn(void *opaque); ++typedef void IOHandler(void *opaque); ++ ++struct ThreadPool; ++struct LinuxAioState; ++typedef struct LuringState LuringState; ++ ++/* Is polling disabled? */ ++bool aio_poll_disabled(AioContext *ctx); ++ ++/* Callbacks for file descriptor monitoring implementations */ ++typedef struct { ++ /* ++ * update: ++ * @ctx: the AioContext ++ * @old_node: the existing handler or NULL if this file descriptor is being ++ * monitored for the first time ++ * @new_node: the new handler or NULL if this file descriptor is being ++ * removed ++ * ++ * Add/remove/modify a monitored file descriptor. ++ * ++ * Called with ctx->list_lock acquired. ++ */ ++ void (*update)(AioContext *ctx, AioHandler *old_node, AioHandler *new_node); ++ ++ /* ++ * wait: ++ * @ctx: the AioContext ++ * @ready_list: list for handlers that become ready ++ * @timeout: maximum duration to wait, in nanoseconds ++ * ++ * Wait for file descriptors to become ready and place them on ready_list. ++ * ++ * Called with ctx->list_lock incremented but not locked. ++ * ++ * Returns: number of ready file descriptors. ++ */ ++ int (*wait)(AioContext *ctx, AioHandlerList *ready_list, int64_t timeout); ++ ++ /* ++ * need_wait: ++ * @ctx: the AioContext ++ * ++ * Tell aio_poll() when to stop userspace polling early because ->wait() ++ * has fds ready. ++ * ++ * File descriptor monitoring implementations that cannot poll fd readiness ++ * from userspace should use aio_poll_disabled() here. This ensures that ++ * file descriptors are not starved by handlers that frequently make ++ * progress via userspace polling. ++ * ++ * Returns: true if ->wait() should be called, false otherwise. ++ */ ++ bool (*need_wait)(AioContext *ctx); ++} FDMonOps; ++ ++/* ++ * Each aio_bh_poll() call carves off a slice of the BH list, so that newly ++ * scheduled BHs are not processed until the next aio_bh_poll() call. All ++ * active aio_bh_poll() calls chain their slices together in a list, so that ++ * nested aio_bh_poll() calls process all scheduled bottom halves. ++ */ ++typedef QSLIST_HEAD(, QEMUBH) BHList; ++typedef struct BHListSlice BHListSlice; ++struct BHListSlice { ++ BHList bh_list; ++ QSIMPLEQ_ENTRY(BHListSlice) next; ++}; ++ ++typedef QSLIST_HEAD(, AioHandler) AioHandlerSList; ++ ++struct AioContext { ++ GSource source; ++ ++ /* Used by AioContext users to protect from multi-threaded access. */ ++ QemuRecMutex lock; ++ ++ /* ++ * Keep track of readers and writers of the block layer graph. ++ * This is essential to avoid performing additions and removal ++ * of nodes and edges from block graph while some ++ * other thread is traversing it. ++ */ ++ BdrvGraphRWlock *bdrv_graph; ++ ++ /* The list of registered AIO handlers. Protected by ctx->list_lock. */ ++ AioHandlerList aio_handlers; ++ ++ /* The list of AIO handlers to be deleted. Protected by ctx->list_lock. */ ++ AioHandlerList deleted_aio_handlers; ++ ++ /* Used to avoid unnecessary event_notifier_set calls in aio_notify; ++ * only written from the AioContext home thread, or under the BQL in ++ * the case of the main AioContext. However, it is read from any ++ * thread so it is still accessed with atomic primitives. ++ * ++ * If this field is 0, everything (file descriptors, bottom halves, ++ * timers) will be re-evaluated before the next blocking poll() or ++ * io_uring wait; therefore, the event_notifier_set call can be ++ * skipped. If it is non-zero, you may need to wake up a concurrent ++ * aio_poll or the glib main event loop, making event_notifier_set ++ * necessary. ++ * ++ * Bit 0 is reserved for GSource usage of the AioContext, and is 1 ++ * between a call to aio_ctx_prepare and the next call to aio_ctx_check. ++ * Bits 1-31 simply count the number of active calls to aio_poll ++ * that are in the prepare or poll phase. ++ * ++ * The GSource and aio_poll must use a different mechanism because ++ * there is no certainty that a call to GSource's prepare callback ++ * (via g_main_context_prepare) is indeed followed by check and ++ * dispatch. It's not clear whether this would be a bug, but let's ++ * play safe and allow it---it will just cause extra calls to ++ * event_notifier_set until the next call to dispatch. ++ * ++ * Instead, the aio_poll calls include both the prepare and the ++ * dispatch phase, hence a simple counter is enough for them. ++ */ ++ uint32_t notify_me; ++ ++ /* A lock to protect between QEMUBH and AioHandler adders and deleter, ++ * and to ensure that no callbacks are removed while we're walking and ++ * dispatching them. ++ */ ++ QemuLockCnt list_lock; ++ ++ /* Bottom Halves pending aio_bh_poll() processing */ ++ BHList bh_list; ++ ++ /* Chained BH list slices for each nested aio_bh_poll() call */ ++ QSIMPLEQ_HEAD(, BHListSlice) bh_slice_list; ++ ++ /* Used by aio_notify. ++ * ++ * "notified" is used to avoid expensive event_notifier_test_and_clear ++ * calls. When it is clear, the EventNotifier is clear, or one thread ++ * is going to clear "notified" before processing more events. False ++ * positives are possible, i.e. "notified" could be set even though the ++ * EventNotifier is clear. ++ * ++ * Note that event_notifier_set *cannot* be optimized the same way. For ++ * more information on the problem that would result, see "#ifdef BUG2" ++ * in the docs/aio_notify_accept.promela formal model. ++ */ ++ bool notified; ++ EventNotifier notifier; ++ ++ QSLIST_HEAD(, Coroutine) scheduled_coroutines; ++ QEMUBH *co_schedule_bh; ++ ++ int thread_pool_min; ++ int thread_pool_max; ++ /* Thread pool for performing work and receiving completion callbacks. ++ * Has its own locking. ++ */ ++ struct ThreadPool *thread_pool; ++ ++#ifdef CONFIG_LINUX_AIO ++ struct LinuxAioState *linux_aio; ++#endif ++#ifdef CONFIG_LINUX_IO_URING ++ LuringState *linux_io_uring; ++ ++ /* State for file descriptor monitoring using Linux io_uring */ ++ struct io_uring fdmon_io_uring; ++ AioHandlerSList submit_list; ++#endif ++ ++ /* TimerLists for calling timers - one per clock type. Has its own ++ * locking. ++ */ ++ QEMUTimerListGroup tlg; ++ ++ /* Number of AioHandlers without .io_poll() */ ++ int poll_disable_cnt; ++ ++ /* Polling mode parameters */ ++ int64_t poll_ns; /* current polling time in nanoseconds */ ++ int64_t poll_max_ns; /* maximum polling time in nanoseconds */ ++ int64_t poll_grow; /* polling time growth factor */ ++ int64_t poll_shrink; /* polling time shrink factor */ ++ ++ /* AIO engine parameters */ ++ int64_t aio_max_batch; /* maximum number of requests in a batch */ ++ ++ /* ++ * List of handlers participating in userspace polling. Protected by ++ * ctx->list_lock. Iterated and modified mostly by the event loop thread ++ * from aio_poll() with ctx->list_lock incremented. aio_set_fd_handler() ++ * only touches the list to delete nodes if ctx->list_lock's count is zero. ++ */ ++ AioHandlerList poll_aio_handlers; ++ ++ /* Are we in polling mode or monitoring file descriptors? */ ++ bool poll_started; ++ ++ /* epoll(7) state used when built with CONFIG_EPOLL */ ++ int epollfd; ++ ++ const FDMonOps *fdmon_ops; ++}; ++ ++/** ++ * aio_context_new: Allocate a new AioContext. ++ * ++ * AioContext provide a mini event-loop that can be waited on synchronously. ++ * They also provide bottom halves, a service to execute a piece of code ++ * as soon as possible. ++ */ ++AioContext *aio_context_new(Error **errp); ++ ++/** ++ * aio_context_ref: ++ * @ctx: The AioContext to operate on. ++ * ++ * Add a reference to an AioContext. ++ */ ++void aio_context_ref(AioContext *ctx); ++ ++/** ++ * aio_context_unref: ++ * @ctx: The AioContext to operate on. ++ * ++ * Drop a reference to an AioContext. ++ */ ++void aio_context_unref(AioContext *ctx); ++ ++/** ++ * aio_bh_schedule_oneshot_full: Allocate a new bottom half structure that will ++ * run only once and as soon as possible. ++ * ++ * @name: A human-readable identifier for debugging purposes. ++ */ ++void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, ++ const char *name); ++ ++/** ++ * aio_bh_schedule_oneshot: Allocate a new bottom half structure that will run ++ * only once and as soon as possible. ++ * ++ * A convenience wrapper for aio_bh_schedule_oneshot_full() that uses cb as the ++ * name string. ++ */ ++#define aio_bh_schedule_oneshot(ctx, cb, opaque) \ ++ aio_bh_schedule_oneshot_full((ctx), (cb), (opaque), (stringify(cb))) ++ ++/** ++ * aio_bh_new_full: Allocate a new bottom half structure. ++ * ++ * Bottom halves are lightweight callbacks whose invocation is guaranteed ++ * to be wait-free, thread-safe and signal-safe. The #QEMUBH structure ++ * is opaque and must be allocated prior to its use. ++ * ++ * @name: A human-readable identifier for debugging purposes. ++ * @reentrancy_guard: A guard set when entering a cb to prevent ++ * device-reentrancy issues ++ */ ++QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, ++ const char *name, MemReentrancyGuard *reentrancy_guard); ++ ++/** ++ * aio_bh_new: Allocate a new bottom half structure ++ * ++ * A convenience wrapper for aio_bh_new_full() that uses the cb as the name ++ * string. ++ */ ++#define aio_bh_new(ctx, cb, opaque) \ ++ aio_bh_new_full((ctx), (cb), (opaque), (stringify(cb)), NULL) ++ ++/** ++ * aio_bh_new_guarded: Allocate a new bottom half structure with a ++ * reentrancy_guard ++ * ++ * A convenience wrapper for aio_bh_new_full() that uses the cb as the name ++ * string. ++ */ ++#define aio_bh_new_guarded(ctx, cb, opaque, guard) \ ++ aio_bh_new_full((ctx), (cb), (opaque), (stringify(cb)), guard) ++ ++/** ++ * aio_notify: Force processing of pending events. ++ * ++ * Similar to signaling a condition variable, aio_notify forces ++ * aio_poll to exit, so that the next call will re-examine pending events. ++ * The caller of aio_notify will usually call aio_poll again very soon, ++ * or go through another iteration of the GLib main loop. Hence, aio_notify ++ * also has the side effect of recalculating the sets of file descriptors ++ * that the main loop waits for. ++ * ++ * Calling aio_notify is rarely necessary, because for example scheduling ++ * a bottom half calls it already. ++ */ ++void aio_notify(AioContext *ctx); ++ ++/** ++ * aio_notify_accept: Acknowledge receiving an aio_notify. ++ * ++ * aio_notify() uses an EventNotifier in order to wake up a sleeping ++ * aio_poll() or g_main_context_iteration(). Calls to aio_notify() are ++ * usually rare, but the AioContext has to clear the EventNotifier on ++ * every aio_poll() or g_main_context_iteration() in order to avoid ++ * busy waiting. This event_notifier_test_and_clear() cannot be done ++ * using the usual aio_context_set_event_notifier(), because it must ++ * be done before processing all events (file descriptors, bottom halves, ++ * timers). ++ * ++ * aio_notify_accept() is an optimized event_notifier_test_and_clear() ++ * that is specific to an AioContext's notifier; it is used internally ++ * to clear the EventNotifier only if aio_notify() had been called. ++ */ ++void aio_notify_accept(AioContext *ctx); ++ ++/** ++ * aio_bh_call: Executes callback function of the specified BH. ++ */ ++void aio_bh_call(QEMUBH *bh); ++ ++/** ++ * aio_bh_poll: Poll bottom halves for an AioContext. ++ * ++ * These are internal functions used by the QEMU main loop. ++ * And notice that multiple occurrences of aio_bh_poll cannot ++ * be called concurrently ++ */ ++int aio_bh_poll(AioContext *ctx); ++ ++/** ++ * qemu_bh_schedule: Schedule a bottom half. ++ * ++ * Scheduling a bottom half interrupts the main loop and causes the ++ * execution of the callback that was passed to qemu_bh_new. ++ * ++ * Bottom halves that are scheduled from a bottom half handler are instantly ++ * invoked. This can create an infinite loop if a bottom half handler ++ * schedules itself. ++ * ++ * @bh: The bottom half to be scheduled. ++ */ ++void qemu_bh_schedule(QEMUBH *bh); ++ ++/** ++ * qemu_bh_cancel: Cancel execution of a bottom half. ++ * ++ * Canceling execution of a bottom half undoes the effect of calls to ++ * qemu_bh_schedule without freeing its resources yet. While cancellation ++ * itself is also wait-free and thread-safe, it can of course race with the ++ * loop that executes bottom halves unless you are holding the iothread ++ * mutex. This makes it mostly useless if you are not holding the mutex. ++ * ++ * @bh: The bottom half to be canceled. ++ */ ++void qemu_bh_cancel(QEMUBH *bh); ++ ++/** ++ *qemu_bh_delete: Cancel execution of a bottom half and free its resources. ++ * ++ * Deleting a bottom half frees the memory that was allocated for it by ++ * qemu_bh_new. It also implies canceling the bottom half if it was ++ * scheduled. ++ * This func is async. The bottom half will do the delete action at the finial ++ * end. ++ * ++ * @bh: The bottom half to be deleted. ++ */ ++void qemu_bh_delete(QEMUBH *bh); ++ ++/* Return whether there are any pending callbacks from the GSource ++ * attached to the AioContext, before g_poll is invoked. ++ * ++ * This is used internally in the implementation of the GSource. ++ */ ++bool aio_prepare(AioContext *ctx); ++ ++/* Return whether there are any pending callbacks from the GSource ++ * attached to the AioContext, after g_poll is invoked. ++ * ++ * This is used internally in the implementation of the GSource. ++ */ ++bool aio_pending(AioContext *ctx); ++ ++/* Dispatch any pending callbacks from the GSource attached to the AioContext. ++ * ++ * This is used internally in the implementation of the GSource. ++ */ ++void aio_dispatch(AioContext *ctx); ++ ++/* Progress in completing AIO work to occur. This can issue new pending ++ * aio as a result of executing I/O completion or bh callbacks. ++ * ++ * Return whether any progress was made by executing AIO or bottom half ++ * handlers. If @blocking == true, this should always be true except ++ * if someone called aio_notify. ++ * ++ * If there are no pending bottom halves, but there are pending AIO ++ * operations, it may not be possible to make any progress without ++ * blocking. If @blocking is true, this function will wait until one ++ * or more AIO events have completed, to ensure something has moved ++ * before returning. ++ */ ++bool no_coroutine_fn aio_poll(AioContext *ctx, bool blocking); ++ ++/* Register a file descriptor and associated callbacks. Behaves very similarly ++ * to qemu_set_fd_handler. Unlike qemu_set_fd_handler, these callbacks will ++ * be invoked when using aio_poll(). ++ * ++ * Code that invokes AIO completion functions should rely on this function ++ * instead of qemu_set_fd_handler[2]. ++ */ ++void aio_set_fd_handler(AioContext *ctx, ++ int fd, ++ IOHandler *io_read, ++ IOHandler *io_write, ++ AioPollFn *io_poll, ++ IOHandler *io_poll_ready, ++ void *opaque); ++ ++/* Register an event notifier and associated callbacks. Behaves very similarly ++ * to event_notifier_set_handler. Unlike event_notifier_set_handler, these callbacks ++ * will be invoked when using aio_poll(). ++ * ++ * Code that invokes AIO completion functions should rely on this function ++ * instead of event_notifier_set_handler. ++ */ ++void aio_set_event_notifier(AioContext *ctx, ++ EventNotifier *notifier, ++ EventNotifierHandler *io_read, ++ AioPollFn *io_poll, ++ EventNotifierHandler *io_poll_ready); ++ ++/* ++ * Set polling begin/end callbacks for an event notifier that has already been ++ * registered with aio_set_event_notifier. Do nothing if the event notifier is ++ * not registered. ++ * ++ * Note that if the io_poll_end() callback (or the entire notifier) is removed ++ * during polling, it will not be called, so an io_poll_begin() is not ++ * necessarily always followed by an io_poll_end(). ++ */ ++void aio_set_event_notifier_poll(AioContext *ctx, ++ EventNotifier *notifier, ++ EventNotifierHandler *io_poll_begin, ++ EventNotifierHandler *io_poll_end); ++ ++/* Return a GSource that lets the main loop poll the file descriptors attached ++ * to this AioContext. ++ */ ++GSource *aio_get_g_source(AioContext *ctx); ++ ++/* Return the ThreadPool bound to this AioContext */ ++struct ThreadPool *aio_get_thread_pool(AioContext *ctx); ++ ++/* Setup the LinuxAioState bound to this AioContext */ ++struct LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp); ++ ++/* Return the LinuxAioState bound to this AioContext */ ++struct LinuxAioState *aio_get_linux_aio(AioContext *ctx); ++ ++/* Setup the LuringState bound to this AioContext */ ++LuringState *aio_setup_linux_io_uring(AioContext *ctx, Error **errp); ++ ++/* Return the LuringState bound to this AioContext */ ++LuringState *aio_get_linux_io_uring(AioContext *ctx); ++/** ++ * aio_timer_new_with_attrs: ++ * @ctx: the aio context ++ * @type: the clock type ++ * @scale: the scale ++ * @attributes: 0, or one to multiple OR'ed QEMU_TIMER_ATTR_ values ++ * to assign ++ * @cb: the callback to call on timer expiry ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Allocate a new timer (with attributes) attached to the context @ctx. ++ * The function is responsible for memory allocation. ++ * ++ * The preferred interface is aio_timer_init or aio_timer_init_with_attrs. ++ * Use that unless you really need dynamic memory allocation. ++ * ++ * Returns: a pointer to the new timer ++ */ ++static inline QEMUTimer *aio_timer_new_with_attrs(AioContext *ctx, ++ QEMUClockType type, ++ int scale, int attributes, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ return timer_new_full(&ctx->tlg, type, scale, attributes, cb, opaque); ++} ++ ++/** ++ * aio_timer_new: ++ * @ctx: the aio context ++ * @type: the clock type ++ * @scale: the scale ++ * @cb: the callback to call on timer expiry ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Allocate a new timer attached to the context @ctx. ++ * See aio_timer_new_with_attrs for details. ++ * ++ * Returns: a pointer to the new timer ++ */ ++static inline QEMUTimer *aio_timer_new(AioContext *ctx, QEMUClockType type, ++ int scale, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ return timer_new_full(&ctx->tlg, type, scale, 0, cb, opaque); ++} ++ ++/** ++ * aio_timer_init_with_attrs: ++ * @ctx: the aio context ++ * @ts: the timer ++ * @type: the clock type ++ * @scale: the scale ++ * @attributes: 0, or one to multiple OR'ed QEMU_TIMER_ATTR_ values ++ * to assign ++ * @cb: the callback to call on timer expiry ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Initialise a new timer (with attributes) attached to the context @ctx. ++ * The caller is responsible for memory allocation. ++ */ ++static inline void aio_timer_init_with_attrs(AioContext *ctx, ++ QEMUTimer *ts, QEMUClockType type, ++ int scale, int attributes, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ timer_init_full(ts, &ctx->tlg, type, scale, attributes, cb, opaque); ++} ++ ++/** ++ * aio_timer_init: ++ * @ctx: the aio context ++ * @ts: the timer ++ * @type: the clock type ++ * @scale: the scale ++ * @cb: the callback to call on timer expiry ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Initialise a new timer attached to the context @ctx. ++ * See aio_timer_init_with_attrs for details. ++ */ ++static inline void aio_timer_init(AioContext *ctx, ++ QEMUTimer *ts, QEMUClockType type, ++ int scale, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ timer_init_full(ts, &ctx->tlg, type, scale, 0, cb, opaque); ++} ++ ++/** ++ * aio_compute_timeout: ++ * @ctx: the aio context ++ * ++ * Compute the timeout that a blocking aio_poll should use. ++ */ ++int64_t aio_compute_timeout(AioContext *ctx); ++ ++/** ++ * aio_co_schedule: ++ * @ctx: the aio context ++ * @co: the coroutine ++ * ++ * Start a coroutine on a remote AioContext. ++ * ++ * The coroutine must not be entered by anyone else while aio_co_schedule() ++ * is active. In addition the coroutine must have yielded unless ctx ++ * is the context in which the coroutine is running (i.e. the value of ++ * qemu_get_current_aio_context() from the coroutine itself). ++ */ ++void aio_co_schedule(AioContext *ctx, Coroutine *co); ++ ++/** ++ * aio_co_reschedule_self: ++ * @new_ctx: the new context ++ * ++ * Move the currently running coroutine to new_ctx. If the coroutine is already ++ * running in new_ctx, do nothing. ++ * ++ * Note that this function cannot reschedule from iohandler_ctx to ++ * qemu_aio_context. ++ */ ++void coroutine_fn aio_co_reschedule_self(AioContext *new_ctx); ++ ++/** ++ * aio_co_wake: ++ * @co: the coroutine ++ * ++ * Restart a coroutine on the AioContext where it was running last, thus ++ * preventing coroutines from jumping from one context to another when they ++ * go to sleep. ++ * ++ * aio_co_wake may be executed either in coroutine or non-coroutine ++ * context. The coroutine must not be entered by anyone else while ++ * aio_co_wake() is active. ++ */ ++void aio_co_wake(Coroutine *co); ++ ++/** ++ * aio_co_enter: ++ * @ctx: the context to run the coroutine ++ * @co: the coroutine to run ++ * ++ * Enter a coroutine in the specified AioContext. ++ */ ++void aio_co_enter(AioContext *ctx, Coroutine *co); ++ ++/** ++ * Return the AioContext whose event loop runs in the current thread. ++ * ++ * If called from an IOThread this will be the IOThread's AioContext. If ++ * called from the main thread or with the "big QEMU lock" taken it ++ * will be the main loop AioContext. ++ * ++ * Note that the return value is never the main loop's iohandler_ctx and the ++ * return value is the main loop AioContext instead. ++ */ ++AioContext *qemu_get_current_aio_context(void); ++ ++void qemu_set_current_aio_context(AioContext *ctx); ++ ++/** ++ * aio_context_setup: ++ * @ctx: the aio context ++ * ++ * Initialize the aio context. ++ */ ++void aio_context_setup(AioContext *ctx); ++ ++/** ++ * aio_context_destroy: ++ * @ctx: the aio context ++ * ++ * Destroy the aio context. ++ */ ++void aio_context_destroy(AioContext *ctx); ++ ++/* Used internally, do not call outside AioContext code */ ++void aio_context_use_g_source(AioContext *ctx); ++ ++/** ++ * aio_context_set_poll_params: ++ * @ctx: the aio context ++ * @max_ns: how long to busy poll for, in nanoseconds ++ * @grow: polling time growth factor ++ * @shrink: polling time shrink factor ++ * ++ * Poll mode can be disabled by setting poll_max_ns to 0. ++ */ ++void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, ++ int64_t grow, int64_t shrink, ++ Error **errp); ++ ++/** ++ * aio_context_set_aio_params: ++ * @ctx: the aio context ++ * @max_batch: maximum number of requests in a batch, 0 means that the ++ * engine will use its default ++ */ ++void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch); ++ ++/** ++ * aio_context_set_thread_pool_params: ++ * @ctx: the aio context ++ * @min: min number of threads to have readily available in the thread pool ++ * @min: max number of threads the thread pool can contain ++ */ ++void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min, ++ int64_t max, Error **errp); ++#endif +diff --git a/include/block/aio_task.h b/include/block/aio_task.h +new file mode 100644 +index 00000000..18a9c41f +--- /dev/null ++++ b/include/block/aio_task.h +@@ -0,0 +1,52 @@ ++/* ++ * Aio tasks loops ++ * ++ * Copyright (c) 2019 Virtuozzo International GmbH. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCK_AIO_TASK_H ++#define BLOCK_AIO_TASK_H ++ ++typedef struct AioTaskPool AioTaskPool; ++typedef struct AioTask AioTask; ++typedef int coroutine_fn (*AioTaskFunc)(AioTask *task); ++struct AioTask { ++ AioTaskPool *pool; ++ AioTaskFunc func; ++ int ret; ++}; ++ ++AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks); ++void aio_task_pool_free(AioTaskPool *); ++ ++/* error code of failed task or 0 if all is OK */ ++int aio_task_pool_status(AioTaskPool *pool); ++ ++bool aio_task_pool_empty(AioTaskPool *pool); ++ ++/* User provides filled @task, however task->pool will be set automatically */ ++void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task); ++ ++void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool); ++void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool); ++void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool); ++ ++#endif /* BLOCK_AIO_TASK_H */ +diff --git a/include/block/block-common.h b/include/block/block-common.h +new file mode 100644 +index 00000000..338fe5ff +--- /dev/null ++++ b/include/block/block-common.h +@@ -0,0 +1,567 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_COMMON_H ++#define BLOCK_COMMON_H ++ ++#include "qapi/qapi-types-block-core.h" ++#include "qemu/queue.h" ++ ++/* ++ * co_wrapper{*}: Function specifiers used by block-coroutine-wrapper.py ++ * ++ * Function specifiers, which do nothing but mark functions to be ++ * generated by scripts/block-coroutine-wrapper.py ++ * ++ * Usage: read docs/devel/block-coroutine-wrapper.rst ++ * ++ * There are 4 kind of specifiers: ++ * - co_wrapper functions can be called by only non-coroutine context, because ++ * they always generate a new coroutine. ++ * - co_wrapper_mixed functions can be called by both coroutine and ++ * non-coroutine context. ++ * - co_wrapper_bdrv_rdlock are co_wrapper functions but automatically take and ++ * release the graph rdlock when creating a new coroutine ++ * - co_wrapper_mixed_bdrv_rdlock are co_wrapper_mixed functions but ++ * automatically take and release the graph rdlock when creating a new ++ * coroutine. ++ * ++ * These functions should not be called from a coroutine_fn; instead, ++ * call the wrapped function directly. ++ */ ++#define co_wrapper no_coroutine_fn ++#define co_wrapper_mixed no_coroutine_fn coroutine_mixed_fn ++#define co_wrapper_bdrv_rdlock no_coroutine_fn ++#define co_wrapper_mixed_bdrv_rdlock no_coroutine_fn coroutine_mixed_fn ++ ++/* ++ * no_co_wrapper: Function specifier used by block-coroutine-wrapper.py ++ * ++ * Function specifier which does nothing but mark functions to be generated by ++ * scripts/block-coroutine-wrapper.py. ++ * ++ * A no_co_wrapper function declaration creates a coroutine_fn wrapper around ++ * functions that must not be called in coroutine context. It achieves this by ++ * scheduling a BH in the bottom half that runs the respective non-coroutine ++ * function. The coroutine yields after scheduling the BH and is reentered when ++ * the wrapped function returns. ++ * ++ * A no_co_wrapper_bdrv_rdlock function is a no_co_wrapper function that ++ * automatically takes the graph rdlock when calling the wrapped function. In ++ * the same way, no_co_wrapper_bdrv_wrlock functions automatically take the ++ * graph wrlock. ++ */ ++#define no_co_wrapper ++#define no_co_wrapper_bdrv_rdlock ++#define no_co_wrapper_bdrv_wrlock ++ ++#include "block/blockjob.h" ++ ++/* block.c */ ++typedef struct BlockDriver BlockDriver; ++typedef struct BdrvChild BdrvChild; ++typedef struct BdrvChildClass BdrvChildClass; ++ ++typedef enum BlockZoneOp { ++ BLK_ZO_OPEN, ++ BLK_ZO_CLOSE, ++ BLK_ZO_FINISH, ++ BLK_ZO_RESET, ++} BlockZoneOp; ++ ++typedef enum BlockZoneModel { ++ BLK_Z_NONE = 0x0, /* Regular block device */ ++ BLK_Z_HM = 0x1, /* Host-managed zoned block device */ ++ BLK_Z_HA = 0x2, /* Host-aware zoned block device */ ++} BlockZoneModel; ++ ++typedef enum BlockZoneState { ++ BLK_ZS_NOT_WP = 0x0, ++ BLK_ZS_EMPTY = 0x1, ++ BLK_ZS_IOPEN = 0x2, ++ BLK_ZS_EOPEN = 0x3, ++ BLK_ZS_CLOSED = 0x4, ++ BLK_ZS_RDONLY = 0xD, ++ BLK_ZS_FULL = 0xE, ++ BLK_ZS_OFFLINE = 0xF, ++} BlockZoneState; ++ ++typedef enum BlockZoneType { ++ BLK_ZT_CONV = 0x1, /* Conventional random writes supported */ ++ BLK_ZT_SWR = 0x2, /* Sequential writes required */ ++ BLK_ZT_SWP = 0x3, /* Sequential writes preferred */ ++} BlockZoneType; ++ ++/* ++ * Zone descriptor data structure. ++ * Provides information on a zone with all position and size values in bytes. ++ */ ++typedef struct BlockZoneDescriptor { ++ uint64_t start; ++ uint64_t length; ++ uint64_t cap; ++ uint64_t wp; ++ BlockZoneType type; ++ BlockZoneState state; ++} BlockZoneDescriptor; ++ ++/* ++ * Track write pointers of a zone in bytes. ++ */ ++typedef struct BlockZoneWps { ++ CoMutex colock; ++ uint64_t wp[]; ++} BlockZoneWps; ++ ++typedef struct BlockDriverInfo { ++ /* in bytes, 0 if irrelevant */ ++ int cluster_size; ++ /* ++ * A fraction of cluster_size, if supported (currently QCOW2 only); if ++ * disabled or unsupported, set equal to cluster_size. ++ */ ++ int subcluster_size; ++ /* offset at which the VM state can be saved (0 if not possible) */ ++ int64_t vm_state_offset; ++ bool is_dirty; ++ /* ++ * True if this block driver only supports compressed writes ++ */ ++ bool needs_compressed_writes; ++} BlockDriverInfo; ++ ++typedef struct BlockFragInfo { ++ uint64_t allocated_clusters; ++ uint64_t total_clusters; ++ uint64_t fragmented_clusters; ++ uint64_t compressed_clusters; ++} BlockFragInfo; ++ ++typedef enum { ++ BDRV_REQ_COPY_ON_READ = 0x1, ++ BDRV_REQ_ZERO_WRITE = 0x2, ++ ++ /* ++ * The BDRV_REQ_MAY_UNMAP flag is used in write_zeroes requests to indicate ++ * that the block driver should unmap (discard) blocks if it is guaranteed ++ * that the result will read back as zeroes. The flag is only passed to the ++ * driver if the block device is opened with BDRV_O_UNMAP. ++ */ ++ BDRV_REQ_MAY_UNMAP = 0x4, ++ ++ /* ++ * An optimization hint when all QEMUIOVector elements are within ++ * previously registered bdrv_register_buf() memory ranges. ++ * ++ * Code that replaces the user's QEMUIOVector elements with bounce buffers ++ * must take care to clear this flag. ++ */ ++ BDRV_REQ_REGISTERED_BUF = 0x8, ++ ++ BDRV_REQ_FUA = 0x10, ++ BDRV_REQ_WRITE_COMPRESSED = 0x20, ++ ++ /* ++ * Signifies that this write request will not change the visible disk ++ * content. ++ */ ++ BDRV_REQ_WRITE_UNCHANGED = 0x40, ++ ++ /* ++ * Forces request serialisation. Use only with write requests. ++ */ ++ BDRV_REQ_SERIALISING = 0x80, ++ ++ /* ++ * Execute the request only if the operation can be offloaded or otherwise ++ * be executed efficiently, but return an error instead of using a slow ++ * fallback. ++ */ ++ BDRV_REQ_NO_FALLBACK = 0x100, ++ ++ /* ++ * BDRV_REQ_PREFETCH makes sense only in the context of copy-on-read ++ * (i.e., together with the BDRV_REQ_COPY_ON_READ flag or when a COR ++ * filter is involved), in which case it signals that the COR operation ++ * need not read the data into memory (qiov) but only ensure they are ++ * copied to the top layer (i.e., that COR operation is done). ++ */ ++ BDRV_REQ_PREFETCH = 0x200, ++ ++ /* ++ * If we need to wait for other requests, just fail immediately. Used ++ * only together with BDRV_REQ_SERIALISING. Used only with requests aligned ++ * to request_alignment (corresponding assertions are in block/io.c). ++ */ ++ BDRV_REQ_NO_WAIT = 0x400, ++ ++ /* Mask of valid flags */ ++ BDRV_REQ_MASK = 0x7ff, ++} BdrvRequestFlags; ++ ++#define BDRV_O_NO_SHARE 0x0001 /* don't share permissions */ ++#define BDRV_O_RDWR 0x0002 ++#define BDRV_O_RESIZE 0x0004 /* request permission for resizing the node */ ++#define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save ++ writes in a snapshot */ ++#define BDRV_O_TEMPORARY 0x0010 /* delete the file after use */ ++#define BDRV_O_NOCACHE 0x0020 /* do not use the host page cache */ ++#define BDRV_O_NATIVE_AIO 0x0080 /* use native AIO instead of the ++ thread pool */ ++#define BDRV_O_NO_BACKING 0x0100 /* don't open the backing file */ ++#define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */ ++#define BDRV_O_COPY_ON_READ 0x0400 /* copy read backing sectors into image */ ++#define BDRV_O_INACTIVE 0x0800 /* consistency hint for migration handoff */ ++#define BDRV_O_CHECK 0x1000 /* open solely for consistency check */ ++#define BDRV_O_ALLOW_RDWR 0x2000 /* allow reopen to change from r/o to r/w */ ++#define BDRV_O_UNMAP 0x4000 /* execute guest UNMAP/TRIM operations */ ++#define BDRV_O_PROTOCOL 0x8000 /* if no block driver is explicitly given: ++ select an appropriate protocol driver, ++ ignoring the format layer */ ++#define BDRV_O_NO_IO 0x10000 /* don't initialize for I/O */ ++#define BDRV_O_AUTO_RDONLY 0x20000 /* degrade to read-only if opening ++ read-write fails */ ++#define BDRV_O_IO_URING 0x40000 /* use io_uring instead of the thread pool */ ++ ++#define BDRV_O_CBW_DISCARD_SOURCE 0x80000 /* for copy-before-write filter */ ++ ++#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH) ++ ++ ++/* Option names of options parsed by the block layer */ ++ ++#define BDRV_OPT_CACHE_WB "cache.writeback" ++#define BDRV_OPT_CACHE_DIRECT "cache.direct" ++#define BDRV_OPT_CACHE_NO_FLUSH "cache.no-flush" ++#define BDRV_OPT_READ_ONLY "read-only" ++#define BDRV_OPT_AUTO_READ_ONLY "auto-read-only" ++#define BDRV_OPT_DISCARD "discard" ++#define BDRV_OPT_FORCE_SHARE "force-share" ++ ++ ++#define BDRV_SECTOR_BITS 9 ++#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS) ++ ++/* ++ * Get the first most significant bit of wp. If it is zero, then ++ * the zone type is SWR. ++ */ ++#define BDRV_ZT_IS_CONV(wp) (wp & (1ULL << 63)) ++ ++#define BDRV_REQUEST_MAX_SECTORS MIN_CONST(SIZE_MAX >> BDRV_SECTOR_BITS, \ ++ INT_MAX >> BDRV_SECTOR_BITS) ++#define BDRV_REQUEST_MAX_BYTES (BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS) ++ ++/* ++ * We want allow aligning requests and disk length up to any 32bit alignment ++ * and don't afraid of overflow. ++ * To achieve it, and in the same time use some pretty number as maximum disk ++ * size, let's define maximum "length" (a limit for any offset/bytes request and ++ * for disk size) to be the greatest power of 2 less than INT64_MAX. ++ */ ++#define BDRV_MAX_ALIGNMENT (1L << 30) ++#define BDRV_MAX_LENGTH (QEMU_ALIGN_DOWN(INT64_MAX, BDRV_MAX_ALIGNMENT)) ++ ++/* ++ * Allocation status flags for bdrv_block_status() and friends. ++ * ++ * Public flags: ++ * BDRV_BLOCK_DATA: allocation for data at offset is tied to this layer ++ * BDRV_BLOCK_ZERO: offset reads as zero ++ * BDRV_BLOCK_OFFSET_VALID: an associated offset exists for accessing raw data ++ * BDRV_BLOCK_ALLOCATED: the content of the block is determined by this ++ * layer rather than any backing, set by block layer ++ * BDRV_BLOCK_EOF: the returned pnum covers through end of file for this ++ * layer, set by block layer ++ * BDRV_BLOCK_COMPRESSED: the underlying data is compressed; only valid for ++ * the formats supporting compression: qcow, qcow2 ++ * ++ * Internal flags: ++ * BDRV_BLOCK_RAW: for use by passthrough drivers, such as raw, to request ++ * that the block layer recompute the answer from the returned ++ * BDS; must be accompanied by just BDRV_BLOCK_OFFSET_VALID. ++ * BDRV_BLOCK_RECURSE: request that the block layer will recursively search for ++ * zeroes in file child of current block node inside ++ * returned region. Only valid together with both ++ * BDRV_BLOCK_DATA and BDRV_BLOCK_OFFSET_VALID. Should not ++ * appear with BDRV_BLOCK_ZERO. ++ * ++ * If BDRV_BLOCK_OFFSET_VALID is set, the map parameter represents the ++ * host offset within the returned BDS that is allocated for the ++ * corresponding raw guest data. However, whether that offset ++ * actually contains data also depends on BDRV_BLOCK_DATA, as follows: ++ * ++ * DATA ZERO OFFSET_VALID ++ * t t t sectors read as zero, returned file is zero at offset ++ * t f t sectors read as valid from file at offset ++ * f t t sectors preallocated, read as zero, returned file not ++ * necessarily zero at offset ++ * f f t sectors preallocated but read from backing_hd, ++ * returned file contains garbage at offset ++ * t t f sectors preallocated, read as zero, unknown offset ++ * t f f sectors read from unknown file or offset ++ * f t f not allocated or unknown offset, read as zero ++ * f f f not allocated or unknown offset, read from backing_hd ++ */ ++#define BDRV_BLOCK_DATA 0x01 ++#define BDRV_BLOCK_ZERO 0x02 ++#define BDRV_BLOCK_OFFSET_VALID 0x04 ++#define BDRV_BLOCK_RAW 0x08 ++#define BDRV_BLOCK_ALLOCATED 0x10 ++#define BDRV_BLOCK_EOF 0x20 ++#define BDRV_BLOCK_RECURSE 0x40 ++#define BDRV_BLOCK_COMPRESSED 0x80 ++ ++typedef QTAILQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; ++ ++typedef struct BDRVReopenState { ++ BlockDriverState *bs; ++ int flags; ++ BlockdevDetectZeroesOptions detect_zeroes; ++ bool backing_missing; ++ BlockDriverState *old_backing_bs; /* keep pointer for permissions update */ ++ BlockDriverState *old_file_bs; /* keep pointer for permissions update */ ++ QDict *options; ++ QDict *explicit_options; ++ void *opaque; ++} BDRVReopenState; ++ ++/* ++ * Block operation types ++ */ ++typedef enum BlockOpType { ++ BLOCK_OP_TYPE_BACKUP_SOURCE, ++ BLOCK_OP_TYPE_BACKUP_TARGET, ++ BLOCK_OP_TYPE_CHANGE, ++ BLOCK_OP_TYPE_COMMIT_SOURCE, ++ BLOCK_OP_TYPE_COMMIT_TARGET, ++ BLOCK_OP_TYPE_DATAPLANE, ++ BLOCK_OP_TYPE_DRIVE_DEL, ++ BLOCK_OP_TYPE_EJECT, ++ BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, ++ BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, ++ BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, ++ BLOCK_OP_TYPE_MIRROR_SOURCE, ++ BLOCK_OP_TYPE_MIRROR_TARGET, ++ BLOCK_OP_TYPE_RESIZE, ++ BLOCK_OP_TYPE_STREAM, ++ BLOCK_OP_TYPE_REPLACE, ++ BLOCK_OP_TYPE_MAX, ++} BlockOpType; ++ ++/* Block node permission constants */ ++enum { ++ /** ++ * A user that has the "permission" of consistent reads is guaranteed that ++ * their view of the contents of the block device is complete and ++ * self-consistent, representing the contents of a disk at a specific ++ * point. ++ * ++ * For most block devices (including their backing files) this is true, but ++ * the property cannot be maintained in a few situations like for ++ * intermediate nodes of a commit block job. ++ */ ++ BLK_PERM_CONSISTENT_READ = 0x01, ++ ++ /** This permission is required to change the visible disk contents. */ ++ BLK_PERM_WRITE = 0x02, ++ ++ /** ++ * This permission (which is weaker than BLK_PERM_WRITE) is both enough and ++ * required for writes to the block node when the caller promises that ++ * the visible disk content doesn't change. ++ * ++ * As the BLK_PERM_WRITE permission is strictly stronger, either is ++ * sufficient to perform an unchanging write. ++ */ ++ BLK_PERM_WRITE_UNCHANGED = 0x04, ++ ++ /** This permission is required to change the size of a block node. */ ++ BLK_PERM_RESIZE = 0x08, ++ ++ /** ++ * There was a now-removed bit BLK_PERM_GRAPH_MOD, with value of 0x10. QEMU ++ * 6.1 and earlier may still lock the corresponding byte in block/file-posix ++ * locking. So, implementing some new permission should be very careful to ++ * not interfere with this old unused thing. ++ */ ++ ++ BLK_PERM_ALL = 0x0f, ++ ++ DEFAULT_PERM_PASSTHROUGH = BLK_PERM_CONSISTENT_READ ++ | BLK_PERM_WRITE ++ | BLK_PERM_WRITE_UNCHANGED ++ | BLK_PERM_RESIZE, ++ ++ DEFAULT_PERM_UNCHANGED = BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH, ++}; ++ ++/* ++ * Flags that parent nodes assign to child nodes to specify what kind of ++ * role(s) they take. ++ * ++ * At least one of DATA, METADATA, FILTERED, or COW must be set for ++ * every child. ++ * ++ * ++ * = Connection with bs->children, bs->file and bs->backing fields = ++ * ++ * 1. Filters ++ * ++ * Filter drivers have drv->is_filter = true. ++ * ++ * Filter node has exactly one FILTERED|PRIMARY child, and may have other ++ * children which must not have these bits (one example is the ++ * copy-before-write filter, which also has its target DATA child). ++ * ++ * Filter nodes never have COW children. ++ * ++ * For most filters, the filtered child is linked in bs->file, bs->backing is ++ * NULL. For some filters (as an exception), it is the other way around; those ++ * drivers will have drv->filtered_child_is_backing set to true (see that ++ * field’s documentation for what drivers this concerns) ++ * ++ * 2. "raw" driver (block/raw-format.c) ++ * ++ * Formally it's not a filter (drv->is_filter = false) ++ * ++ * bs->backing is always NULL ++ * ++ * Only has one child, linked in bs->file. Its role is either FILTERED|PRIMARY ++ * (like filter) or DATA|PRIMARY depending on options. ++ * ++ * 3. Other drivers ++ * ++ * Don't have any FILTERED children. ++ * ++ * May have at most one COW child. In this case it's linked in bs->backing. ++ * Otherwise bs->backing is NULL. COW child is never PRIMARY. ++ * ++ * May have at most one PRIMARY child. In this case it's linked in bs->file. ++ * Otherwise bs->file is NULL. ++ * ++ * May also have some other children that don't have the PRIMARY or COW bit set. ++ */ ++enum BdrvChildRoleBits { ++ /* ++ * This child stores data. ++ * Any node may have an arbitrary number of such children. ++ */ ++ BDRV_CHILD_DATA = (1 << 0), ++ ++ /* ++ * This child stores metadata. ++ * Any node may have an arbitrary number of metadata-storing ++ * children. ++ */ ++ BDRV_CHILD_METADATA = (1 << 1), ++ ++ /* ++ * A child that always presents exactly the same visible data as ++ * the parent, e.g. by virtue of the parent forwarding all reads ++ * and writes. ++ * This flag is mutually exclusive with DATA, METADATA, and COW. ++ * Any node may have at most one filtered child at a time. ++ */ ++ BDRV_CHILD_FILTERED = (1 << 2), ++ ++ /* ++ * Child from which to read all data that isn't allocated in the ++ * parent (i.e., the backing child); such data is copied to the ++ * parent through COW (and optionally COR). ++ * This field is mutually exclusive with DATA, METADATA, and ++ * FILTERED. ++ * Any node may have at most one such backing child at a time. ++ */ ++ BDRV_CHILD_COW = (1 << 3), ++ ++ /* ++ * The primary child. For most drivers, this is the child whose ++ * filename applies best to the parent node. ++ * Any node may have at most one primary child at a time. ++ */ ++ BDRV_CHILD_PRIMARY = (1 << 4), ++ ++ /* Useful combination of flags */ ++ BDRV_CHILD_IMAGE = BDRV_CHILD_DATA ++ | BDRV_CHILD_METADATA ++ | BDRV_CHILD_PRIMARY, ++}; ++ ++/* Mask of BdrvChildRoleBits values */ ++typedef unsigned int BdrvChildRole; ++ ++typedef struct BdrvCheckResult { ++ int corruptions; ++ int leaks; ++ int check_errors; ++ int corruptions_fixed; ++ int leaks_fixed; ++ int64_t image_end_offset; ++ BlockFragInfo bfi; ++} BdrvCheckResult; ++ ++typedef enum { ++ BDRV_FIX_LEAKS = 1, ++ BDRV_FIX_ERRORS = 2, ++} BdrvCheckMode; ++ ++typedef struct BlockSizes { ++ uint32_t phys; ++ uint32_t log; ++} BlockSizes; ++ ++typedef struct HDGeometry { ++ uint32_t heads; ++ uint32_t sectors; ++ uint32_t cylinders; ++} HDGeometry; ++ ++/* ++ * Common functions that are neither I/O nor Global State. ++ * ++ * These functions must never call any function from other categories ++ * (I/O, "I/O or GS", Global State) except this one, but can be invoked by ++ * all of them. ++ */ ++ ++char *bdrv_perm_names(uint64_t perm); ++uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm); ++ ++void bdrv_init_with_whitelist(void); ++bool bdrv_uses_whitelist(void); ++int bdrv_is_whitelisted(BlockDriver *drv, bool read_only); ++ ++int bdrv_parse_aio(const char *mode, int *flags); ++int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough); ++int bdrv_parse_discard_flags(const char *mode, int *flags); ++ ++int path_has_protocol(const char *path); ++int path_is_absolute(const char *path); ++char *path_combine(const char *base_path, const char *filename); ++ ++char *bdrv_get_full_backing_filename_from_filename(const char *backed, ++ const char *backing, ++ Error **errp); ++ ++#endif /* BLOCK_COMMON_H */ +diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h +new file mode 100644 +index 00000000..bd7cecd1 +--- /dev/null ++++ b/include/block/block-global-state.h +@@ -0,0 +1,305 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_GLOBAL_STATE_H ++#define BLOCK_GLOBAL_STATE_H ++ ++#include "block/block-common.h" ++#include "qemu/coroutine.h" ++#include "qemu/transactions.h" ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * If a function modifies the graph, it also uses the graph lock to be sure it ++ * has unique access. The graph lock is needed together with BQL because of the ++ * thread-safe I/O API that concurrently runs and accesses the graph without ++ * the BQL. ++ * ++ * It is important to note that not all of these functions are ++ * necessarily limited to running under the BQL, but they would ++ * require additional auditing and many small thread-safety changes ++ * to move them into the I/O API. Often it's not worth doing that ++ * work since the APIs are only used with the BQL held at the ++ * moment, so they have been placed in the GS API (for now). ++ * ++ * These functions can call any function from this and other categories ++ * (I/O, "I/O or GS", Common), but must be invoked only by other GS APIs. ++ * ++ * All functions in this header must use the macro ++ * GLOBAL_STATE_CODE(); ++ * to catch when they are accidentally called without the BQL. ++ */ ++ ++void bdrv_init(void); ++BlockDriver *bdrv_find_protocol(const char *filename, ++ bool allow_protocol_prefix, ++ Error **errp); ++BlockDriver *bdrv_find_format(const char *format_name); ++ ++int coroutine_fn GRAPH_UNLOCKED ++bdrv_co_create(BlockDriver *drv, const char *filename, QemuOpts *opts, ++ Error **errp); ++ ++int co_wrapper bdrv_create(BlockDriver *drv, const char *filename, ++ QemuOpts *opts, Error **errp); ++ ++int coroutine_fn GRAPH_UNLOCKED ++bdrv_co_create_file(const char *filename, QemuOpts *opts, Error **errp); ++ ++BlockDriverState *bdrv_new(void); ++int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, ++ Error **errp); ++ ++int GRAPH_WRLOCK ++bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp); ++ ++int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, ++ Error **errp); ++BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, ++ int flags, Error **errp); ++int bdrv_drop_filter(BlockDriverState *bs, Error **errp); ++ ++BdrvChild * no_coroutine_fn ++bdrv_open_child(const char *filename, QDict *options, const char *bdref_key, ++ BlockDriverState *parent, const BdrvChildClass *child_class, ++ BdrvChildRole child_role, bool allow_none, Error **errp); ++ ++BdrvChild * coroutine_fn no_co_wrapper ++bdrv_co_open_child(const char *filename, QDict *options, const char *bdref_key, ++ BlockDriverState *parent, const BdrvChildClass *child_class, ++ BdrvChildRole child_role, bool allow_none, Error **errp); ++ ++int bdrv_open_file_child(const char *filename, ++ QDict *options, const char *bdref_key, ++ BlockDriverState *parent, Error **errp); ++ ++BlockDriverState * no_coroutine_fn ++bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp); ++ ++BlockDriverState * coroutine_fn no_co_wrapper ++bdrv_co_open_blockdev_ref(BlockdevRef *ref, Error **errp); ++ ++int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, ++ Error **errp); ++int GRAPH_WRLOCK ++bdrv_set_backing_hd_drained(BlockDriverState *bs, BlockDriverState *backing_hd, ++ Error **errp); ++ ++int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, ++ const char *bdref_key, Error **errp); ++ ++BlockDriverState * no_coroutine_fn ++bdrv_open(const char *filename, const char *reference, QDict *options, ++ int flags, Error **errp); ++ ++BlockDriverState * coroutine_fn no_co_wrapper ++bdrv_co_open(const char *filename, const char *reference, ++ QDict *options, int flags, Error **errp); ++ ++BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, ++ const char *node_name, ++ QDict *options, int flags, ++ Error **errp); ++BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, ++ int flags, Error **errp); ++BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, ++ BlockDriverState *bs, QDict *options, ++ bool keep_old_opts); ++void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue); ++int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); ++int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts, ++ Error **errp); ++int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, ++ Error **errp); ++BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, ++ const char *backing_file); ++void GRAPH_RDLOCK bdrv_refresh_filename(BlockDriverState *bs); ++ ++void GRAPH_RDLOCK ++bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp); ++ ++int bdrv_commit(BlockDriverState *bs); ++int GRAPH_RDLOCK bdrv_make_empty(BdrvChild *c, Error **errp); ++ ++void bdrv_register(BlockDriver *bdrv); ++int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, ++ const char *backing_file_str, ++ bool backing_mask_protocol); ++ ++BlockDriverState * GRAPH_RDLOCK ++bdrv_find_overlay(BlockDriverState *active, BlockDriverState *bs); ++ ++BlockDriverState * GRAPH_RDLOCK bdrv_find_base(BlockDriverState *bs); ++ ++int GRAPH_RDLOCK ++bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, ++ Error **errp); ++void GRAPH_RDLOCK ++bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base); ++ ++/* ++ * The units of offset and total_work_size may be chosen arbitrarily by the ++ * block driver; total_work_size may change during the course of the amendment ++ * operation ++ */ ++typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset, ++ int64_t total_work_size, void *opaque); ++int GRAPH_RDLOCK ++bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ bool force, Error **errp); ++ ++/* check if a named node can be replaced when doing drive-mirror */ ++BlockDriverState * GRAPH_RDLOCK ++check_to_replace_node(BlockDriverState *parent_bs, const char *node_name, ++ Error **errp); ++ ++int no_coroutine_fn GRAPH_RDLOCK ++bdrv_activate(BlockDriverState *bs, Error **errp); ++ ++int coroutine_fn no_co_wrapper_bdrv_rdlock ++bdrv_co_activate(BlockDriverState *bs, Error **errp); ++ ++void bdrv_activate_all(Error **errp); ++int bdrv_inactivate_all(void); ++ ++int bdrv_flush_all(void); ++void bdrv_close_all(void); ++void bdrv_drain_all_begin(void); ++void bdrv_drain_all_begin_nopoll(void); ++void bdrv_drain_all_end(void); ++void bdrv_drain_all(void); ++ ++void bdrv_aio_cancel(BlockAIOCB *acb); ++ ++int bdrv_has_zero_init_1(BlockDriverState *bs); ++int coroutine_mixed_fn GRAPH_RDLOCK bdrv_has_zero_init(BlockDriverState *bs); ++BlockDriverState *bdrv_find_node(const char *node_name); ++BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, Error **errp); ++XDbgBlockGraph * GRAPH_RDLOCK bdrv_get_xdbg_block_graph(Error **errp); ++BlockDriverState *bdrv_lookup_bs(const char *device, ++ const char *node_name, ++ Error **errp); ++bool GRAPH_RDLOCK ++bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); ++ ++BlockDriverState *bdrv_next_node(BlockDriverState *bs); ++BlockDriverState *bdrv_next_all_states(BlockDriverState *bs); ++ ++typedef struct BdrvNextIterator { ++ enum { ++ BDRV_NEXT_BACKEND_ROOTS, ++ BDRV_NEXT_MONITOR_OWNED, ++ } phase; ++ BlockBackend *blk; ++ BlockDriverState *bs; ++} BdrvNextIterator; ++ ++BlockDriverState * GRAPH_RDLOCK bdrv_first(BdrvNextIterator *it); ++BlockDriverState * GRAPH_RDLOCK bdrv_next(BdrvNextIterator *it); ++void bdrv_next_cleanup(BdrvNextIterator *it); ++ ++BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs); ++void bdrv_iterate_format(void (*it)(void *opaque, const char *name), ++ void *opaque, bool read_only); ++ ++char * GRAPH_RDLOCK ++bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp); ++ ++char * GRAPH_RDLOCK bdrv_dirname(BlockDriverState *bs, Error **errp); ++ ++void bdrv_img_create(const char *filename, const char *fmt, ++ const char *base_filename, const char *base_fmt, ++ char *options, uint64_t img_size, int flags, ++ bool quiet, Error **errp); ++ ++void bdrv_ref(BlockDriverState *bs); ++void no_coroutine_fn bdrv_unref(BlockDriverState *bs); ++void coroutine_fn no_co_wrapper bdrv_co_unref(BlockDriverState *bs); ++void GRAPH_WRLOCK bdrv_schedule_unref(BlockDriverState *bs); ++ ++void GRAPH_WRLOCK ++bdrv_unref_child(BlockDriverState *parent, BdrvChild *child); ++ ++void coroutine_fn no_co_wrapper_bdrv_wrlock ++bdrv_co_unref_child(BlockDriverState *parent, BdrvChild *child); ++ ++BdrvChild * GRAPH_WRLOCK ++bdrv_attach_child(BlockDriverState *parent_bs, ++ BlockDriverState *child_bs, ++ const char *child_name, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ Error **errp); ++ ++bool GRAPH_RDLOCK ++bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp); ++ ++void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason); ++void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason); ++void bdrv_op_block_all(BlockDriverState *bs, Error *reason); ++void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason); ++bool bdrv_op_blocker_is_empty(BlockDriverState *bs); ++ ++int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, ++ const char *tag); ++int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag); ++int bdrv_debug_resume(BlockDriverState *bs, const char *tag); ++bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag); ++ ++bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp); ++int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, ++ BdrvChild *ignore_child, Error **errp); ++ ++int GRAPH_RDLOCK bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); ++int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo); ++ ++void GRAPH_WRLOCK ++bdrv_add_child(BlockDriverState *parent, BlockDriverState *child, Error **errp); ++ ++void GRAPH_WRLOCK ++bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp); ++ ++/** ++ * ++ * bdrv_register_buf/bdrv_unregister_buf: ++ * ++ * Register/unregister a buffer for I/O. For example, VFIO drivers are ++ * interested to know the memory areas that would later be used for I/O, so ++ * that they can prepare IOMMU mapping etc., to get better performance. ++ * ++ * Buffers must not overlap and they must be unregistered with the same values that they were registered with. ++ * ++ * Returns: true on success, false on failure ++ */ ++bool bdrv_register_buf(BlockDriverState *bs, void *host, size_t size, ++ Error **errp); ++void bdrv_unregister_buf(BlockDriverState *bs, void *host, size_t size); ++ ++void bdrv_cancel_in_flight(BlockDriverState *bs); ++ ++#endif /* BLOCK_GLOBAL_STATE_H */ +diff --git a/include/block/block-io.h b/include/block/block-io.h +new file mode 100644 +index 00000000..b49e0537 +--- /dev/null ++++ b/include/block/block-io.h +@@ -0,0 +1,455 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_IO_H ++#define BLOCK_IO_H ++ ++#include "block/aio-wait.h" ++#include "block/block-common.h" ++#include "qemu/coroutine.h" ++#include "qemu/iov.h" ++ ++/* ++ * I/O API functions. These functions are thread-safe, and therefore ++ * can run in any thread. ++ * ++ * These functions can only call functions from I/O and Common categories, ++ * but can be invoked by GS, "I/O or GS" and I/O APIs. ++ * ++ * All functions in this category must use the macro ++ * IO_CODE(); ++ * to catch when they are accidentally called by the wrong API. ++ */ ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags); ++ ++int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_pread(BdrvChild *child, int64_t offset, int64_t bytes, void *buf, ++ BdrvRequestFlags flags); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_pwrite(BdrvChild *child, int64_t offset,int64_t bytes, ++ const void *buf, BdrvRequestFlags flags); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_pwrite_sync(BdrvChild *child, int64_t offset, int64_t bytes, ++ const void *buf, BdrvRequestFlags flags); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_pwrite_sync(BdrvChild *child, int64_t offset, int64_t bytes, ++ const void *buf, BdrvRequestFlags flags); ++ ++/* ++ * Efficiently zero a region of the disk image. Note that this is a regular ++ * I/O request like read or write and should have a reasonable size. This ++ * function is not suitable for zeroing the entire image in a single request ++ * because it may allocate memory for the entire region. ++ */ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); ++ ++int64_t coroutine_fn GRAPH_RDLOCK bdrv_co_nb_sectors(BlockDriverState *bs); ++int64_t coroutine_mixed_fn bdrv_nb_sectors(BlockDriverState *bs); ++ ++int64_t coroutine_fn GRAPH_RDLOCK bdrv_co_getlength(BlockDriverState *bs); ++int64_t co_wrapper_mixed_bdrv_rdlock bdrv_getlength(BlockDriverState *bs); ++ ++int64_t coroutine_fn GRAPH_RDLOCK ++bdrv_co_get_allocated_file_size(BlockDriverState *bs); ++ ++int64_t co_wrapper_bdrv_rdlock ++bdrv_get_allocated_file_size(BlockDriverState *bs); ++ ++BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts, ++ BlockDriverState *in_bs, Error **errp); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_delete_file(BlockDriverState *bs, Error **errp); ++ ++void coroutine_fn GRAPH_RDLOCK ++bdrv_co_delete_file_noerr(BlockDriverState *bs); ++ ++ ++/* async block I/O */ ++void bdrv_aio_cancel_async(BlockAIOCB *acb); ++ ++/* sg packet commands */ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf); ++ ++/* Ensure contents are flushed to disk. */ ++int coroutine_fn GRAPH_RDLOCK bdrv_co_flush(BlockDriverState *bs); ++ ++int coroutine_fn GRAPH_RDLOCK bdrv_co_pdiscard(BdrvChild *child, int64_t offset, ++ int64_t bytes); ++ ++/* Report zone information of zone block device. */ ++int coroutine_fn GRAPH_RDLOCK bdrv_co_zone_report(BlockDriverState *bs, ++ int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_zone_mgmt(BlockDriverState *bs, ++ BlockZoneOp op, ++ int64_t offset, int64_t len); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_zone_append(BlockDriverState *bs, ++ int64_t *offset, ++ QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ ++bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, BlockDriverState **file); ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, BlockDriverState **file); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_block_status_above(BlockDriverState *bs, BlockDriverState *base, ++ int64_t offset, int64_t bytes, int64_t *pnum, ++ int64_t *map, BlockDriverState **file); ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base, ++ int64_t offset, int64_t bytes, int64_t *pnum, ++ int64_t *map, BlockDriverState **file); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ int64_t *pnum); ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_is_allocated(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, int64_t *pnum); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_is_allocated_above(BlockDriverState *top, BlockDriverState *base, ++ bool include_base, int64_t offset, int64_t bytes, ++ int64_t *pnum); ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_is_allocated_above(BlockDriverState *bs, BlockDriverState *base, ++ bool include_base, int64_t offset, ++ int64_t bytes, int64_t *pnum); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, int64_t bytes); ++ ++int GRAPH_RDLOCK ++bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, ++ Error **errp); ++ ++bool bdrv_is_read_only(BlockDriverState *bs); ++bool bdrv_is_writable(BlockDriverState *bs); ++bool bdrv_is_sg(BlockDriverState *bs); ++int bdrv_get_flags(BlockDriverState *bs); ++ ++bool coroutine_fn GRAPH_RDLOCK bdrv_co_is_inserted(BlockDriverState *bs); ++bool co_wrapper_bdrv_rdlock bdrv_is_inserted(BlockDriverState *bs); ++ ++void coroutine_fn GRAPH_RDLOCK ++bdrv_co_lock_medium(BlockDriverState *bs, bool locked); ++ ++void coroutine_fn GRAPH_RDLOCK ++bdrv_co_eject(BlockDriverState *bs, bool eject_flag); ++ ++const char *bdrv_get_format_name(BlockDriverState *bs); ++ ++bool GRAPH_RDLOCK bdrv_supports_compressed_writes(BlockDriverState *bs); ++const char *bdrv_get_node_name(const BlockDriverState *bs); ++ ++const char * GRAPH_RDLOCK ++bdrv_get_device_name(const BlockDriverState *bs); ++ ++const char * GRAPH_RDLOCK ++bdrv_get_device_or_node_name(const BlockDriverState *bs); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); ++ ++ImageInfoSpecific * GRAPH_RDLOCK ++bdrv_get_specific_info(BlockDriverState *bs, Error **errp); ++ ++BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs); ++void bdrv_round_to_subclusters(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, ++ int64_t *cluster_offset, ++ int64_t *cluster_bytes); ++ ++void bdrv_get_backing_filename(BlockDriverState *bs, ++ char *filename, int filename_size); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_change_backing_file(BlockDriverState *bs, const char *backing_file, ++ const char *backing_fmt, bool warn); ++ ++int co_wrapper_bdrv_rdlock ++bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, ++ const char *backing_fmt, bool warn); ++ ++int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, ++ int64_t pos, int size); ++ ++int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, ++ int64_t pos, int size); ++ ++/* ++ * Returns the alignment in bytes that is required so that no bounce buffer ++ * is required throughout the stack ++ */ ++size_t bdrv_min_mem_align(BlockDriverState *bs); ++/* Returns optimal alignment in bytes for bounce buffer */ ++size_t bdrv_opt_mem_align(BlockDriverState *bs); ++void *qemu_blockalign(BlockDriverState *bs, size_t size); ++void *qemu_blockalign0(BlockDriverState *bs, size_t size); ++void *qemu_try_blockalign(BlockDriverState *bs, size_t size); ++void *qemu_try_blockalign0(BlockDriverState *bs, size_t size); ++ ++void bdrv_enable_copy_on_read(BlockDriverState *bs); ++void bdrv_disable_copy_on_read(BlockDriverState *bs); ++ ++void coroutine_fn GRAPH_RDLOCK ++bdrv_co_debug_event(BlockDriverState *bs, BlkdebugEvent event); ++ ++void co_wrapper_mixed_bdrv_rdlock ++bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event); ++ ++#define BLKDBG_CO_EVENT(child, evt) \ ++ do { \ ++ if (child) { \ ++ bdrv_co_debug_event(child->bs, evt); \ ++ } \ ++ } while (0) ++ ++#define BLKDBG_EVENT(child, evt) \ ++ do { \ ++ if (child) { \ ++ bdrv_debug_event(child->bs, evt); \ ++ } \ ++ } while (0) ++ ++/** ++ * bdrv_get_aio_context: ++ * ++ * Returns: the currently bound #AioContext ++ */ ++AioContext *bdrv_get_aio_context(BlockDriverState *bs); ++ ++AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c); ++ ++/** ++ * Move the current coroutine to the AioContext of @bs and return the old ++ * AioContext of the coroutine. Increase bs->in_flight so that draining @bs ++ * will wait for the operation to proceed until the corresponding ++ * bdrv_co_leave(). ++ * ++ * Consequently, you can't call drain inside a bdrv_co_enter/leave() section as ++ * this will deadlock. ++ */ ++AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs); ++ ++/** ++ * Ends a section started by bdrv_co_enter(). Move the current coroutine back ++ * to old_ctx and decrease bs->in_flight again. ++ */ ++void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx); ++ ++AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c); ++ ++bool coroutine_fn GRAPH_RDLOCK ++bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, ++ uint32_t granularity, Error **errp); ++bool co_wrapper_bdrv_rdlock ++bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, ++ uint32_t granularity, Error **errp); ++ ++/** ++ * ++ * bdrv_co_copy_range: ++ * ++ * Do offloaded copy between two children. If the operation is not implemented ++ * by the driver, or if the backend storage doesn't support it, a negative ++ * error code will be returned. ++ * ++ * Note: block layer doesn't emulate or fallback to a bounce buffer approach ++ * because usually the caller shouldn't attempt offloaded copy any more (e.g. ++ * calling copy_file_range(2)) after the first error, thus it should fall back ++ * to a read+write path in the caller level. ++ * ++ * @src: Source child to copy data from ++ * @src_offset: offset in @src image to read data ++ * @dst: Destination child to copy data to ++ * @dst_offset: offset in @dst image to write data ++ * @bytes: number of bytes to copy ++ * @flags: request flags. Supported flags: ++ * BDRV_REQ_ZERO_WRITE - treat the @src range as zero data and do zero ++ * write on @dst as if bdrv_co_pwrite_zeroes is ++ * called. Used to simplify caller code, or ++ * during BlockDriver.bdrv_co_copy_range_from() ++ * recursion. ++ * BDRV_REQ_NO_SERIALISING - do not serialize with other overlapping ++ * requests currently in flight. ++ * ++ * Returns: 0 if succeeded; negative error code if failed. ++ **/ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags); ++ ++/* ++ * "I/O or GS" API functions. These functions can run without ++ * the BQL, but only in one specific iothread/main loop. ++ * ++ * More specifically, these functions use BDRV_POLL_WHILE(bs), which requires ++ * the caller to be either in the main thread or directly in the home thread ++ * that runs the bs AioContext. Calling them from another thread in another ++ * AioContext would cause deadlocks. ++ * ++ * Therefore, these functions are not proper I/O, because they ++ * can't run in *any* iothreads, but only in a specific one. ++ * ++ * These functions can call any function from I/O, Common and this ++ * categories, but must be invoked only by other "I/O or GS" and GS APIs. ++ * ++ * All functions in this category must use the macro ++ * IO_OR_GS_CODE(); ++ * to catch when they are accidentally called by the wrong API. ++ */ ++ ++#define BDRV_POLL_WHILE(bs, cond) ({ \ ++ BlockDriverState *bs_ = (bs); \ ++ IO_OR_GS_CODE(); \ ++ AIO_WAIT_WHILE(bdrv_get_aio_context(bs_), \ ++ cond); }) ++ ++void bdrv_drain(BlockDriverState *bs); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_truncate(BdrvChild *child, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); ++ ++/* Invalidate any cached metadata used by image formats */ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_invalidate_cache(BlockDriverState *bs, Error **errp); ++ ++int co_wrapper_mixed_bdrv_rdlock bdrv_flush(BlockDriverState *bs); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); ++ ++/** ++ * bdrv_parent_drained_begin_single: ++ * ++ * Begin a quiesced section for the parent of @c. ++ */ ++void GRAPH_RDLOCK bdrv_parent_drained_begin_single(BdrvChild *c); ++ ++/** ++ * bdrv_parent_drained_poll_single: ++ * ++ * Returns true if there is any pending activity to cease before @c can be ++ * called quiesced, false otherwise. ++ */ ++bool GRAPH_RDLOCK bdrv_parent_drained_poll_single(BdrvChild *c); ++ ++/** ++ * bdrv_parent_drained_end_single: ++ * ++ * End a quiesced section for the parent of @c. ++ */ ++void GRAPH_RDLOCK bdrv_parent_drained_end_single(BdrvChild *c); ++ ++/** ++ * bdrv_drain_poll: ++ * ++ * Poll for pending requests in @bs and its parents (except for @ignore_parent). ++ * ++ * If @ignore_bds_parents is true, parents that are BlockDriverStates must ++ * ignore the drain request because they will be drained separately (used for ++ * drain_all). ++ * ++ * This is part of bdrv_drained_begin. ++ */ ++bool GRAPH_RDLOCK ++bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent, ++ bool ignore_bds_parents); ++ ++/** ++ * bdrv_drained_begin: ++ * ++ * Begin a quiesced section for exclusive access to the BDS, by disabling ++ * external request sources including NBD server, block jobs, and device model. ++ * ++ * This function can only be invoked by the main loop or a coroutine ++ * (regardless of the AioContext where it is running). ++ * If the coroutine is running in an Iothread AioContext, this function will ++ * just schedule a BH to run in the main loop. ++ * However, it cannot be directly called by an Iothread. ++ * ++ * This function can be recursive. ++ */ ++void bdrv_drained_begin(BlockDriverState *bs); ++ ++/** ++ * bdrv_do_drained_begin_quiesce: ++ * ++ * Quiesces a BDS like bdrv_drained_begin(), but does not wait for already ++ * running requests to complete. ++ */ ++void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent); ++ ++/** ++ * bdrv_drained_end: ++ * ++ * End a quiescent section started by bdrv_drained_begin(). ++ * ++ * This function can only be invoked by the main loop or a coroutine ++ * (regardless of the AioContext where it is running). ++ * If the coroutine is running in an Iothread AioContext, this function will ++ * just schedule a BH to run in the main loop. ++ * However, it cannot be directly called by an Iothread. ++ */ ++void bdrv_drained_end(BlockDriverState *bs); ++ ++#endif /* BLOCK_IO_H */ +diff --git a/include/block/block.h b/include/block/block.h +new file mode 100644 +index 00000000..e2c647de +--- /dev/null ++++ b/include/block/block.h +@@ -0,0 +1,32 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_H ++#define BLOCK_H ++ ++#include "block/block-global-state.h" ++#include "block/block-io.h" ++ ++/* DO NOT ADD ANYTHING IN HERE. USE ONE OF THE HEADERS INCLUDED ABOVE */ ++ ++#endif /* BLOCK_H */ +diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h +new file mode 100644 +index 00000000..ebb4e56a +--- /dev/null ++++ b/include/block/block_int-common.h +@@ -0,0 +1,1318 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_INT_COMMON_H ++#define BLOCK_INT_COMMON_H ++ ++#include "block/aio.h" ++#include "block/block-common.h" ++#include "block/block-global-state.h" ++#include "block/snapshot.h" ++#include "qemu/iov.h" ++#include "qemu/rcu.h" ++#include "qemu/stats64.h" ++ ++#define BLOCK_FLAG_LAZY_REFCOUNTS 8 ++ ++#define BLOCK_OPT_SIZE "size" ++#define BLOCK_OPT_ENCRYPT "encryption" ++#define BLOCK_OPT_ENCRYPT_FORMAT "encrypt.format" ++#define BLOCK_OPT_COMPAT6 "compat6" ++#define BLOCK_OPT_HWVERSION "hwversion" ++#define BLOCK_OPT_BACKING_FILE "backing_file" ++#define BLOCK_OPT_BACKING_FMT "backing_fmt" ++#define BLOCK_OPT_CLUSTER_SIZE "cluster_size" ++#define BLOCK_OPT_TABLE_SIZE "table_size" ++#define BLOCK_OPT_PREALLOC "preallocation" ++#define BLOCK_OPT_SUBFMT "subformat" ++#define BLOCK_OPT_COMPAT_LEVEL "compat" ++#define BLOCK_OPT_LAZY_REFCOUNTS "lazy_refcounts" ++#define BLOCK_OPT_ADAPTER_TYPE "adapter_type" ++#define BLOCK_OPT_REDUNDANCY "redundancy" ++#define BLOCK_OPT_NOCOW "nocow" ++#define BLOCK_OPT_EXTENT_SIZE_HINT "extent_size_hint" ++#define BLOCK_OPT_OBJECT_SIZE "object_size" ++#define BLOCK_OPT_REFCOUNT_BITS "refcount_bits" ++#define BLOCK_OPT_DATA_FILE "data_file" ++#define BLOCK_OPT_DATA_FILE_RAW "data_file_raw" ++#define BLOCK_OPT_COMPRESSION_TYPE "compression_type" ++#define BLOCK_OPT_EXTL2 "extended_l2" ++ ++#define BLOCK_PROBE_BUF_SIZE 512 ++ ++enum BdrvTrackedRequestType { ++ BDRV_TRACKED_READ, ++ BDRV_TRACKED_WRITE, ++ BDRV_TRACKED_DISCARD, ++ BDRV_TRACKED_TRUNCATE, ++}; ++ ++/* ++ * That is not quite good that BdrvTrackedRequest structure is public, ++ * as block/io.c is very careful about incoming offset/bytes being ++ * correct. Be sure to assert bdrv_check_request() succeeded after any ++ * modification of BdrvTrackedRequest object out of block/io.c ++ */ ++typedef struct BdrvTrackedRequest { ++ BlockDriverState *bs; ++ int64_t offset; ++ int64_t bytes; ++ enum BdrvTrackedRequestType type; ++ ++ bool serialising; ++ int64_t overlap_offset; ++ int64_t overlap_bytes; ++ ++ QLIST_ENTRY(BdrvTrackedRequest) list; ++ Coroutine *co; /* owner, used for deadlock detection */ ++ CoQueue wait_queue; /* coroutines blocked on this request */ ++ ++ struct BdrvTrackedRequest *waiting_for; ++} BdrvTrackedRequest; ++ ++ ++struct BlockDriver { ++ /* ++ * These fields are initialized when this object is created, ++ * and are never changed afterwards. ++ */ ++ ++ const char *format_name; ++ int instance_size; ++ ++ /* ++ * Set to true if the BlockDriver is a block filter. Block filters pass ++ * certain callbacks that refer to data (see block.c) to their bs->file ++ * or bs->backing (whichever one exists) if the driver doesn't implement ++ * them. Drivers that do not wish to forward must implement them and return ++ * -ENOTSUP. ++ * Note that filters are not allowed to modify data. ++ * ++ * Filters generally cannot have more than a single filtered child, ++ * because the data they present must at all times be the same as ++ * that on their filtered child. That would be impossible to ++ * achieve for multiple filtered children. ++ * (And this filtered child must then be bs->file or bs->backing.) ++ */ ++ bool is_filter; ++ /* ++ * Only make sense for filter drivers, for others must be false. ++ * If true, filtered child is bs->backing. Otherwise it's bs->file. ++ * Two internal filters use bs->backing as filtered child and has this ++ * field set to true: mirror_top and commit_top. There also two such test ++ * filters in tests/unit/test-bdrv-graph-mod.c. ++ * ++ * Never create any more such filters! ++ * ++ * TODO: imagine how to deprecate this behavior and make all filters work ++ * similarly using bs->file as filtered child. ++ */ ++ bool filtered_child_is_backing; ++ ++ /* ++ * Set to true if the BlockDriver is a format driver. Format nodes ++ * generally do not expect their children to be other format nodes ++ * (except for backing files), and so format probing is disabled ++ * on those children. ++ */ ++ bool is_format; ++ ++ /* ++ * Set to true if the BlockDriver supports zoned children. ++ */ ++ bool supports_zoned_children; ++ ++ /* ++ * Drivers not implementing bdrv_parse_filename nor bdrv_open should have ++ * this field set to true, except ones that are defined only by their ++ * child's bs. ++ * An example of the last type will be the quorum block driver. ++ */ ++ bool bdrv_needs_filename; ++ ++ /* ++ * Set if a driver can support backing files. This also implies the ++ * following semantics: ++ * ++ * - Return status 0 of .bdrv_co_block_status means that corresponding ++ * blocks are not allocated in this layer of backing-chain ++ * - For such (unallocated) blocks, read will: ++ * - fill buffer with zeros if there is no backing file ++ * - read from the backing file otherwise, where the block layer ++ * takes care of reading zeros beyond EOF if backing file is short ++ */ ++ bool supports_backing; ++ ++ /* ++ * Drivers setting this field must be able to work with just a plain ++ * filename with ':' as a prefix, and no other options. ++ * Options may be extracted from the filename by implementing ++ * bdrv_parse_filename. ++ */ ++ const char *protocol_name; ++ ++ /* List of options for creating images, terminated by name == NULL */ ++ QemuOptsList *create_opts; ++ ++ /* List of options for image amend */ ++ QemuOptsList *amend_opts; ++ ++ /* ++ * If this driver supports reopening images this contains a ++ * NULL-terminated list of the runtime options that can be ++ * modified. If an option in this list is unspecified during ++ * reopen then it _must_ be reset to its default value or return ++ * an error. ++ */ ++ const char *const *mutable_opts; ++ ++ /* ++ * Pointer to a NULL-terminated array of names of strong options ++ * that can be specified for bdrv_open(). A strong option is one ++ * that changes the data of a BDS. ++ * If this pointer is NULL, the array is considered empty. ++ * "filename" and "driver" are always considered strong. ++ */ ++ const char *const *strong_runtime_opts; ++ ++ ++ /* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++ /* ++ * This function is invoked under BQL before .bdrv_co_amend() ++ * (which in contrast does not necessarily run under the BQL) ++ * to allow driver-specific initialization code that requires ++ * the BQL, like setting up specific permission flags. ++ */ ++ int GRAPH_RDLOCK_PTR (*bdrv_amend_pre_run)( ++ BlockDriverState *bs, Error **errp); ++ /* ++ * This function is invoked under BQL after .bdrv_co_amend() ++ * to allow cleaning up what was done in .bdrv_amend_pre_run(). ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_amend_clean)(BlockDriverState *bs); ++ ++ /* ++ * Return true if @to_replace can be replaced by a BDS with the ++ * same data as @bs without it affecting @bs's behavior (that is, ++ * without it being visible to @bs's parents). ++ */ ++ bool GRAPH_RDLOCK_PTR (*bdrv_recurse_can_replace)( ++ BlockDriverState *bs, BlockDriverState *to_replace); ++ ++ int (*bdrv_probe_device)(const char *filename); ++ ++ /* ++ * Any driver implementing this callback is expected to be able to handle ++ * NULL file names in its .bdrv_open() implementation. ++ */ ++ void (*bdrv_parse_filename)(const char *filename, QDict *options, ++ Error **errp); ++ ++ /* For handling image reopen for split or non-split files. */ ++ int GRAPH_UNLOCKED_PTR (*bdrv_reopen_prepare)( ++ BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp); ++ void GRAPH_UNLOCKED_PTR (*bdrv_reopen_commit)( ++ BDRVReopenState *reopen_state); ++ void GRAPH_UNLOCKED_PTR (*bdrv_reopen_commit_post)( ++ BDRVReopenState *reopen_state); ++ void GRAPH_UNLOCKED_PTR (*bdrv_reopen_abort)( ++ BDRVReopenState *reopen_state); ++ void (*bdrv_join_options)(QDict *options, QDict *old_options); ++ ++ int GRAPH_UNLOCKED_PTR (*bdrv_open)( ++ BlockDriverState *bs, QDict *options, int flags, Error **errp); ++ ++ void (*bdrv_close)(BlockDriverState *bs); ++ ++ int coroutine_fn GRAPH_UNLOCKED_PTR (*bdrv_co_create)( ++ BlockdevCreateOptions *opts, Error **errp); ++ ++ int coroutine_fn GRAPH_UNLOCKED_PTR (*bdrv_co_create_opts)( ++ BlockDriver *drv, const char *filename, QemuOpts *opts, Error **errp); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_amend_options)( ++ BlockDriverState *bs, QemuOpts *opts, ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ bool force, Error **errp); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_make_empty)(BlockDriverState *bs); ++ ++ /* ++ * Refreshes the bs->exact_filename field. If that is impossible, ++ * bs->exact_filename has to be left empty. ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_refresh_filename)(BlockDriverState *bs); ++ ++ /* ++ * Gathers the open options for all children into @target. ++ * A simple format driver (without backing file support) might ++ * implement this function like this: ++ * ++ * QINCREF(bs->file->bs->full_open_options); ++ * qdict_put(target, "file", bs->file->bs->full_open_options); ++ * ++ * If not specified, the generic implementation will simply put ++ * all children's options under their respective name. ++ * ++ * @backing_overridden is true when bs->backing seems not to be ++ * the child that would result from opening bs->backing_file. ++ * Therefore, if it is true, the backing child's options should be ++ * gathered; otherwise, there is no need since the backing child ++ * is the one implied by the image header. ++ * ++ * Note that ideally this function would not be needed. Every ++ * block driver which implements it is probably doing something ++ * shady regarding its runtime option structure. ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_gather_child_options)( ++ BlockDriverState *bs, QDict *target, bool backing_overridden); ++ ++ /* ++ * Returns an allocated string which is the directory name of this BDS: It ++ * will be used to make relative filenames absolute by prepending this ++ * function's return value to them. ++ */ ++ char * GRAPH_RDLOCK_PTR (*bdrv_dirname)(BlockDriverState *bs, Error **errp); ++ ++ /* ++ * This informs the driver that we are no longer interested in the result ++ * of in-flight requests, so don't waste the time if possible. ++ * ++ * One example usage is to avoid waiting for an nbd target node reconnect ++ * timeout during job-cancel with force=true. ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_cancel_in_flight)(BlockDriverState *bs); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_inactivate)(BlockDriverState *bs); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_snapshot_create)( ++ BlockDriverState *bs, QEMUSnapshotInfo *sn_info); ++ ++ int GRAPH_UNLOCKED_PTR (*bdrv_snapshot_goto)( ++ BlockDriverState *bs, const char *snapshot_id); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_snapshot_delete)( ++ BlockDriverState *bs, const char *snapshot_id, const char *name, ++ Error **errp); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_snapshot_list)( ++ BlockDriverState *bs, QEMUSnapshotInfo **psn_info); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_snapshot_load_tmp)( ++ BlockDriverState *bs, const char *snapshot_id, const char *name, ++ Error **errp); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_change_backing_file)( ++ BlockDriverState *bs, const char *backing_file, ++ const char *backing_fmt); ++ ++ /* TODO Better pass a option string/QDict/QemuOpts to add any rule? */ ++ int (*bdrv_debug_breakpoint)(BlockDriverState *bs, const char *event, ++ const char *tag); ++ int (*bdrv_debug_remove_breakpoint)(BlockDriverState *bs, ++ const char *tag); ++ int (*bdrv_debug_resume)(BlockDriverState *bs, const char *tag); ++ bool (*bdrv_debug_is_suspended)(BlockDriverState *bs, const char *tag); ++ ++ void GRAPH_RDLOCK_PTR (*bdrv_refresh_limits)( ++ BlockDriverState *bs, Error **errp); ++ ++ /* ++ * Returns 1 if newly created images are guaranteed to contain only ++ * zeros, 0 otherwise. ++ */ ++ int GRAPH_RDLOCK_PTR (*bdrv_has_zero_init)(BlockDriverState *bs); ++ ++ /* ++ * Remove fd handlers, timers, and other event loop callbacks so the event ++ * loop is no longer in use. Called with no in-flight requests and in ++ * depth-first traversal order with parents before child nodes. ++ */ ++ void (*bdrv_detach_aio_context)(BlockDriverState *bs); ++ ++ /* ++ * Add fd handlers, timers, and other event loop callbacks so I/O requests ++ * can be processed again. Called with no in-flight requests and in ++ * depth-first traversal order with child nodes before parent nodes. ++ */ ++ void (*bdrv_attach_aio_context)(BlockDriverState *bs, ++ AioContext *new_context); ++ ++ /** ++ * bdrv_drain_begin is called if implemented in the beginning of a ++ * drain operation to drain and stop any internal sources of requests in ++ * the driver. ++ * bdrv_drain_end is called if implemented at the end of the drain. ++ * ++ * They should be used by the driver to e.g. manage scheduled I/O ++ * requests, or toggle an internal state. After the end of the drain new ++ * requests will continue normally. ++ * ++ * Implementations of both functions must not call aio_poll(). ++ */ ++ void (*bdrv_drain_begin)(BlockDriverState *bs); ++ void (*bdrv_drain_end)(BlockDriverState *bs); ++ ++ /** ++ * Try to get @bs's logical and physical block size. ++ * On success, store them in @bsz and return zero. ++ * On failure, return negative errno. ++ */ ++ int GRAPH_RDLOCK_PTR (*bdrv_probe_blocksizes)( ++ BlockDriverState *bs, BlockSizes *bsz); ++ /** ++ * Try to get @bs's geometry (cyls, heads, sectors) ++ * On success, store them in @geo and return 0. ++ * On failure return -errno. ++ * Only drivers that want to override guest geometry implement this ++ * callback; see hd_geometry_guess(). ++ */ ++ int GRAPH_RDLOCK_PTR (*bdrv_probe_geometry)( ++ BlockDriverState *bs, HDGeometry *geo); ++ ++ void GRAPH_WRLOCK_PTR (*bdrv_add_child)( ++ BlockDriverState *parent, BlockDriverState *child, Error **errp); ++ ++ void GRAPH_WRLOCK_PTR (*bdrv_del_child)( ++ BlockDriverState *parent, BdrvChild *child, Error **errp); ++ ++ /** ++ * Informs the block driver that a permission change is intended. The ++ * driver checks whether the change is permissible and may take other ++ * preparations for the change (e.g. get file system locks). This operation ++ * is always followed either by a call to either .bdrv_set_perm or ++ * .bdrv_abort_perm_update. ++ * ++ * Checks whether the requested set of cumulative permissions in @perm ++ * can be granted for accessing @bs and whether no other users are using ++ * permissions other than those given in @shared (both arguments take ++ * BLK_PERM_* bitmasks). ++ * ++ * If both conditions are met, 0 is returned. Otherwise, -errno is returned ++ * and errp is set to an error describing the conflict. ++ */ ++ int GRAPH_RDLOCK_PTR (*bdrv_check_perm)(BlockDriverState *bs, uint64_t perm, ++ uint64_t shared, Error **errp); ++ ++ /** ++ * Called to inform the driver that the set of cumulative set of used ++ * permissions for @bs has changed to @perm, and the set of shareable ++ * permission to @shared. The driver can use this to propagate changes to ++ * its children (i.e. request permissions only if a parent actually needs ++ * them). ++ * ++ * This function is only invoked after bdrv_check_perm(), so block drivers ++ * may rely on preparations made in their .bdrv_check_perm implementation. ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_set_perm)( ++ BlockDriverState *bs, uint64_t perm, uint64_t shared); ++ ++ /* ++ * Called to inform the driver that after a previous bdrv_check_perm() ++ * call, the permission update is not performed and any preparations made ++ * for it (e.g. taken file locks) need to be undone. ++ * ++ * This function can be called even for nodes that never saw a ++ * bdrv_check_perm() call. It is a no-op then. ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_abort_perm_update)(BlockDriverState *bs); ++ ++ /** ++ * Returns in @nperm and @nshared the permissions that the driver for @bs ++ * needs on its child @c, based on the cumulative permissions requested by ++ * the parents in @parent_perm and @parent_shared. ++ * ++ * If @c is NULL, return the permissions for attaching a new child for the ++ * given @child_class and @role. ++ * ++ * If @reopen_queue is non-NULL, don't return the currently needed ++ * permissions, but those that will be needed after applying the ++ * @reopen_queue. ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_child_perm)( ++ BlockDriverState *bs, BdrvChild *c, BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t parent_perm, uint64_t parent_shared, ++ uint64_t *nperm, uint64_t *nshared); ++ ++ /** ++ * Register/unregister a buffer for I/O. For example, when the driver is ++ * interested to know the memory areas that will later be used in iovs, so ++ * that it can do IOMMU mapping with VFIO etc., in order to get better ++ * performance. In the case of VFIO drivers, this callback is used to do ++ * DMA mapping for hot buffers. ++ * ++ * Returns: true on success, false on failure ++ */ ++ bool GRAPH_RDLOCK_PTR (*bdrv_register_buf)( ++ BlockDriverState *bs, void *host, size_t size, Error **errp); ++ void GRAPH_RDLOCK_PTR (*bdrv_unregister_buf)( ++ BlockDriverState *bs, void *host, size_t size); ++ ++ /* ++ * This field is modified only under the BQL, and is part of ++ * the global state. ++ */ ++ QLIST_ENTRY(BlockDriver) list; ++ ++ /* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++ int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_amend)( ++ BlockDriverState *bs, BlockdevAmendOptions *opts, bool force, ++ Error **errp); ++ ++ /* aio */ ++ BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_preadv)(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); ++ ++ BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_pwritev)(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); ++ ++ BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_flush)( ++ BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque); ++ ++ BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_pdiscard)( ++ BlockDriverState *bs, int64_t offset, int bytes, ++ BlockCompletionFunc *cb, void *opaque); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_readv)(BlockDriverState *bs, ++ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); ++ ++ /** ++ * @offset: position in bytes to read at ++ * @bytes: number of bytes to read ++ * @qiov: the buffers to fill with read data ++ * @flags: currently unused, always 0 ++ * ++ * @offset and @bytes will be a multiple of 'request_alignment', ++ * but the length of individual @qiov elements does not have to ++ * be a multiple. ++ * ++ * @bytes will always equal the total size of @qiov, and will be ++ * no larger than 'max_transfer'. ++ * ++ * The buffer in @qiov may point directly to guest memory. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_preadv)(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_preadv_part)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_writev)(BlockDriverState *bs, ++ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, ++ int flags); ++ /** ++ * @offset: position in bytes to write at ++ * @bytes: number of bytes to write ++ * @qiov: the buffers containing data to write ++ * @flags: zero or more bits allowed by 'supported_write_flags' ++ * ++ * @offset and @bytes will be a multiple of 'request_alignment', ++ * but the length of individual @qiov elements does not have to ++ * be a multiple. ++ * ++ * @bytes will always equal the total size of @qiov, and will be ++ * no larger than 'max_transfer'. ++ * ++ * The buffer in @qiov may point directly to guest memory. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwritev)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwritev_part)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ size_t qiov_offset, BdrvRequestFlags flags); ++ ++ /* ++ * Efficiently zero a region of the disk image. Typically an image format ++ * would use a compact metadata representation to implement this. This ++ * function pointer may be NULL or return -ENOSUP and .bdrv_co_writev() ++ * will be called instead. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwrite_zeroes)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pdiscard)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes); ++ ++ /* ++ * Map [offset, offset + nbytes) range onto a child of @bs to copy from, ++ * and invoke bdrv_co_copy_range_from(child, ...), or invoke ++ * bdrv_co_copy_range_to() if @bs is the leaf child to copy data from. ++ * ++ * See the comment of bdrv_co_copy_range for the parameter and return value ++ * semantics. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_copy_range_from)( ++ BlockDriverState *bs, BdrvChild *src, int64_t offset, ++ BdrvChild *dst, int64_t dst_offset, int64_t bytes, ++ BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); ++ ++ /* ++ * Map [offset, offset + nbytes) range onto a child of bs to copy data to, ++ * and invoke bdrv_co_copy_range_to(child, src, ...), or perform the copy ++ * operation if @bs is the leaf and @src has the same BlockDriver. Return ++ * -ENOTSUP if @bs is the leaf but @src has a different BlockDriver. ++ * ++ * See the comment of bdrv_co_copy_range for the parameter and return value ++ * semantics. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_copy_range_to)( ++ BlockDriverState *bs, BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, int64_t bytes, ++ BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); ++ ++ /* ++ * Building block for bdrv_block_status[_above] and ++ * bdrv_is_allocated[_above]. The driver should answer only ++ * according to the current layer, and should only need to set ++ * BDRV_BLOCK_DATA, BDRV_BLOCK_ZERO, BDRV_BLOCK_OFFSET_VALID, ++ * and/or BDRV_BLOCK_RAW; if the current layer defers to a backing ++ * layer, the result should be 0 (and not BDRV_BLOCK_ZERO). See ++ * block.h for the overall meaning of the bits. As a hint, the ++ * flag want_zero is true if the caller cares more about precise ++ * mappings (favor accurate _OFFSET_VALID/_ZERO) or false for ++ * overall allocation (favor larger *pnum, perhaps by reporting ++ * _DATA instead of _ZERO). The block layer guarantees input ++ * clamped to bdrv_getlength() and aligned to request_alignment, ++ * as well as non-NULL pnum, map, and file; in turn, the driver ++ * must return an error or set pnum to an aligned non-zero value. ++ * ++ * Note that @bytes is just a hint on how big of a region the ++ * caller wants to inspect. It is not a limit on *pnum. ++ * Implementations are free to return larger values of *pnum if ++ * doing so does not incur a performance penalty. ++ * ++ * block/io.c's bdrv_co_block_status() will utilize an unclamped ++ * *pnum value for the block-status cache on protocol nodes, prior ++ * to clamping *pnum for return to its caller. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_block_status)( ++ BlockDriverState *bs, ++ bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, ++ int64_t *map, BlockDriverState **file); ++ ++ /* ++ * Snapshot-access API. ++ * ++ * Block-driver may provide snapshot-access API: special functions to access ++ * some internal "snapshot". The functions are similar with normal ++ * read/block_status/discard handler, but don't have any specific handling ++ * in generic block-layer: no serializing, no alignment, no tracked ++ * requests. So, block-driver that realizes these APIs is fully responsible ++ * for synchronization between snapshot-access API and normal IO requests. ++ * ++ * TODO: To be able to support qcow2's internal snapshots, this API will ++ * need to be extended to: ++ * - be able to select a specific snapshot ++ * - receive the snapshot's actual length (which may differ from bs's ++ * length) ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_preadv_snapshot)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_snapshot_block_status)( ++ BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, BlockDriverState **file); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pdiscard_snapshot)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes); ++ ++ /* ++ * Invalidate any cached meta-data. ++ */ ++ void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_invalidate_cache)( ++ BlockDriverState *bs, Error **errp); ++ ++ /* ++ * Flushes all data for all layers by calling bdrv_co_flush for underlying ++ * layers, if needed. This function is needed for deterministic ++ * synchronization of the flush finishing callback. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_flush)(BlockDriverState *bs); ++ ++ /* Delete a created file. */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_delete_file)( ++ BlockDriverState *bs, Error **errp); ++ ++ /* ++ * Flushes all data that was already written to the OS all the way down to ++ * the disk (for example file-posix.c calls fsync()). ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_flush_to_disk)( ++ BlockDriverState *bs); ++ ++ /* ++ * Flushes all internal caches to the OS. The data may still sit in a ++ * writeback cache of the host OS, but it will survive a crash of the qemu ++ * process. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_flush_to_os)( ++ BlockDriverState *bs); ++ ++ /* ++ * Truncate @bs to @offset bytes using the given @prealloc mode ++ * when growing. Modes other than PREALLOC_MODE_OFF should be ++ * rejected when shrinking @bs. ++ * ++ * If @exact is true, @bs must be resized to exactly @offset. ++ * Otherwise, it is sufficient for @bs (if it is a host block ++ * device and thus there is no way to resize it) to be at least ++ * @offset bytes in length. ++ * ++ * If @exact is true and this function fails but would succeed ++ * with @exact = false, it should return -ENOTSUP. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_truncate)( ++ BlockDriverState *bs, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); ++ ++ int64_t coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_getlength)( ++ BlockDriverState *bs); ++ ++ int64_t coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_get_allocated_file_size)( ++ BlockDriverState *bs); ++ ++ BlockMeasureInfo *(*bdrv_measure)(QemuOpts *opts, BlockDriverState *in_bs, ++ Error **errp); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwritev_compressed)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwritev_compressed_part)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_get_info)( ++ BlockDriverState *bs, BlockDriverInfo *bdi); ++ ++ ImageInfoSpecific * GRAPH_RDLOCK_PTR (*bdrv_get_specific_info)( ++ BlockDriverState *bs, Error **errp); ++ BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_save_vmstate)( ++ BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_load_vmstate)( ++ BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); ++ ++ int coroutine_fn (*bdrv_co_zone_report)(BlockDriverState *bs, ++ int64_t offset, unsigned int *nr_zones, ++ BlockZoneDescriptor *zones); ++ int coroutine_fn (*bdrv_co_zone_mgmt)(BlockDriverState *bs, BlockZoneOp op, ++ int64_t offset, int64_t len); ++ int coroutine_fn (*bdrv_co_zone_append)(BlockDriverState *bs, ++ int64_t *offset, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ ++ /* removable device specific */ ++ bool coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_is_inserted)( ++ BlockDriverState *bs); ++ void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_eject)( ++ BlockDriverState *bs, bool eject_flag); ++ void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_lock_medium)( ++ BlockDriverState *bs, bool locked); ++ ++ /* to control generic scsi devices */ ++ BlockAIOCB *coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_aio_ioctl)( ++ BlockDriverState *bs, unsigned long int req, void *buf, ++ BlockCompletionFunc *cb, void *opaque); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_ioctl)( ++ BlockDriverState *bs, unsigned long int req, void *buf); ++ ++ /* ++ * Returns 0 for completed check, -errno for internal errors. ++ * The check results are stored in result. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_check)( ++ BlockDriverState *bs, BdrvCheckResult *result, BdrvCheckMode fix); ++ ++ void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_debug_event)( ++ BlockDriverState *bs, BlkdebugEvent event); ++ ++ bool (*bdrv_supports_persistent_dirty_bitmap)(BlockDriverState *bs); ++ ++ bool coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_can_store_new_dirty_bitmap)( ++ BlockDriverState *bs, const char *name, uint32_t granularity, ++ Error **errp); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_remove_persistent_dirty_bitmap)( ++ BlockDriverState *bs, const char *name, Error **errp); ++}; ++ ++static inline bool TSA_NO_TSA block_driver_can_compress(BlockDriver *drv) ++{ ++ return drv->bdrv_co_pwritev_compressed || ++ drv->bdrv_co_pwritev_compressed_part; ++} ++ ++typedef struct BlockLimits { ++ /* ++ * Alignment requirement, in bytes, for offset/length of I/O ++ * requests. Must be a power of 2 less than INT_MAX; defaults to ++ * 1 for drivers with modern byte interfaces, and to 512 ++ * otherwise. ++ */ ++ uint32_t request_alignment; ++ ++ /* ++ * Maximum number of bytes that can be discarded at once. Must be multiple ++ * of pdiscard_alignment, but need not be power of 2. May be 0 if no ++ * inherent 64-bit limit. ++ */ ++ int64_t max_pdiscard; ++ ++ /* ++ * Optimal alignment for discard requests in bytes. A power of 2 ++ * is best but not mandatory. Must be a multiple of ++ * bl.request_alignment, and must be less than max_pdiscard if ++ * that is set. May be 0 if bl.request_alignment is good enough ++ */ ++ uint32_t pdiscard_alignment; ++ ++ /* ++ * Maximum number of bytes that can zeroized at once. Must be multiple of ++ * pwrite_zeroes_alignment. 0 means no limit. ++ */ ++ int64_t max_pwrite_zeroes; ++ ++ /* ++ * Optimal alignment for write zeroes requests in bytes. A power ++ * of 2 is best but not mandatory. Must be a multiple of ++ * bl.request_alignment, and must be less than max_pwrite_zeroes ++ * if that is set. May be 0 if bl.request_alignment is good ++ * enough ++ */ ++ uint32_t pwrite_zeroes_alignment; ++ ++ /* ++ * Optimal transfer length in bytes. A power of 2 is best but not ++ * mandatory. Must be a multiple of bl.request_alignment, or 0 if ++ * no preferred size ++ */ ++ uint32_t opt_transfer; ++ ++ /* ++ * Maximal transfer length in bytes. Need not be power of 2, but ++ * must be multiple of opt_transfer and bl.request_alignment, or 0 ++ * for no 32-bit limit. For now, anything larger than INT_MAX is ++ * clamped down. ++ */ ++ uint32_t max_transfer; ++ ++ /* ++ * Maximal hardware transfer length in bytes. Applies whenever ++ * transfers to the device bypass the kernel I/O scheduler, for ++ * example with SG_IO. If larger than max_transfer or if zero, ++ * blk_get_max_hw_transfer will fall back to max_transfer. ++ */ ++ uint64_t max_hw_transfer; ++ ++ /* ++ * Maximal number of scatter/gather elements allowed by the hardware. ++ * Applies whenever transfers to the device bypass the kernel I/O ++ * scheduler, for example with SG_IO. If larger than max_iov ++ * or if zero, blk_get_max_hw_iov will fall back to max_iov. ++ */ ++ int max_hw_iov; ++ ++ ++ /* memory alignment, in bytes so that no bounce buffer is needed */ ++ size_t min_mem_alignment; ++ ++ /* memory alignment, in bytes, for bounce buffer */ ++ size_t opt_mem_alignment; ++ ++ /* maximum number of iovec elements */ ++ int max_iov; ++ ++ /* ++ * true if the length of the underlying file can change, and QEMU ++ * is expected to adjust automatically. Mostly for CD-ROM drives, ++ * whose length is zero when the tray is empty (they don't need ++ * an explicit monitor command to load the disk inside the guest). ++ */ ++ bool has_variable_length; ++ ++ /* device zone model */ ++ BlockZoneModel zoned; ++ ++ /* zone size expressed in bytes */ ++ uint32_t zone_size; ++ ++ /* total number of zones */ ++ uint32_t nr_zones; ++ ++ /* maximum sectors of a zone append write operation */ ++ uint32_t max_append_sectors; ++ ++ /* maximum number of open zones */ ++ uint32_t max_open_zones; ++ ++ /* maximum number of active zones */ ++ uint32_t max_active_zones; ++ ++ uint32_t write_granularity; ++} BlockLimits; ++ ++typedef struct BdrvOpBlocker BdrvOpBlocker; ++ ++typedef struct BdrvAioNotifier { ++ void (*attached_aio_context)(AioContext *new_context, void *opaque); ++ void (*detach_aio_context)(void *opaque); ++ ++ void *opaque; ++ bool deleted; ++ ++ QLIST_ENTRY(BdrvAioNotifier) list; ++} BdrvAioNotifier; ++ ++struct BdrvChildClass { ++ /* ++ * If true, bdrv_replace_node() doesn't change the node this BdrvChild ++ * points to. ++ */ ++ bool stay_at_node; ++ ++ /* ++ * If true, the parent is a BlockDriverState and bdrv_next_all_states() ++ * will return it. This information is used for drain_all, where every node ++ * will be drained separately, so the drain only needs to be propagated to ++ * non-BDS parents. ++ */ ++ bool parent_is_bds; ++ ++ /* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ void (*inherit_options)(BdrvChildRole role, bool parent_is_format, ++ int *child_flags, QDict *child_options, ++ int parent_flags, QDict *parent_options); ++ void (*change_media)(BdrvChild *child, bool load); ++ ++ /* ++ * Returns a malloced string that describes the parent of the child for a ++ * human reader. This could be a node-name, BlockBackend name, qdev ID or ++ * QOM path of the device owning the BlockBackend, job type and ID etc. The ++ * caller is responsible for freeing the memory. ++ */ ++ char *(*get_parent_desc)(BdrvChild *child); ++ ++ /* ++ * Notifies the parent that the child has been activated/inactivated (e.g. ++ * when migration is completing) and it can start/stop requesting ++ * permissions and doing I/O on it. ++ */ ++ void GRAPH_RDLOCK_PTR (*activate)(BdrvChild *child, Error **errp); ++ int GRAPH_RDLOCK_PTR (*inactivate)(BdrvChild *child); ++ ++ void GRAPH_WRLOCK_PTR (*attach)(BdrvChild *child); ++ void GRAPH_WRLOCK_PTR (*detach)(BdrvChild *child); ++ ++ /* ++ * If this pair of functions is implemented, the parent doesn't issue new ++ * requests after returning from .drained_begin() until .drained_end() is ++ * called. ++ * ++ * These functions must not change the graph (and therefore also must not ++ * call aio_poll(), which could change the graph indirectly). ++ * ++ * Note that this can be nested. If drained_begin() was called twice, new ++ * I/O is allowed only after drained_end() was called twice, too. ++ */ ++ void GRAPH_RDLOCK_PTR (*drained_begin)(BdrvChild *child); ++ void GRAPH_RDLOCK_PTR (*drained_end)(BdrvChild *child); ++ ++ /* ++ * Returns whether the parent has pending requests for the child. This ++ * callback is polled after .drained_begin() has been called until all ++ * activity on the child has stopped. ++ */ ++ bool GRAPH_RDLOCK_PTR (*drained_poll)(BdrvChild *child); ++ ++ /* ++ * Notifies the parent that the filename of its child has changed (e.g. ++ * because the direct child was removed from the backing chain), so that it ++ * can update its reference. ++ */ ++ int (*update_filename)(BdrvChild *child, BlockDriverState *new_base, ++ const char *filename, ++ bool backing_mask_protocol, ++ Error **errp); ++ ++ bool (*change_aio_ctx)(BdrvChild *child, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp); ++ ++ /* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++ void (*resize)(BdrvChild *child); ++ ++ /* ++ * Returns a name that is supposedly more useful for human users than the ++ * node name for identifying the node in question (in particular, a BB ++ * name), or NULL if the parent can't provide a better name. ++ */ ++ const char *(*get_name)(BdrvChild *child); ++ ++ AioContext *(*get_parent_aio_context)(BdrvChild *child); ++}; ++ ++extern const BdrvChildClass child_of_bds; ++ ++struct BdrvChild { ++ BlockDriverState *bs; ++ char *name; ++ const BdrvChildClass *klass; ++ BdrvChildRole role; ++ void *opaque; ++ ++ /** ++ * Granted permissions for operating on this BdrvChild (BLK_PERM_* bitmask) ++ */ ++ uint64_t perm; ++ ++ /** ++ * Permissions that can still be granted to other users of @bs while this ++ * BdrvChild is still attached to it. (BLK_PERM_* bitmask) ++ */ ++ uint64_t shared_perm; ++ ++ /* ++ * This link is frozen: the child can neither be replaced nor ++ * detached from the parent. ++ */ ++ bool frozen; ++ ++ /* ++ * True if the parent of this child has been drained by this BdrvChild ++ * (through klass->drained_*). ++ * ++ * It is generally true if bs->quiesce_counter > 0. It may differ while the ++ * child is entering or leaving a drained section. ++ */ ++ bool quiesced_parent; ++ ++ QLIST_ENTRY(BdrvChild GRAPH_RDLOCK_PTR) next; ++ QLIST_ENTRY(BdrvChild GRAPH_RDLOCK_PTR) next_parent; ++}; ++ ++/* ++ * Allows bdrv_co_block_status() to cache one data region for a ++ * protocol node. ++ * ++ * @valid: Whether the cache is valid (should be accessed with atomic ++ * functions so this can be reset by RCU readers) ++ * @data_start: Offset where we know (or strongly assume) is data ++ * @data_end: Offset where the data region ends (which is not necessarily ++ * the start of a zeroed region) ++ */ ++typedef struct BdrvBlockStatusCache { ++ struct rcu_head rcu; ++ ++ bool valid; ++ int64_t data_start; ++ int64_t data_end; ++} BdrvBlockStatusCache; ++ ++struct BlockDriverState { ++ /* ++ * Protected by big QEMU lock or read-only after opening. No special ++ * locking needed during I/O... ++ */ ++ int open_flags; /* flags used to open the file, re-used for re-open */ ++ bool encrypted; /* if true, the media is encrypted */ ++ bool sg; /* if true, the device is a /dev/sg* */ ++ bool probed; /* if true, format was probed rather than specified */ ++ bool force_share; /* if true, always allow all shared permissions */ ++ bool implicit; /* if true, this filter node was automatically inserted */ ++ ++ BlockDriver *drv; /* NULL means no media */ ++ void *opaque; ++ ++ AioContext *aio_context; /* event loop used for fd handlers, timers, etc */ ++ /* ++ * long-running tasks intended to always use the same AioContext as this ++ * BDS may register themselves in this list to be notified of changes ++ * regarding this BDS's context ++ */ ++ QLIST_HEAD(, BdrvAioNotifier) aio_notifiers; ++ bool walking_aio_notifiers; /* to make removal during iteration safe */ ++ ++ char filename[PATH_MAX]; ++ /* ++ * If not empty, this image is a diff in relation to backing_file. ++ * Note that this is the name given in the image header and ++ * therefore may or may not be equal to .backing->bs->filename. ++ * If this field contains a relative path, it is to be resolved ++ * relatively to the overlay's location. ++ */ ++ char backing_file[PATH_MAX]; ++ /* ++ * The backing filename indicated by the image header. Contrary ++ * to backing_file, if we ever open this file, auto_backing_file ++ * is replaced by the resulting BDS's filename (i.e. after a ++ * bdrv_refresh_filename() run). ++ */ ++ char auto_backing_file[PATH_MAX]; ++ char backing_format[16]; /* if non-zero and backing_file exists */ ++ ++ QDict *full_open_options; ++ char exact_filename[PATH_MAX]; ++ ++ /* I/O Limits */ ++ BlockLimits bl; ++ ++ /* ++ * Flags honored during pread ++ */ ++ BdrvRequestFlags supported_read_flags; ++ /* ++ * Flags honored during pwrite (so far: BDRV_REQ_FUA, ++ * BDRV_REQ_WRITE_UNCHANGED). ++ * If a driver does not support BDRV_REQ_WRITE_UNCHANGED, those ++ * writes will be issued as normal writes without the flag set. ++ * This is important to note for drivers that do not explicitly ++ * request a WRITE permission for their children and instead take ++ * the same permissions as their parent did (this is commonly what ++ * block filters do). Such drivers have to be aware that the ++ * parent may have taken a WRITE_UNCHANGED permission only and is ++ * issuing such requests. Drivers either must make sure that ++ * these requests do not result in plain WRITE accesses (usually ++ * by supporting BDRV_REQ_WRITE_UNCHANGED, and then forwarding ++ * every incoming write request as-is, including potentially that ++ * flag), or they have to explicitly take the WRITE permission for ++ * their children. ++ */ ++ BdrvRequestFlags supported_write_flags; ++ /* ++ * Flags honored during pwrite_zeroes (so far: BDRV_REQ_FUA, ++ * BDRV_REQ_MAY_UNMAP, BDRV_REQ_WRITE_UNCHANGED) ++ */ ++ BdrvRequestFlags supported_zero_flags; ++ /* ++ * Flags honoured during truncate (so far: BDRV_REQ_ZERO_WRITE). ++ * ++ * If BDRV_REQ_ZERO_WRITE is given, the truncate operation must make sure ++ * that any added space reads as all zeros. If this can't be guaranteed, ++ * the operation must fail. ++ */ ++ BdrvRequestFlags supported_truncate_flags; ++ ++ /* the following member gives a name to every node on the bs graph. */ ++ char node_name[32]; ++ /* element of the list of named nodes building the graph */ ++ QTAILQ_ENTRY(BlockDriverState) node_list; ++ /* element of the list of all BlockDriverStates (all_bdrv_states) */ ++ QTAILQ_ENTRY(BlockDriverState) bs_list; ++ /* element of the list of monitor-owned BDS */ ++ QTAILQ_ENTRY(BlockDriverState) monitor_list; ++ int refcnt; ++ ++ /* operation blockers. Protected by BQL. */ ++ QLIST_HEAD(, BdrvOpBlocker) op_blockers[BLOCK_OP_TYPE_MAX]; ++ ++ /* ++ * The node that this node inherited default options from (and a reopen on ++ * which can affect this node by changing these defaults). This is always a ++ * parent node of this node. ++ */ ++ BlockDriverState *inherits_from; ++ ++ /* ++ * @backing and @file are some of @children or NULL. All these three fields ++ * (@file, @backing and @children) are modified only in ++ * bdrv_child_cb_attach() and bdrv_child_cb_detach(). ++ * ++ * See also comment in include/block/block.h, to learn how backing and file ++ * are connected with BdrvChildRole. ++ */ ++ QLIST_HEAD(, BdrvChild GRAPH_RDLOCK_PTR) children; ++ BdrvChild * GRAPH_RDLOCK_PTR backing; ++ BdrvChild * GRAPH_RDLOCK_PTR file; ++ ++ QLIST_HEAD(, BdrvChild GRAPH_RDLOCK_PTR) parents; ++ ++ QDict *options; ++ QDict *explicit_options; ++ BlockdevDetectZeroesOptions detect_zeroes; ++ ++ /* The error object in use for blocking operations on backing_hd */ ++ Error *backing_blocker; ++ ++ /* ++ * If we are reading a disk image, give its size in sectors. ++ * Generally read-only; it is written to by load_snapshot and ++ * save_snaphost, but the block layer is quiescent during those. ++ */ ++ int64_t total_sectors; ++ ++ /* threshold limit for writes, in bytes. "High water mark". */ ++ uint64_t write_threshold_offset; ++ ++ /* ++ * Writing to the list requires the BQL _and_ the dirty_bitmap_mutex. ++ * Reading from the list can be done with either the BQL or the ++ * dirty_bitmap_mutex. Modifying a bitmap only requires ++ * dirty_bitmap_mutex. ++ */ ++ QemuMutex dirty_bitmap_mutex; ++ QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps; ++ ++ /* Offset after the highest byte written to */ ++ Stat64 wr_highest_offset; ++ ++ /* ++ * If true, copy read backing sectors into image. Can be >1 if more ++ * than one client has requested copy-on-read. Accessed with atomic ++ * ops. ++ */ ++ int copy_on_read; ++ ++ /* ++ * number of in-flight requests; overall and serialising. ++ * Accessed with atomic ops. ++ */ ++ unsigned int in_flight; ++ unsigned int serialising_in_flight; ++ ++ /* do we need to tell the quest if we have a volatile write cache? */ ++ int enable_write_cache; ++ ++ /* Accessed with atomic ops. */ ++ int quiesce_counter; ++ ++ unsigned int write_gen; /* Current data generation */ ++ ++ /* Protected by reqs_lock. */ ++ QemuMutex reqs_lock; ++ QLIST_HEAD(, BdrvTrackedRequest) tracked_requests; ++ CoQueue flush_queue; /* Serializing flush queue */ ++ bool active_flush_req; /* Flush request in flight? */ ++ ++ /* Only read/written by whoever has set active_flush_req to true. */ ++ unsigned int flushed_gen; /* Flushed write generation */ ++ ++ /* BdrvChild links to this node may never be frozen */ ++ bool never_freeze; ++ ++ /* Lock for block-status cache RCU writers */ ++ CoMutex bsc_modify_lock; ++ /* Always non-NULL, but must only be dereferenced under an RCU read guard */ ++ BdrvBlockStatusCache *block_status_cache; ++ ++ /* array of write pointers' location of each zone in the zoned device. */ ++ BlockZoneWps *wps; ++}; ++ ++struct BlockBackendRootState { ++ int open_flags; ++ BlockdevDetectZeroesOptions detect_zeroes; ++}; ++ ++typedef enum BlockMirrorBackingMode { ++ /* ++ * Reuse the existing backing chain from the source for the target. ++ * - sync=full: Set backing BDS to NULL. ++ * - sync=top: Use source's backing BDS. ++ * - sync=none: Use source as the backing BDS. ++ */ ++ MIRROR_SOURCE_BACKING_CHAIN, ++ ++ /* Open the target's backing chain completely anew */ ++ MIRROR_OPEN_BACKING_CHAIN, ++ ++ /* Do not change the target's backing BDS after job completion */ ++ MIRROR_LEAVE_BACKING_CHAIN, ++} BlockMirrorBackingMode; ++ ++ ++/* ++ * Essential block drivers which must always be statically linked into qemu, and ++ * which therefore can be accessed without using bdrv_find_format() ++ */ ++extern BlockDriver bdrv_file; ++extern BlockDriver bdrv_raw; ++extern BlockDriver bdrv_qcow2; ++ ++extern unsigned int bdrv_drain_all_count; ++extern QemuOptsList bdrv_create_opts_simple; ++ ++/* ++ * Common functions that are neither I/O nor Global State. ++ * ++ * See include/block/block-common.h for more information about ++ * the Common API. ++ */ ++ ++static inline BlockDriverState *child_bs(BdrvChild *child) ++{ ++ return child ? child->bs : NULL; ++} ++ ++int bdrv_check_request(int64_t offset, int64_t bytes, Error **errp); ++char *create_tmp_file(Error **errp); ++void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix, ++ QDict *options); ++ ++ ++int bdrv_check_qiov_request(int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ Error **errp); ++ ++#ifdef _WIN32 ++int is_windows_drive(const char *filename); ++#endif ++ ++#endif /* BLOCK_INT_COMMON_H */ +diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h +new file mode 100644 +index 00000000..eb2d92a2 +--- /dev/null ++++ b/include/block/block_int-global-state.h +@@ -0,0 +1,326 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCK_INT_GLOBAL_STATE_H ++#define BLOCK_INT_GLOBAL_STATE_H ++ ++#include "block/blockjob.h" ++#include "block/block_int-common.h" ++#include "qemu/hbitmap.h" ++#include "qemu/main-loop.h" ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++/** ++ * stream_start: ++ * @job_id: The id of the newly-created job, or %NULL to use the ++ * device name of @bs. ++ * @bs: Block device to operate on. ++ * @base: Block device that will become the new base, or %NULL to ++ * flatten the whole backing file chain onto @bs. ++ * @backing_file_str: The file name that will be written to @bs as the ++ * the new backing file if the job completes. Ignored if @base is %NULL. ++ * @backing_mask_protocol: Replace potential protocol name with 'raw' in ++ * 'backing file format' header ++ * @creation_flags: Flags that control the behavior of the Job lifetime. ++ * See @BlockJobCreateFlags ++ * @speed: The maximum speed, in bytes per second, or 0 for unlimited. ++ * @on_error: The action to take upon error. ++ * @filter_node_name: The node name that should be assigned to the filter ++ * driver that the stream job inserts into the graph above ++ * @bs. NULL means that a node name should be autogenerated. ++ * @errp: Error object. ++ * ++ * Start a streaming operation on @bs. Clusters that are unallocated ++ * in @bs, but allocated in any image between @base and @bs (both ++ * exclusive) will be written to @bs. At the end of a successful ++ * streaming job, the backing file of @bs will be changed to ++ * @backing_file_str in the written image and to @base in the live ++ * BlockDriverState. ++ */ ++void stream_start(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *base, const char *backing_file_str, ++ bool backing_mask_protocol, ++ BlockDriverState *bottom, ++ int creation_flags, int64_t speed, ++ BlockdevOnError on_error, ++ const char *filter_node_name, ++ Error **errp); ++ ++/** ++ * commit_start: ++ * @job_id: The id of the newly-created job, or %NULL to use the ++ * device name of @bs. ++ * @bs: Active block device. ++ * @top: Top block device to be committed. ++ * @base: Block device that will be written into, and become the new top. ++ * @creation_flags: Flags that control the behavior of the Job lifetime. ++ * See @BlockJobCreateFlags ++ * @speed: The maximum speed, in bytes per second, or 0 for unlimited. ++ * @on_error: The action to take upon error. ++ * @backing_file_str: String to use as the backing file in @top's overlay ++ * @backing_mask_protocol: Replace potential protocol name with 'raw' in ++ * 'backing file format' header ++ * @filter_node_name: The node name that should be assigned to the filter ++ * driver that the commit job inserts into the graph above @top. NULL means ++ * that a node name should be autogenerated. ++ * @errp: Error object. ++ * ++ */ ++void commit_start(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *base, BlockDriverState *top, ++ int creation_flags, int64_t speed, ++ BlockdevOnError on_error, const char *backing_file_str, ++ bool backing_mask_protocol, ++ const char *filter_node_name, Error **errp); ++/** ++ * commit_active_start: ++ * @job_id: The id of the newly-created job, or %NULL to use the ++ * device name of @bs. ++ * @bs: Active block device to be committed. ++ * @base: Block device that will be written into, and become the new top. ++ * @creation_flags: Flags that control the behavior of the Job lifetime. ++ * See @BlockJobCreateFlags ++ * @speed: The maximum speed, in bytes per second, or 0 for unlimited. ++ * @on_error: The action to take upon error. ++ * @filter_node_name: The node name that should be assigned to the filter ++ * driver that the commit job inserts into the graph above @bs. NULL means that ++ * a node name should be autogenerated. ++ * @cb: Completion function for the job. ++ * @opaque: Opaque pointer value passed to @cb. ++ * @auto_complete: Auto complete the job. ++ * @errp: Error object. ++ * ++ */ ++BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *base, int creation_flags, ++ int64_t speed, BlockdevOnError on_error, ++ const char *filter_node_name, ++ BlockCompletionFunc *cb, void *opaque, ++ bool auto_complete, Error **errp); ++/* ++ * mirror_start: ++ * @job_id: The id of the newly-created job, or %NULL to use the ++ * device name of @bs. ++ * @bs: Block device to operate on. ++ * @target: Block device to write to. ++ * @replaces: Block graph node name to replace once the mirror is done. Can ++ * only be used when full mirroring is selected. ++ * @creation_flags: Flags that control the behavior of the Job lifetime. ++ * See @BlockJobCreateFlags ++ * @speed: The maximum speed, in bytes per second, or 0 for unlimited. ++ * @granularity: The chosen granularity for the dirty bitmap. ++ * @buf_size: The amount of data that can be in flight at one time. ++ * @mode: Whether to collapse all images in the chain to the target. ++ * @backing_mode: How to establish the target's backing chain after completion. ++ * @zero_target: Whether the target should be explicitly zero-initialized ++ * @on_source_error: The action to take upon error reading from the source. ++ * @on_target_error: The action to take upon error writing to the target. ++ * @unmap: Whether to unmap target where source sectors only contain zeroes. ++ * @filter_node_name: The node name that should be assigned to the filter ++ * driver that the mirror job inserts into the graph above @bs. NULL means that ++ * a node name should be autogenerated. ++ * @copy_mode: When to trigger writes to the target. ++ * @errp: Error object. ++ * ++ * Start a mirroring operation on @bs. Clusters that are allocated ++ * in @bs will be written to @target until the job is cancelled or ++ * manually completed. At the end of a successful mirroring job, ++ * @bs will be switched to read from @target. ++ */ ++void mirror_start(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *target, const char *replaces, ++ int creation_flags, int64_t speed, ++ uint32_t granularity, int64_t buf_size, ++ MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, ++ bool zero_target, ++ BlockdevOnError on_source_error, ++ BlockdevOnError on_target_error, ++ bool unmap, const char *filter_node_name, ++ MirrorCopyMode copy_mode, Error **errp); ++ ++/* ++ * backup_job_create: ++ * @job_id: The id of the newly-created job, or %NULL to use the ++ * device name of @bs. ++ * @bs: Block device to operate on. ++ * @target: Block device to write to. ++ * @speed: The maximum speed, in bytes per second, or 0 for unlimited. ++ * @sync_mode: What parts of the disk image should be copied to the destination. ++ * @sync_bitmap: The dirty bitmap if sync_mode is 'bitmap' or 'incremental' ++ * @bitmap_mode: The bitmap synchronization policy to use. ++ * @perf: Performance options. All actual fields assumed to be present, ++ * all ".has_*" fields are ignored. ++ * @on_source_error: The action to take upon error reading from the source. ++ * @on_target_error: The action to take upon error writing to the target. ++ * @creation_flags: Flags that control the behavior of the Job lifetime. ++ * See @BlockJobCreateFlags ++ * @cb: Completion function for the job. ++ * @opaque: Opaque pointer value passed to @cb. ++ * @txn: Transaction that this job is part of (may be NULL). ++ * ++ * Create a backup operation on @bs. Clusters in @bs are written to @target ++ * until the job is cancelled or manually completed. ++ */ ++BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *target, int64_t speed, ++ MirrorSyncMode sync_mode, ++ BdrvDirtyBitmap *sync_bitmap, ++ BitmapSyncMode bitmap_mode, ++ bool compress, bool discard_source, ++ const char *filter_node_name, ++ BackupPerf *perf, ++ BlockdevOnError on_source_error, ++ BlockdevOnError on_target_error, ++ int creation_flags, ++ BlockCompletionFunc *cb, void *opaque, ++ JobTxn *txn, Error **errp); ++ ++BdrvChild * GRAPH_WRLOCK ++bdrv_root_attach_child(BlockDriverState *child_bs, const char *child_name, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ uint64_t perm, uint64_t shared_perm, ++ void *opaque, Error **errp); ++ ++void GRAPH_WRLOCK bdrv_root_unref_child(BdrvChild *child); ++ ++void GRAPH_RDLOCK bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, ++ uint64_t *shared_perm); ++ ++/** ++ * Sets a BdrvChild's permissions. Avoid if the parent is a BDS; use ++ * bdrv_child_refresh_perms() instead and make the parent's ++ * .bdrv_child_perm() implementation return the correct values. ++ */ ++int GRAPH_RDLOCK ++bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, ++ Error **errp); ++ ++/** ++ * Calls bs->drv->bdrv_child_perm() and updates the child's permission ++ * masks with the result. ++ * Drivers should invoke this function whenever an event occurs that ++ * makes their .bdrv_child_perm() implementation return different ++ * values than before, but which will not result in the block layer ++ * automatically refreshing the permissions. ++ */ ++int GRAPH_RDLOCK ++bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp); ++ ++bool GRAPH_RDLOCK bdrv_recurse_can_replace(BlockDriverState *bs, ++ BlockDriverState *to_replace); ++ ++/* ++ * Default implementation for BlockDriver.bdrv_child_perm() that can ++ * be used by block filters and image formats, as long as they use the ++ * child_of_bds child class and set an appropriate BdrvChildRole. ++ */ ++void bdrv_default_perms(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared); ++ ++void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp); ++bool blk_dev_has_removable_media(BlockBackend *blk); ++void blk_dev_eject_request(BlockBackend *blk, bool force); ++bool blk_dev_is_medium_locked(BlockBackend *blk); ++ ++void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup); ++ ++void bdrv_set_monitor_owned(BlockDriverState *bs); ++ ++void blockdev_close_all_bdrv_states(void); ++ ++BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp); ++ ++/** ++ * Simple implementation of bdrv_co_create_opts for protocol drivers ++ * which only support creation via opening a file ++ * (usually existing raw storage device) ++ */ ++int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, ++ const char *filename, ++ QemuOpts *opts, ++ Error **errp); ++ ++BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, ++ const char *name, ++ BlockDriverState **pbs, ++ Error **errp); ++BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, ++ BlockDirtyBitmapOrStrList *bms, ++ HBitmap **backup, Error **errp); ++BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, ++ bool release, ++ BlockDriverState **bitmap_bs, ++ Error **errp); ++ ++ ++BlockDriverState * GRAPH_RDLOCK ++bdrv_skip_implicit_filters(BlockDriverState *bs); ++ ++/** ++ * bdrv_add_aio_context_notifier: ++ * ++ * If a long-running job intends to be always run in the same AioContext as a ++ * certain BDS, it may use this function to be notified of changes regarding the ++ * association of the BDS to an AioContext. ++ * ++ * attached_aio_context() is called after the target BDS has been attached to a ++ * new AioContext; detach_aio_context() is called before the target BDS is being ++ * detached from its old AioContext. ++ */ ++void bdrv_add_aio_context_notifier(BlockDriverState *bs, ++ void (*attached_aio_context)(AioContext *new_context, void *opaque), ++ void (*detach_aio_context)(void *opaque), void *opaque); ++ ++/** ++ * bdrv_remove_aio_context_notifier: ++ * ++ * Unsubscribe of change notifications regarding the BDS's AioContext. The ++ * parameters given here have to be the same as those given to ++ * bdrv_add_aio_context_notifier(). ++ */ ++void bdrv_remove_aio_context_notifier(BlockDriverState *bs, ++ void (*aio_context_attached)(AioContext *, ++ void *), ++ void (*aio_context_detached)(void *), ++ void *opaque); ++ ++/** ++ * End all quiescent sections started by bdrv_drain_all_begin(). This is ++ * needed when deleting a BDS before bdrv_drain_all_end() is called. ++ * ++ * NOTE: this is an internal helper for bdrv_close() *only*. No one else ++ * should call it. ++ */ ++void bdrv_drain_all_end_quiesce(BlockDriverState *bs); ++ ++#endif /* BLOCK_INT_GLOBAL_STATE_H */ +diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h +new file mode 100644 +index 00000000..4a7cf2b4 +--- /dev/null ++++ b/include/block/block_int-io.h +@@ -0,0 +1,194 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_INT_IO_H ++#define BLOCK_INT_IO_H ++ ++#include "block/block_int-common.h" ++#include "qemu/hbitmap.h" ++#include "qemu/main-loop.h" ++ ++/* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++int coroutine_fn GRAPH_RDLOCK bdrv_co_preadv_snapshot(BdrvChild *child, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_snapshot_block_status( ++ BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, BlockDriverState **file); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_pdiscard_snapshot(BlockDriverState *bs, ++ int64_t offset, int64_t bytes); ++ ++ ++int coroutine_fn GRAPH_RDLOCK bdrv_co_preadv(BdrvChild *child, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_preadv_part(BdrvChild *child, ++ int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_pwritev(BdrvChild *child, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_pwritev_part(BdrvChild *child, ++ int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); ++ ++static inline int coroutine_fn GRAPH_RDLOCK bdrv_co_pread(BdrvChild *child, ++ int64_t offset, int64_t bytes, void *buf, BdrvRequestFlags flags) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ return bdrv_co_preadv(child, offset, bytes, &qiov, flags); ++} ++ ++static inline int coroutine_fn GRAPH_RDLOCK bdrv_co_pwrite(BdrvChild *child, ++ int64_t offset, int64_t bytes, const void *buf, BdrvRequestFlags flags) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ return bdrv_co_pwritev(child, offset, bytes, &qiov, flags); ++} ++ ++void coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, ++ uint64_t align); ++BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs); ++ ++BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, ++ const char *filename); ++ ++/** ++ * bdrv_wakeup: ++ * @bs: The BlockDriverState for which an I/O operation has been completed. ++ * ++ * Wake up the main thread if it is waiting on BDRV_POLL_WHILE. During ++ * synchronous I/O on a BlockDriverState that is attached to another ++ * I/O thread, the main thread lets the I/O thread's event loop run, ++ * waiting for the I/O operation to complete. A bdrv_wakeup will wake ++ * up the main thread if necessary. ++ * ++ * Manual calls to bdrv_wakeup are rarely necessary, because ++ * bdrv_dec_in_flight already calls it. ++ */ ++void bdrv_wakeup(BlockDriverState *bs); ++ ++const char * GRAPH_RDLOCK bdrv_get_parent_name(const BlockDriverState *bs); ++bool blk_dev_has_tray(BlockBackend *blk); ++bool blk_dev_is_tray_open(BlockBackend *blk); ++ ++void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes); ++ ++void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); ++void bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, ++ const BdrvDirtyBitmap *src, ++ HBitmap **backup, bool lock); ++ ++void bdrv_inc_in_flight(BlockDriverState *bs); ++void bdrv_dec_in_flight(BlockDriverState *bs); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags); ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_refresh_total_sectors(BlockDriverState *bs, int64_t hint); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint); ++ ++BdrvChild * GRAPH_RDLOCK bdrv_cow_child(BlockDriverState *bs); ++BdrvChild * GRAPH_RDLOCK bdrv_filter_child(BlockDriverState *bs); ++BdrvChild * GRAPH_RDLOCK bdrv_filter_or_cow_child(BlockDriverState *bs); ++BdrvChild * GRAPH_RDLOCK bdrv_primary_child(BlockDriverState *bs); ++BlockDriverState * GRAPH_RDLOCK bdrv_skip_filters(BlockDriverState *bs); ++BlockDriverState * GRAPH_RDLOCK bdrv_backing_chain_next(BlockDriverState *bs); ++ ++static inline BlockDriverState * GRAPH_RDLOCK ++bdrv_cow_bs(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return child_bs(bdrv_cow_child(bs)); ++} ++ ++static inline BlockDriverState * GRAPH_RDLOCK ++bdrv_filter_bs(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return child_bs(bdrv_filter_child(bs)); ++} ++ ++static inline BlockDriverState * GRAPH_RDLOCK ++bdrv_filter_or_cow_bs(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return child_bs(bdrv_filter_or_cow_child(bs)); ++} ++ ++static inline BlockDriverState * GRAPH_RDLOCK ++bdrv_primary_bs(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return child_bs(bdrv_primary_child(bs)); ++} ++ ++/** ++ * Check whether the given offset is in the cached block-status data ++ * region. ++ * ++ * If it is, and @pnum is not NULL, *pnum is set to ++ * `bsc.data_end - offset`, i.e. how many bytes, starting from ++ * @offset, are data (according to the cache). ++ * Otherwise, *pnum is not touched. ++ */ ++bool bdrv_bsc_is_data(BlockDriverState *bs, int64_t offset, int64_t *pnum); ++ ++/** ++ * If [offset, offset + bytes) overlaps with the currently cached ++ * block-status region, invalidate the cache. ++ * ++ * (To be used by I/O paths that cause data regions to be zero or ++ * holes.) ++ */ ++void bdrv_bsc_invalidate_range(BlockDriverState *bs, ++ int64_t offset, int64_t bytes); ++ ++/** ++ * Mark the range [offset, offset + bytes) as a data region. ++ */ ++void bdrv_bsc_fill(BlockDriverState *bs, int64_t offset, int64_t bytes); ++ ++#endif /* BLOCK_INT_IO_H */ +diff --git a/include/block/block_int.h b/include/block/block_int.h +new file mode 100644 +index 00000000..567a178e +--- /dev/null ++++ b/include/block/block_int.h +@@ -0,0 +1,33 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_INT_H ++#define BLOCK_INT_H ++ ++#include "block/block_int-global-state.h" ++#include "block/block_int-io.h" ++#include "block/graph-lock.h" ++ ++/* DO NOT ADD ANYTHING IN HERE. USE ONE OF THE HEADERS INCLUDED ABOVE */ ++ ++#endif /* BLOCK_INT_H */ +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +new file mode 100644 +index 00000000..7061ab72 +--- /dev/null ++++ b/include/block/blockjob.h +@@ -0,0 +1,238 @@ ++/* ++ * Declarations for long-running block device operations ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCKJOB_H ++#define BLOCKJOB_H ++ ++#include "qapi/qapi-types-block-core.h" ++#include "qemu/job.h" ++#include "qemu/ratelimit.h" ++ ++#define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */ ++ ++typedef struct BlockJobDriver BlockJobDriver; ++ ++/** ++ * BlockJob: ++ * ++ * Long-running operation on a BlockDriverState. ++ */ ++typedef struct BlockJob { ++ /** ++ * Data belonging to the generic Job infrastructure. ++ * Protected by job mutex. ++ */ ++ Job job; ++ ++ /** ++ * Status that is published by the query-block-jobs QMP API. ++ * Protected by job mutex. ++ */ ++ BlockDeviceIoStatus iostatus; ++ ++ /** ++ * Speed that was set with @block_job_set_speed. ++ * Always modified and read under the BQL (GLOBAL_STATE_CODE). ++ */ ++ int64_t speed; ++ ++ /** ++ * Rate limiting data structure for implementing @speed. ++ * RateLimit API is thread-safe. ++ */ ++ RateLimit limit; ++ ++ /** ++ * Block other operations when block job is running. ++ * Always modified and read under the BQL (GLOBAL_STATE_CODE). ++ */ ++ Error *blocker; ++ ++ /** All notifiers are set once in block_job_create() and never modified. */ ++ ++ /** Called when a cancelled job is finalised. */ ++ Notifier finalize_cancelled_notifier; ++ ++ /** Called when a successfully completed job is finalised. */ ++ Notifier finalize_completed_notifier; ++ ++ /** Called when the job transitions to PENDING */ ++ Notifier pending_notifier; ++ ++ /** Called when the job transitions to READY */ ++ Notifier ready_notifier; ++ ++ /** Called when the job coroutine yields or terminates */ ++ Notifier idle_notifier; ++ ++ /** ++ * BlockDriverStates that are involved in this block job. ++ * Always modified and read under the BQL (GLOBAL_STATE_CODE). ++ */ ++ GSList *nodes; ++} BlockJob; ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++/** ++ * block_job_next_locked: ++ * @job: A block job, or %NULL. ++ * ++ * Get the next element from the list of block jobs after @job, or the ++ * first one if @job is %NULL. ++ * ++ * Returns the requested job, or %NULL if there are no more jobs left. ++ * Called with job lock held. ++ */ ++BlockJob *block_job_next_locked(BlockJob *job); ++ ++/** ++ * block_job_get: ++ * @id: The id of the block job. ++ * ++ * Get the block job identified by @id (which must not be %NULL). ++ * ++ * Returns the requested job, or %NULL if it doesn't exist. ++ * Called with job lock *not* held. ++ */ ++BlockJob *block_job_get(const char *id); ++ ++/* Same as block_job_get(), but called with job lock held. */ ++BlockJob *block_job_get_locked(const char *id); ++ ++/** ++ * block_job_add_bdrv: ++ * @job: A block job ++ * @name: The name to assign to the new BdrvChild ++ * @bs: A BlockDriverState that is involved in @job ++ * @perm, @shared_perm: Permissions to request on the node ++ * ++ * Add @bs to the list of BlockDriverState that are involved in ++ * @job. This means that all operations will be blocked on @bs while ++ * @job exists. ++ */ ++int GRAPH_WRLOCK ++block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, ++ uint64_t perm, uint64_t shared_perm, Error **errp); ++ ++/** ++ * block_job_remove_all_bdrv: ++ * @job: The block job ++ * ++ * Remove all BlockDriverStates from the list of nodes that are involved in the ++ * job. This removes the blockers added with block_job_add_bdrv(). ++ */ ++void block_job_remove_all_bdrv(BlockJob *job); ++ ++/** ++ * block_job_has_bdrv: ++ * @job: The block job ++ * ++ * Searches for @bs in the list of nodes that are involved in the ++ * job. ++ */ ++bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs); ++ ++/** ++ * block_job_set_speed_locked: ++ * @job: The job to set the speed for. ++ * @speed: The new value ++ * @errp: Error object. ++ * ++ * Set a rate-limiting parameter for the job; the actual meaning may ++ * vary depending on the job type. ++ * ++ * Called with job lock held, but might release it temporarily. ++ */ ++bool block_job_set_speed_locked(BlockJob *job, int64_t speed, Error **errp); ++ ++/** ++ * block_job_change_locked: ++ * @job: The job to change. ++ * @opts: The new options. ++ * @errp: Error object. ++ * ++ * Change the job according to opts. ++ */ ++void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, ++ Error **errp); ++ ++/** ++ * block_job_query_locked: ++ * @job: The job to get information about. ++ * ++ * Return information about a job. ++ * ++ * Called with job lock held. ++ */ ++BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp); ++ ++/** ++ * block_job_iostatus_reset_locked: ++ * @job: The job whose I/O status should be reset. ++ * ++ * Reset I/O status on @job and on BlockDriverState objects it uses, ++ * other than job->blk. ++ * ++ * Called with job lock held. ++ */ ++void block_job_iostatus_reset_locked(BlockJob *job); ++ ++/* ++ * block_job_get_aio_context: ++ * ++ * Returns aio context associated with a block job. ++ */ ++AioContext *block_job_get_aio_context(BlockJob *job); ++ ++ ++/* ++ * Common functions that are neither I/O nor Global State. ++ * ++ * See include/block/block-common.h for more information about ++ * the Common API. ++ */ ++ ++/** ++ * block_job_is_internal: ++ * @job: The job to determine if it is user-visible or not. ++ * ++ * Returns true if the job should not be visible to the management layer. ++ */ ++bool block_job_is_internal(BlockJob *job); ++ ++/** ++ * block_job_driver: ++ * ++ * Returns the driver associated with a block job. ++ */ ++const BlockJobDriver *block_job_driver(BlockJob *job); ++ ++#endif +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +new file mode 100644 +index 00000000..4c3d2e25 +--- /dev/null ++++ b/include/block/blockjob_int.h +@@ -0,0 +1,168 @@ ++/* ++ * Declarations for long-running block device operations ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCKJOB_INT_H ++#define BLOCKJOB_INT_H ++ ++#include "block/blockjob.h" ++ ++/** ++ * BlockJobDriver: ++ * ++ * A class type for block job driver. ++ */ ++struct BlockJobDriver { ++ /** Generic JobDriver callbacks and settings */ ++ JobDriver job_driver; ++ ++ /* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++ /* ++ * Returns whether the job has pending requests for the child or will ++ * submit new requests before the next pause point. This callback is polled ++ * in the context of draining a job node after requesting that the job be ++ * paused, until all activity on the child has stopped. ++ */ ++ bool (*drained_poll)(BlockJob *job); ++ ++ /* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++ /* ++ * If the callback is not NULL, it will be invoked before the job is ++ * resumed in a new AioContext. This is the place to move any resources ++ * besides job->blk to the new AioContext. ++ */ ++ void (*attached_aio_context)(BlockJob *job, AioContext *new_context); ++ ++ void (*set_speed)(BlockJob *job, int64_t speed); ++ ++ /* ++ * Change the @job's options according to @opts. ++ * ++ * Note that this can already be called before the job coroutine is running. ++ */ ++ void (*change)(BlockJob *job, BlockJobChangeOptions *opts, Error **errp); ++ ++ /* ++ * Query information specific to this kind of block job. ++ */ ++ void (*query)(BlockJob *job, BlockJobInfo *info); ++}; ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++/** ++ * block_job_create: ++ * @job_id: The id of the newly-created job, or %NULL to have one ++ * generated automatically. ++ * @driver: The class object for the newly-created job. ++ * @txn: The transaction this job belongs to, if any. %NULL otherwise. ++ * @bs: The block ++ * @perm, @shared_perm: Permissions to request for @bs ++ * @speed: The maximum speed, in bytes per second, or 0 for unlimited. ++ * @flags: Creation flags for the Block Job. See @JobCreateFlags. ++ * @cb: Completion function for the job. ++ * @opaque: Opaque pointer value passed to @cb. ++ * @errp: Error object. ++ * ++ * Create a new long-running block device job and return it. The job ++ * will call @cb asynchronously when the job completes. Note that ++ * @bs may have been closed at the time the @cb it is called. If ++ * this is the case, the job may be reported as either cancelled or ++ * completed. ++ * ++ * This function is not part of the public job interface; it should be ++ * called from a wrapper that is specific to the job type. ++ */ ++void * GRAPH_UNLOCKED ++block_job_create(const char *job_id, const BlockJobDriver *driver, ++ JobTxn *txn, BlockDriverState *bs, uint64_t perm, ++ uint64_t shared_perm, int64_t speed, int flags, ++ BlockCompletionFunc *cb, void *opaque, Error **errp); ++ ++/** ++ * block_job_free: ++ * Callback to be used for JobDriver.free in all block jobs. Frees block job ++ * specific resources in @job. ++ */ ++void block_job_free(Job *job); ++ ++/** ++ * block_job_user_resume: ++ * Callback to be used for JobDriver.user_resume in all block jobs. Resets the ++ * iostatus when the user resumes @job. ++ */ ++void block_job_user_resume(Job *job); ++ ++/* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++/** ++ * block_job_ratelimit_processed_bytes: ++ * ++ * To be called after some work has been done. Adjusts the delay for the next ++ * request. See the documentation of ratelimit_calculate_delay() for details. ++ */ ++void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n); ++ ++/** ++ * Put the job to sleep (assuming that it wasn't canceled) to throttle it to the ++ * right speed according to its rate limiting. ++ */ ++void block_job_ratelimit_sleep(BlockJob *job); ++ ++/** ++ * block_job_error_action: ++ * @job: The job to signal an error for. ++ * @on_err: The error action setting. ++ * @is_read: Whether the operation was a read. ++ * @error: The error that was reported. ++ * ++ * Report an I/O error for a block job and possibly stop the VM. Return the ++ * action that was selected based on @on_err and @error. ++ */ ++BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, ++ int is_read, int error); ++ ++#endif +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +new file mode 100644 +index 00000000..fa956deb +--- /dev/null ++++ b/include/block/dirty-bitmap.h +@@ -0,0 +1,130 @@ ++#ifndef BLOCK_DIRTY_BITMAP_H ++#define BLOCK_DIRTY_BITMAP_H ++ ++#include "block/block-common.h" ++#include "qapi/qapi-types-block-core.h" ++#include "qemu/hbitmap.h" ++ ++typedef enum BitmapCheckFlags { ++ BDRV_BITMAP_BUSY = 1, ++ BDRV_BITMAP_RO = 2, ++ BDRV_BITMAP_INCONSISTENT = 4, ++} BitmapCheckFlags; ++ ++#define BDRV_BITMAP_DEFAULT (BDRV_BITMAP_BUSY | BDRV_BITMAP_RO | \ ++ BDRV_BITMAP_INCONSISTENT) ++#define BDRV_BITMAP_ALLOW_RO (BDRV_BITMAP_BUSY | BDRV_BITMAP_INCONSISTENT) ++ ++#define BDRV_BITMAP_MAX_NAME_SIZE 1023 ++ ++bool bdrv_supports_persistent_dirty_bitmap(BlockDriverState *bs); ++BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, ++ uint32_t granularity, ++ const char *name, ++ Error **errp); ++int bdrv_dirty_bitmap_create_successor(BdrvDirtyBitmap *bitmap, ++ Error **errp); ++BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BdrvDirtyBitmap *bitmap, ++ Error **errp); ++BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BdrvDirtyBitmap *bitmap, ++ Error **errp); ++void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap); ++BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, ++ const char *name); ++int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags, ++ Error **errp); ++void bdrv_release_dirty_bitmap(BdrvDirtyBitmap *bitmap); ++void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, ++ Error **errp); ++int co_wrapper_bdrv_rdlock ++bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, ++ Error **errp); ++ ++void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap); ++void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap); ++void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap); ++BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs); ++uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs); ++uint32_t bdrv_dirty_bitmap_granularity(const BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap); ++const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap); ++int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap); ++void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes); ++void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes); ++BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap); ++void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter); ++ ++uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap, ++ uint64_t offset, uint64_t bytes); ++uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap); ++uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size, ++ const BdrvDirtyBitmap *bitmap); ++void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap, ++ uint8_t *buf, uint64_t offset, ++ uint64_t bytes); ++void bdrv_dirty_bitmap_deserialize_part(BdrvDirtyBitmap *bitmap, ++ uint8_t *buf, uint64_t offset, ++ uint64_t bytes, bool finish); ++void bdrv_dirty_bitmap_deserialize_zeroes(BdrvDirtyBitmap *bitmap, ++ uint64_t offset, uint64_t bytes, ++ bool finish); ++void bdrv_dirty_bitmap_deserialize_ones(BdrvDirtyBitmap *bitmap, ++ uint64_t offset, uint64_t bytes, ++ bool finish); ++void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap); ++ ++void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value); ++void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, ++ bool persistent); ++void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap); ++void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy); ++bool bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, ++ HBitmap **backup, Error **errp); ++void bdrv_dirty_bitmap_skip_store(BdrvDirtyBitmap *bitmap, bool skip); ++bool bdrv_dirty_bitmap_get(BdrvDirtyBitmap *bitmap, int64_t offset); ++ ++/* Functions that require manual locking. */ ++void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap); ++void bdrv_dirty_bitmap_unlock(BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_get_locked(BdrvDirtyBitmap *bitmap, int64_t offset); ++void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes); ++void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes); ++int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter); ++void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t offset); ++int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); ++void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes); ++bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap); ++bool bdrv_has_readonly_bitmaps(BlockDriverState *bs); ++bool bdrv_has_named_bitmaps(BlockDriverState *bs); ++bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap); ++ ++BdrvDirtyBitmap *bdrv_dirty_bitmap_first(BlockDriverState *bs); ++BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BdrvDirtyBitmap *bitmap); ++#define FOR_EACH_DIRTY_BITMAP(bs, bitmap) \ ++for (bitmap = bdrv_dirty_bitmap_first(bs); bitmap; \ ++ bitmap = bdrv_dirty_bitmap_next(bitmap)) ++ ++char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp); ++int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset, ++ int64_t bytes); ++int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset, ++ int64_t bytes); ++bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, ++ int64_t start, int64_t end, int64_t max_dirty_count, ++ int64_t *dirty_start, int64_t *dirty_count); ++bool bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap, int64_t offset, ++ int64_t bytes, int64_t *count); ++BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, ++ Error **errp); ++ ++#endif +diff --git a/include/block/export.h b/include/block/export.h +new file mode 100644 +index 00000000..f2fe0f80 +--- /dev/null ++++ b/include/block/export.h +@@ -0,0 +1,91 @@ ++/* ++ * Declarations for block exports ++ * ++ * Copyright (c) 2012, 2020 Red Hat, Inc. ++ * ++ * Authors: ++ * Paolo Bonzini ++ * Kevin Wolf ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef BLOCK_EXPORT_H ++#define BLOCK_EXPORT_H ++ ++#include "qapi/qapi-types-block-export.h" ++#include "qemu/queue.h" ++ ++typedef struct BlockExport BlockExport; ++ ++typedef struct BlockExportDriver { ++ /* The export type that this driver services */ ++ BlockExportType type; ++ ++ /* ++ * The size of the driver-specific state that contains BlockExport as its ++ * first field. ++ */ ++ size_t instance_size; ++ ++ /* Creates and starts a new block export */ ++ int (*create)(BlockExport *, BlockExportOptions *, Error **); ++ ++ /* ++ * Frees a removed block export. This function is only called after all ++ * references have been dropped. ++ */ ++ void (*delete)(BlockExport *); ++ ++ /* ++ * Start to disconnect all clients and drop other references held ++ * internally by the export driver. When the function returns, there may ++ * still be active references while the export is in the process of ++ * shutting down. ++ */ ++ void (*request_shutdown)(BlockExport *); ++} BlockExportDriver; ++ ++struct BlockExport { ++ const BlockExportDriver *drv; ++ ++ /* Unique identifier for the export */ ++ char *id; ++ ++ /* ++ * Reference count for this block export. This includes strong references ++ * both from the owner (qemu-nbd or the monitor) and clients connected to ++ * the export. ++ * ++ * Use atomics to access this field. ++ */ ++ int refcount; ++ ++ /* ++ * True if one of the references in refcount belongs to the user. After the ++ * user has dropped their reference, they may not e.g. remove the same ++ * export a second time (which would decrease the refcount without having ++ * it incremented first). ++ */ ++ bool user_owned; ++ ++ /* The AioContext whose lock protects this BlockExport object. */ ++ AioContext *ctx; ++ ++ /* The block device to export */ ++ BlockBackend *blk; ++ ++ /* List entry for block_exports */ ++ QLIST_ENTRY(BlockExport) next; ++}; ++ ++BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp); ++BlockExport *blk_exp_find(const char *id); ++void blk_exp_ref(BlockExport *exp); ++void blk_exp_unref(BlockExport *exp); ++void blk_exp_request_shutdown(BlockExport *exp); ++void blk_exp_close_all(void); ++void blk_exp_close_all_type(BlockExportType type); ++ ++#endif +diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h +new file mode 100644 +index 00000000..dc8d9491 +--- /dev/null ++++ b/include/block/graph-lock.h +@@ -0,0 +1,285 @@ ++/* ++ * Graph lock: rwlock to protect block layer graph manipulations (add/remove ++ * edges and nodes) ++ * ++ * Copyright (c) 2022 Red Hat ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ */ ++#ifndef GRAPH_LOCK_H ++#define GRAPH_LOCK_H ++ ++#include "qemu/clang-tsa.h" ++ ++/** ++ * Graph Lock API ++ * This API provides a rwlock used to protect block layer ++ * graph modifications like edge (BdrvChild) and node (BlockDriverState) ++ * addition and removal. ++ * Currently we have 1 writer only, the Main loop, and many ++ * readers, mostly coroutines running in other AioContext thus other threads. ++ * ++ * We distinguish between writer (main loop, under BQL) that modifies the ++ * graph, and readers (all other coroutines running in various AioContext), ++ * that go through the graph edges, reading ++ * BlockDriverState ->parents and->children. ++ * ++ * The writer (main loop) has an "exclusive" access, so it first waits for ++ * current read to finish, and then prevents incoming ones from ++ * entering while it has the exclusive access. ++ * ++ * The readers (coroutines in multiple AioContext) are free to ++ * access the graph as long the writer is not modifying the graph. ++ * In case it is, they go in a CoQueue and sleep until the writer ++ * is done. ++ * ++ * If a coroutine changes AioContext, the counter in the original and new ++ * AioContext are left intact, since the writer does not care where is the ++ * reader, but only if there is one. ++ * As a result, some AioContexts might have a negative reader count, to ++ * balance the positive count of the AioContext that took the lock. ++ * This also means that when an AioContext is deleted it may have a nonzero ++ * reader count. In that case we transfer the count to a global shared counter ++ * so that the writer is always aware of all readers. ++ */ ++typedef struct BdrvGraphRWlock BdrvGraphRWlock; ++ ++/* Dummy lock object to use for Thread Safety Analysis (TSA) */ ++typedef struct TSA_CAPABILITY("mutex") BdrvGraphLock { ++} BdrvGraphLock; ++ ++extern BdrvGraphLock graph_lock; ++ ++/* ++ * clang doesn't check consistency in locking annotations between forward ++ * declarations and the function definition. Having the annotation on the ++ * definition, but not the declaration in a header file, may give the reader ++ * a false sense of security because the condition actually remains unchecked ++ * for callers in other source files. ++ * ++ * Therefore, as a convention, for public functions, GRAPH_RDLOCK and ++ * GRAPH_WRLOCK annotations should be present only in the header file. ++ */ ++#define GRAPH_WRLOCK TSA_REQUIRES(graph_lock) ++#define GRAPH_RDLOCK TSA_REQUIRES_SHARED(graph_lock) ++#define GRAPH_UNLOCKED TSA_EXCLUDES(graph_lock) ++ ++/* ++ * TSA annotations are not part of function types, so checks are defeated when ++ * using a function pointer. As a workaround, annotate function pointers with ++ * this macro that will require that the lock is at least taken while reading ++ * the pointer. In most cases this is equivalent to actually protecting the ++ * function call. ++ */ ++#define GRAPH_RDLOCK_PTR TSA_GUARDED_BY(graph_lock) ++#define GRAPH_WRLOCK_PTR TSA_GUARDED_BY(graph_lock) ++#define GRAPH_UNLOCKED_PTR ++ ++/* ++ * register_aiocontext: ++ * Add AioContext @ctx to the list of AioContext. ++ * This list is used to obtain the total number of readers ++ * currently running the graph. ++ */ ++void register_aiocontext(AioContext *ctx); ++ ++/* ++ * unregister_aiocontext: ++ * Removes AioContext @ctx to the list of AioContext. ++ */ ++void unregister_aiocontext(AioContext *ctx); ++ ++/* ++ * bdrv_graph_wrlock: ++ * Start an exclusive write operation to modify the graph. This means we are ++ * adding or removing an edge or a node in the block layer graph. Nobody else ++ * is allowed to access the graph. ++ * ++ * Must only be called from outside bdrv_graph_co_rdlock. ++ * ++ * The wrlock can only be taken from the main loop, with BQL held, as only the ++ * main loop is allowed to modify the graph. ++ */ ++void no_coroutine_fn TSA_ACQUIRE(graph_lock) TSA_NO_TSA ++bdrv_graph_wrlock(void); ++ ++/* ++ * bdrv_graph_wrunlock: ++ * Write finished, reset global has_writer to 0 and restart ++ * all readers that are waiting. ++ */ ++void no_coroutine_fn TSA_RELEASE(graph_lock) TSA_NO_TSA ++bdrv_graph_wrunlock(void); ++ ++/* ++ * bdrv_graph_co_rdlock: ++ * Read the bs graph. This usually means traversing all nodes in ++ * the graph, therefore it can't happen while another thread is ++ * modifying it. ++ * Increases the reader counter of the current aiocontext, ++ * and if has_writer is set, it means that the writer is modifying ++ * the graph, therefore wait in a coroutine queue. ++ * The writer will then wake this coroutine once it is done. ++ * ++ * This lock should be taken from Iothreads (IO_CODE() class of functions) ++ * because it signals the writer that there are some ++ * readers currently running, or waits until the current ++ * write is finished before continuing. ++ * Calling this function from the Main Loop with BQL held ++ * is not necessary, since the Main Loop itself is the only ++ * writer, thus won't be able to read and write at the same time. ++ * The only exception to that is when we can't take the lock in the ++ * function/coroutine itself, and need to delegate the caller (usually main ++ * loop) to take it and wait that the coroutine ends, so that ++ * we always signal that a reader is running. ++ */ ++void coroutine_fn TSA_ACQUIRE_SHARED(graph_lock) TSA_NO_TSA ++bdrv_graph_co_rdlock(void); ++ ++/* ++ * bdrv_graph_rdunlock: ++ * Read terminated, decrease the count of readers in the current aiocontext. ++ * If the writer is waiting for reads to finish (has_writer == 1), signal ++ * the writer that we are done via aio_wait_kick() to let it continue. ++ */ ++void coroutine_fn TSA_RELEASE_SHARED(graph_lock) TSA_NO_TSA ++bdrv_graph_co_rdunlock(void); ++ ++/* ++ * bdrv_graph_rd{un}lock_main_loop: ++ * Just a placeholder to mark where the graph rdlock should be taken ++ * in the main loop. It is just asserting that we are not ++ * in a coroutine and in GLOBAL_STATE_CODE. ++ */ ++void TSA_ACQUIRE_SHARED(graph_lock) TSA_NO_TSA ++bdrv_graph_rdlock_main_loop(void); ++ ++void TSA_RELEASE_SHARED(graph_lock) TSA_NO_TSA ++bdrv_graph_rdunlock_main_loop(void); ++ ++/* ++ * assert_bdrv_graph_readable: ++ * Make sure that the reader is either the main loop, ++ * or there is at least a reader helding the rdlock. ++ * In this way an incoming writer is aware of the read and waits. ++ */ ++void GRAPH_RDLOCK assert_bdrv_graph_readable(void); ++ ++/* ++ * assert_bdrv_graph_writable: ++ * Make sure that the writer is the main loop and has set @has_writer, ++ * so that incoming readers will pause. ++ */ ++void GRAPH_WRLOCK assert_bdrv_graph_writable(void); ++ ++/* ++ * Calling this function tells TSA that we know that the lock is effectively ++ * taken even though we cannot prove it (yet) with GRAPH_RDLOCK. This can be ++ * useful in intermediate stages of a conversion to using the GRAPH_RDLOCK ++ * macro. ++ */ ++static inline void TSA_ASSERT_SHARED(graph_lock) TSA_NO_TSA ++assume_graph_lock(void) ++{ ++} ++ ++typedef struct GraphLockable { } GraphLockable; ++ ++/* ++ * In C, compound literals have the lifetime of an automatic variable. ++ * In C++ it would be different, but then C++ wouldn't need QemuLockable ++ * either... ++ */ ++#define GML_OBJ_() (&(GraphLockable) { }) ++ ++/* ++ * This is not marked as TSA_ACQUIRE_SHARED() because TSA doesn't understand the ++ * cleanup attribute and would therefore complain that the graph is never ++ * unlocked. TSA_ASSERT_SHARED() makes sure that the following calls know that ++ * we hold the lock while unlocking is left unchecked. ++ */ ++static inline GraphLockable * TSA_ACQUIRE_SHARED(graph_lock) coroutine_fn ++graph_lockable_auto_lock(GraphLockable *x) ++{ ++ bdrv_graph_co_rdlock(); ++ return x; ++} ++ ++static inline void TSA_RELEASE_SHARED(graph_lock) coroutine_fn ++graph_lockable_auto_unlock(GraphLockable **x) ++{ ++ bdrv_graph_co_rdunlock(); ++} ++ ++#define GRAPH_AUTO_UNLOCK __attribute__((cleanup(graph_lockable_auto_unlock))) ++ ++/* ++ * @var is only used to break the loop after the first iteration. ++ * @unlock_var can't be unlocked and then set to NULL because TSA wants the lock ++ * to be held at the start of every iteration of the loop. ++ */ ++#define WITH_GRAPH_RDLOCK_GUARD_(var) \ ++ for (GraphLockable *unlock_var GRAPH_AUTO_UNLOCK = \ ++ graph_lockable_auto_lock(GML_OBJ_()), \ ++ *var = unlock_var; \ ++ var; \ ++ var = NULL) ++ ++#define WITH_GRAPH_RDLOCK_GUARD() \ ++ WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__)) ++ ++#define GRAPH_RDLOCK_GUARD(x) \ ++ GraphLockable * GRAPH_AUTO_UNLOCK \ ++ glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \ ++ graph_lockable_auto_lock(GML_OBJ_()) ++ ++ ++typedef struct GraphLockableMainloop { } GraphLockableMainloop; ++ ++/* ++ * In C, compound literals have the lifetime of an automatic variable. ++ * In C++ it would be different, but then C++ wouldn't need QemuLockable ++ * either... ++ */ ++#define GMLML_OBJ_() (&(GraphLockableMainloop) { }) ++ ++/* ++ * This is not marked as TSA_ACQUIRE_SHARED() because TSA doesn't understand the ++ * cleanup attribute and would therefore complain that the graph is never ++ * unlocked. TSA_ASSERT_SHARED() makes sure that the following calls know that ++ * we hold the lock while unlocking is left unchecked. ++ */ ++static inline GraphLockableMainloop * TSA_ASSERT_SHARED(graph_lock) TSA_NO_TSA ++graph_lockable_auto_lock_mainloop(GraphLockableMainloop *x) ++{ ++ bdrv_graph_rdlock_main_loop(); ++ return x; ++} ++ ++static inline void TSA_NO_TSA ++graph_lockable_auto_unlock_mainloop(GraphLockableMainloop *x) ++{ ++ bdrv_graph_rdunlock_main_loop(); ++} ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockableMainloop, ++ graph_lockable_auto_unlock_mainloop) ++ ++#define GRAPH_RDLOCK_GUARD_MAINLOOP(x) \ ++ g_autoptr(GraphLockableMainloop) \ ++ glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \ ++ graph_lockable_auto_lock_mainloop(GMLML_OBJ_()) ++ ++#endif /* GRAPH_LOCK_H */ ++ +diff --git a/include/block/module_block.h b/include/block/module_block.h +new file mode 100644 +index 00000000..6b09256b +--- /dev/null ++++ b/include/block/module_block.h +@@ -0,0 +1,19 @@ ++/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ ++/* ++ * QEMU Block Module Infrastructure ++ * ++ * Authors: ++ * Marc Mari ++ */ ++ ++#ifndef QEMU_MODULE_BLOCK_H ++#define QEMU_MODULE_BLOCK_H ++ ++static const struct { ++ const char *format_name; ++ const char *protocol_name; ++ const char *library_name; ++} block_driver_modules[] = { ++}; ++ ++#endif +diff --git a/include/block/nbd.h b/include/block/nbd.h +new file mode 100644 +index 00000000..d4f8b21a +--- /dev/null ++++ b/include/block/nbd.h +@@ -0,0 +1,509 @@ ++/* ++ * Copyright Red Hat ++ * Copyright (C) 2005 Anthony Liguori ++ * ++ * Network Block Device ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; under version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see . ++ */ ++ ++#ifndef NBD_H ++#define NBD_H ++ ++#include "block/export.h" ++#include "io/channel-socket.h" ++#include "crypto/tlscreds.h" ++#include "qapi/error.h" ++#include "qemu/bswap.h" ++ ++typedef struct NBDExport NBDExport; ++typedef struct NBDClient NBDClient; ++typedef struct NBDClientConnection NBDClientConnection; ++typedef struct NBDMetaContexts NBDMetaContexts; ++ ++extern const BlockExportDriver blk_exp_nbd; ++ ++/* ++ * NBD_DEFAULT_HANDSHAKE_MAX_SECS: Number of seconds in which client must ++ * succeed at NBD_OPT_GO before being forcefully dropped as too slow. ++ */ ++#define NBD_DEFAULT_HANDSHAKE_MAX_SECS 10 ++ ++/* ++ * NBD_DEFAULT_MAX_CONNECTIONS: Number of client sockets to allow at ++ * once; must be large enough to allow a MULTI_CONN-aware client like ++ * nbdcopy to create its typical number of 8-16 sockets. ++ */ ++#define NBD_DEFAULT_MAX_CONNECTIONS 100 ++ ++/* Handshake phase structs - this struct is passed on the wire */ ++ ++typedef struct NBDOption { ++ uint64_t magic; /* NBD_OPTS_MAGIC */ ++ uint32_t option; /* NBD_OPT_* */ ++ uint32_t length; ++} QEMU_PACKED NBDOption; ++ ++typedef struct NBDOptionReply { ++ uint64_t magic; /* NBD_REP_MAGIC */ ++ uint32_t option; /* NBD_OPT_* */ ++ uint32_t type; /* NBD_REP_* */ ++ uint32_t length; ++} QEMU_PACKED NBDOptionReply; ++ ++typedef struct NBDOptionReplyMetaContext { ++ NBDOptionReply h; /* h.type = NBD_REP_META_CONTEXT, h.length > 4 */ ++ uint32_t context_id; ++ /* metadata context name follows */ ++} QEMU_PACKED NBDOptionReplyMetaContext; ++ ++/* Track results of negotiation */ ++typedef enum NBDMode { ++ /* Keep this list in a continuum of increasing features. */ ++ NBD_MODE_OLDSTYLE, /* server lacks newstyle negotiation */ ++ NBD_MODE_EXPORT_NAME, /* newstyle but only OPT_EXPORT_NAME safe */ ++ NBD_MODE_SIMPLE, /* newstyle but only simple replies */ ++ NBD_MODE_STRUCTURED, /* newstyle, structured replies enabled */ ++ NBD_MODE_EXTENDED, /* newstyle, extended headers enabled */ ++} NBDMode; ++ ++/* Transmission phase structs */ ++ ++/* ++ * Note: NBDRequest is _NOT_ the same as the network representation of an NBD ++ * request! ++ */ ++typedef struct NBDRequest { ++ uint64_t cookie; ++ uint64_t from; /* Offset touched by the command */ ++ uint64_t len; /* Effect length; 32 bit limit without extended headers */ ++ uint16_t flags; /* NBD_CMD_FLAG_* */ ++ uint16_t type; /* NBD_CMD_* */ ++ NBDMode mode; /* Determines which network representation to use */ ++ NBDMetaContexts *contexts; /* Used by NBD_CMD_BLOCK_STATUS */ ++} NBDRequest; ++ ++typedef struct NBDSimpleReply { ++ uint32_t magic; /* NBD_SIMPLE_REPLY_MAGIC */ ++ uint32_t error; ++ uint64_t cookie; ++} QEMU_PACKED NBDSimpleReply; ++ ++/* Header of all structured replies */ ++typedef struct NBDStructuredReplyChunk { ++ uint32_t magic; /* NBD_STRUCTURED_REPLY_MAGIC */ ++ uint16_t flags; /* combination of NBD_REPLY_FLAG_* */ ++ uint16_t type; /* NBD_REPLY_TYPE_* */ ++ uint64_t cookie; /* request handle */ ++ uint32_t length; /* length of payload */ ++} QEMU_PACKED NBDStructuredReplyChunk; ++ ++typedef struct NBDExtendedReplyChunk { ++ uint32_t magic; /* NBD_EXTENDED_REPLY_MAGIC */ ++ uint16_t flags; /* combination of NBD_REPLY_FLAG_* */ ++ uint16_t type; /* NBD_REPLY_TYPE_* */ ++ uint64_t cookie; /* request handle */ ++ uint64_t offset; /* request offset */ ++ uint64_t length; /* length of payload */ ++} QEMU_PACKED NBDExtendedReplyChunk; ++ ++typedef union NBDReply { ++ NBDSimpleReply simple; ++ NBDStructuredReplyChunk structured; ++ NBDExtendedReplyChunk extended; ++ struct { ++ /* ++ * @magic and @cookie fields have the same offset and size in all ++ * forms of replies, so let them be accessible without ".simple.", ++ * ".structured.", or ".extended." specifications. ++ */ ++ uint32_t magic; ++ uint32_t _skip; ++ uint64_t cookie; ++ }; ++} NBDReply; ++QEMU_BUILD_BUG_ON(offsetof(NBDReply, simple.cookie) != ++ offsetof(NBDReply, cookie)); ++QEMU_BUILD_BUG_ON(offsetof(NBDReply, structured.cookie) != ++ offsetof(NBDReply, cookie)); ++QEMU_BUILD_BUG_ON(offsetof(NBDReply, extended.cookie) != ++ offsetof(NBDReply, cookie)); ++ ++/* Header of chunk for NBD_REPLY_TYPE_OFFSET_DATA */ ++typedef struct NBDStructuredReadData { ++ /* header's .length >= 9 */ ++ uint64_t offset; ++ /* At least one byte of data payload follows, calculated from h.length */ ++} QEMU_PACKED NBDStructuredReadData; ++ ++/* Complete chunk for NBD_REPLY_TYPE_OFFSET_HOLE */ ++typedef struct NBDStructuredReadHole { ++ /* header's length == 12 */ ++ uint64_t offset; ++ uint32_t length; ++} QEMU_PACKED NBDStructuredReadHole; ++ ++/* Header of all NBD_REPLY_TYPE_ERROR* errors */ ++typedef struct NBDStructuredError { ++ /* header's length >= 6 */ ++ uint32_t error; ++ uint16_t message_length; ++} QEMU_PACKED NBDStructuredError; ++ ++/* Header of NBD_REPLY_TYPE_BLOCK_STATUS */ ++typedef struct NBDStructuredMeta { ++ /* header's length >= 12 (at least one extent) */ ++ uint32_t context_id; ++ /* NBDExtent32 extents[] follows, array length implied by header */ ++} QEMU_PACKED NBDStructuredMeta; ++ ++/* Extent array element for NBD_REPLY_TYPE_BLOCK_STATUS */ ++typedef struct NBDExtent32 { ++ uint32_t length; ++ uint32_t flags; /* NBD_STATE_* */ ++} QEMU_PACKED NBDExtent32; ++ ++/* Header of NBD_REPLY_TYPE_BLOCK_STATUS_EXT */ ++typedef struct NBDExtendedMeta { ++ /* header's length >= 24 (at least one extent) */ ++ uint32_t context_id; ++ uint32_t count; /* header length must be count * 16 + 8 */ ++ /* NBDExtent64 extents[count] follows */ ++} QEMU_PACKED NBDExtendedMeta; ++ ++/* Extent array element for NBD_REPLY_TYPE_BLOCK_STATUS_EXT */ ++typedef struct NBDExtent64 { ++ uint64_t length; ++ uint64_t flags; /* NBD_STATE_* */ ++} QEMU_PACKED NBDExtent64; ++ ++/* Client payload for limiting NBD_CMD_BLOCK_STATUS reply */ ++typedef struct NBDBlockStatusPayload { ++ uint64_t effect_length; ++ /* uint32_t ids[] follows, array length implied by header */ ++} QEMU_PACKED NBDBlockStatusPayload; ++ ++/* Transmission (export) flags: sent from server to client during handshake, ++ but describe what will happen during transmission */ ++enum { ++ NBD_FLAG_HAS_FLAGS_BIT = 0, /* Flags are there */ ++ NBD_FLAG_READ_ONLY_BIT = 1, /* Device is read-only */ ++ NBD_FLAG_SEND_FLUSH_BIT = 2, /* Send FLUSH */ ++ NBD_FLAG_SEND_FUA_BIT = 3, /* Send FUA (Force Unit Access) */ ++ NBD_FLAG_ROTATIONAL_BIT = 4, /* Use elevator algorithm - ++ rotational media */ ++ NBD_FLAG_SEND_TRIM_BIT = 5, /* Send TRIM (discard) */ ++ NBD_FLAG_SEND_WRITE_ZEROES_BIT = 6, /* Send WRITE_ZEROES */ ++ NBD_FLAG_SEND_DF_BIT = 7, /* Send DF (Do not Fragment) */ ++ NBD_FLAG_CAN_MULTI_CONN_BIT = 8, /* Multi-client cache consistent */ ++ NBD_FLAG_SEND_RESIZE_BIT = 9, /* Send resize */ ++ NBD_FLAG_SEND_CACHE_BIT = 10, /* Send CACHE (prefetch) */ ++ NBD_FLAG_SEND_FAST_ZERO_BIT = 11, /* FAST_ZERO flag for WRITE_ZEROES */ ++ NBD_FLAG_BLOCK_STAT_PAYLOAD_BIT = 12, /* PAYLOAD flag for BLOCK_STATUS */ ++}; ++ ++#define NBD_FLAG_HAS_FLAGS (1 << NBD_FLAG_HAS_FLAGS_BIT) ++#define NBD_FLAG_READ_ONLY (1 << NBD_FLAG_READ_ONLY_BIT) ++#define NBD_FLAG_SEND_FLUSH (1 << NBD_FLAG_SEND_FLUSH_BIT) ++#define NBD_FLAG_SEND_FUA (1 << NBD_FLAG_SEND_FUA_BIT) ++#define NBD_FLAG_ROTATIONAL (1 << NBD_FLAG_ROTATIONAL_BIT) ++#define NBD_FLAG_SEND_TRIM (1 << NBD_FLAG_SEND_TRIM_BIT) ++#define NBD_FLAG_SEND_WRITE_ZEROES (1 << NBD_FLAG_SEND_WRITE_ZEROES_BIT) ++#define NBD_FLAG_SEND_DF (1 << NBD_FLAG_SEND_DF_BIT) ++#define NBD_FLAG_CAN_MULTI_CONN (1 << NBD_FLAG_CAN_MULTI_CONN_BIT) ++#define NBD_FLAG_SEND_RESIZE (1 << NBD_FLAG_SEND_RESIZE_BIT) ++#define NBD_FLAG_SEND_CACHE (1 << NBD_FLAG_SEND_CACHE_BIT) ++#define NBD_FLAG_SEND_FAST_ZERO (1 << NBD_FLAG_SEND_FAST_ZERO_BIT) ++#define NBD_FLAG_BLOCK_STAT_PAYLOAD (1 << NBD_FLAG_BLOCK_STAT_PAYLOAD_BIT) ++ ++/* New-style handshake (global) flags, sent from server to client, and ++ control what will happen during handshake phase. */ ++#define NBD_FLAG_FIXED_NEWSTYLE (1 << 0) /* Fixed newstyle protocol. */ ++#define NBD_FLAG_NO_ZEROES (1 << 1) /* End handshake without zeroes. */ ++ ++/* New-style client flags, sent from client to server to control what happens ++ during handshake phase. */ ++#define NBD_FLAG_C_FIXED_NEWSTYLE (1 << 0) /* Fixed newstyle protocol. */ ++#define NBD_FLAG_C_NO_ZEROES (1 << 1) /* End handshake without zeroes. */ ++ ++/* Option requests. */ ++#define NBD_OPT_EXPORT_NAME (1) ++#define NBD_OPT_ABORT (2) ++#define NBD_OPT_LIST (3) ++/* #define NBD_OPT_PEEK_EXPORT (4) not in use */ ++#define NBD_OPT_STARTTLS (5) ++#define NBD_OPT_INFO (6) ++#define NBD_OPT_GO (7) ++#define NBD_OPT_STRUCTURED_REPLY (8) ++#define NBD_OPT_LIST_META_CONTEXT (9) ++#define NBD_OPT_SET_META_CONTEXT (10) ++#define NBD_OPT_EXTENDED_HEADERS (11) ++ ++/* Option reply types. */ ++#define NBD_REP_ERR(value) ((UINT32_C(1) << 31) | (value)) ++ ++#define NBD_REP_ACK (1) /* Data sending finished. */ ++#define NBD_REP_SERVER (2) /* Export description. */ ++#define NBD_REP_INFO (3) /* NBD_OPT_INFO/GO. */ ++#define NBD_REP_META_CONTEXT (4) /* NBD_OPT_{LIST,SET}_META_CONTEXT */ ++ ++#define NBD_REP_ERR_UNSUP NBD_REP_ERR(1) /* Unknown option */ ++#define NBD_REP_ERR_POLICY NBD_REP_ERR(2) /* Server denied */ ++#define NBD_REP_ERR_INVALID NBD_REP_ERR(3) /* Invalid length */ ++#define NBD_REP_ERR_PLATFORM NBD_REP_ERR(4) /* Not compiled in */ ++#define NBD_REP_ERR_TLS_REQD NBD_REP_ERR(5) /* TLS required */ ++#define NBD_REP_ERR_UNKNOWN NBD_REP_ERR(6) /* Export unknown */ ++#define NBD_REP_ERR_SHUTDOWN NBD_REP_ERR(7) /* Server shutting down */ ++#define NBD_REP_ERR_BLOCK_SIZE_REQD NBD_REP_ERR(8) /* Need INFO_BLOCK_SIZE */ ++#define NBD_REP_ERR_TOO_BIG NBD_REP_ERR(9) /* Payload size overflow */ ++#define NBD_REP_ERR_EXT_HEADER_REQD NBD_REP_ERR(10) /* Need extended headers */ ++ ++/* Info types, used during NBD_REP_INFO */ ++#define NBD_INFO_EXPORT 0 ++#define NBD_INFO_NAME 1 ++#define NBD_INFO_DESCRIPTION 2 ++#define NBD_INFO_BLOCK_SIZE 3 ++ ++/* Request flags, sent from client to server during transmission phase */ ++#define NBD_CMD_FLAG_FUA (1 << 0) /* 'force unit access' during write */ ++#define NBD_CMD_FLAG_NO_HOLE (1 << 1) /* don't punch hole on zero run */ ++#define NBD_CMD_FLAG_DF (1 << 2) /* don't fragment structured read */ ++#define NBD_CMD_FLAG_REQ_ONE (1 << 3) \ ++ /* only one extent in BLOCK_STATUS reply chunk */ ++#define NBD_CMD_FLAG_FAST_ZERO (1 << 4) /* fail if WRITE_ZEROES is not fast */ ++#define NBD_CMD_FLAG_PAYLOAD_LEN (1 << 5) \ ++ /* length describes payload, not effect; only with ext header */ ++ ++/* Supported request types */ ++enum { ++ NBD_CMD_READ = 0, ++ NBD_CMD_WRITE = 1, ++ NBD_CMD_DISC = 2, ++ NBD_CMD_FLUSH = 3, ++ NBD_CMD_TRIM = 4, ++ NBD_CMD_CACHE = 5, ++ NBD_CMD_WRITE_ZEROES = 6, ++ NBD_CMD_BLOCK_STATUS = 7, ++}; ++ ++#define NBD_DEFAULT_PORT 10809 ++ ++/* Maximum size of a single READ/WRITE data buffer */ ++#define NBD_MAX_BUFFER_SIZE (32 * 1024 * 1024) ++ ++/* ++ * Maximum size of a protocol string (export name, metadata context name, ++ * etc.). Use malloc rather than stack allocation for storage of a ++ * string. ++ */ ++#define NBD_MAX_STRING_SIZE 4096 ++ ++/* Two types of request structures, a given client will only use 1 */ ++#define NBD_REQUEST_MAGIC 0x25609513 ++#define NBD_EXTENDED_REQUEST_MAGIC 0x21e41c71 ++ ++/* ++ * Three types of reply structures, but what a client expects depends ++ * on NBD_OPT_STRUCTURED_REPLY and NBD_OPT_EXTENDED_HEADERS. ++ */ ++#define NBD_SIMPLE_REPLY_MAGIC 0x67446698 ++#define NBD_STRUCTURED_REPLY_MAGIC 0x668e33ef ++#define NBD_EXTENDED_REPLY_MAGIC 0x6e8a278c ++ ++/* Chunk reply flags (for structured and extended replies) */ ++#define NBD_REPLY_FLAG_DONE (1 << 0) /* This reply-chunk is last */ ++ ++/* Chunk reply types */ ++#define NBD_REPLY_ERR(value) ((1 << 15) | (value)) ++ ++#define NBD_REPLY_TYPE_NONE 0 ++#define NBD_REPLY_TYPE_OFFSET_DATA 1 ++#define NBD_REPLY_TYPE_OFFSET_HOLE 2 ++#define NBD_REPLY_TYPE_BLOCK_STATUS 5 ++#define NBD_REPLY_TYPE_BLOCK_STATUS_EXT 6 ++#define NBD_REPLY_TYPE_ERROR NBD_REPLY_ERR(1) ++#define NBD_REPLY_TYPE_ERROR_OFFSET NBD_REPLY_ERR(2) ++ ++/* Extent flags for base:allocation in NBD_REPLY_TYPE_BLOCK_STATUS */ ++#define NBD_STATE_HOLE (1 << 0) ++#define NBD_STATE_ZERO (1 << 1) ++ ++/* Extent flags for qemu:dirty-bitmap in NBD_REPLY_TYPE_BLOCK_STATUS */ ++#define NBD_STATE_DIRTY (1 << 0) ++ ++/* No flags needed for qemu:allocation-depth in NBD_REPLY_TYPE_BLOCK_STATUS */ ++ ++static inline bool nbd_reply_type_is_error(int type) ++{ ++ return type & (1 << 15); ++} ++ ++/* NBD errors are based on errno numbers, so there is a 1:1 mapping, ++ * but only a limited set of errno values is specified in the protocol. ++ * Everything else is squashed to EINVAL. ++ */ ++#define NBD_SUCCESS 0 ++#define NBD_EPERM 1 ++#define NBD_EIO 5 ++#define NBD_ENOMEM 12 ++#define NBD_EINVAL 22 ++#define NBD_ENOSPC 28 ++#define NBD_EOVERFLOW 75 ++#define NBD_ENOTSUP 95 ++#define NBD_ESHUTDOWN 108 ++ ++/* Details collected by NBD_OPT_EXPORT_NAME and NBD_OPT_GO */ ++typedef struct NBDExportInfo { ++ /* Set by client before nbd_receive_negotiate() */ ++ bool request_sizes; ++ char *x_dirty_bitmap; ++ ++ /* Set by client before nbd_receive_negotiate(), or by server results ++ * during nbd_receive_export_list() */ ++ char *name; /* must be non-NULL */ ++ ++ /* In-out fields, set by client before nbd_receive_negotiate() and ++ * updated by server results during nbd_receive_negotiate() */ ++ NBDMode mode; /* input maximum mode tolerated; output actual mode chosen */ ++ bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */ ++ ++ /* Set by server results during nbd_receive_negotiate() and ++ * nbd_receive_export_list() */ ++ uint64_t size; ++ uint16_t flags; ++ uint32_t min_block; ++ uint32_t opt_block; ++ uint32_t max_block; ++ ++ uint32_t context_id; ++ ++ /* Set by server results during nbd_receive_export_list() */ ++ char *description; ++ int n_contexts; ++ char **contexts; ++} NBDExportInfo; ++ ++int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, ++ const char *hostname, QIOChannel **outioc, ++ NBDExportInfo *info, Error **errp); ++void nbd_free_export_list(NBDExportInfo *info, int count); ++int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, ++ const char *hostname, NBDExportInfo **info, ++ Error **errp); ++int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, ++ Error **errp); ++int nbd_send_request(QIOChannel *ioc, NBDRequest *request); ++int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc, ++ NBDReply *reply, NBDMode mode, ++ Error **errp); ++int nbd_client(int fd); ++int nbd_disconnect(int fd); ++int nbd_errno_to_system_errno(int err); ++ ++void nbd_export_set_on_eject_blk(BlockExport *exp, BlockBackend *blk); ++ ++AioContext *nbd_export_aio_context(NBDExport *exp); ++NBDExport *nbd_export_find(const char *name); ++ ++void nbd_client_new(QIOChannelSocket *sioc, ++ uint32_t handshake_max_secs, ++ QCryptoTLSCreds *tlscreds, ++ const char *tlsauthz, ++ void (*close_fn)(NBDClient *, bool), ++ void *owner); ++void *nbd_client_owner(NBDClient *client); ++void nbd_client_get(NBDClient *client); ++void nbd_client_put(NBDClient *client); ++ ++void nbd_server_is_qemu_nbd(int max_connections); ++bool nbd_server_is_running(void); ++int nbd_server_max_connections(void); ++void nbd_server_start(SocketAddress *addr, const char *tls_creds, ++ const char *tls_authz, uint32_t max_connections, ++ Error **errp); ++void nbd_server_start_options(NbdServerOptions *arg, Error **errp); ++ ++/* nbd_read ++ * Reads @size bytes from @ioc. Returns 0 on success. ++ */ ++static inline int nbd_read(QIOChannel *ioc, void *buffer, size_t size, ++ const char *desc, Error **errp) ++{ ++ ERRP_GUARD(); ++ int ret = qio_channel_read_all(ioc, buffer, size, errp) < 0 ? -EIO : 0; ++ ++ if (ret < 0) { ++ if (desc) { ++ error_prepend(errp, "Failed to read %s: ", desc); ++ } ++ return ret; ++ } ++ ++ return 0; ++} ++ ++#define DEF_NBD_READ_N(bits) \ ++static inline int nbd_read##bits(QIOChannel *ioc, \ ++ uint##bits##_t *val, \ ++ const char *desc, Error **errp) \ ++{ \ ++ int ret = nbd_read(ioc, val, sizeof(*val), desc, errp); \ ++ if (ret < 0) { \ ++ return ret; \ ++ } \ ++ *val = be##bits##_to_cpu(*val); \ ++ return 0; \ ++} ++ ++DEF_NBD_READ_N(16) /* Defines nbd_read16(). */ ++DEF_NBD_READ_N(32) /* Defines nbd_read32(). */ ++DEF_NBD_READ_N(64) /* Defines nbd_read64(). */ ++ ++#undef DEF_NBD_READ_N ++ ++static inline bool nbd_reply_is_simple(NBDReply *reply) ++{ ++ return reply->magic == NBD_SIMPLE_REPLY_MAGIC; ++} ++ ++static inline bool nbd_reply_is_structured(NBDReply *reply) ++{ ++ return reply->magic == NBD_STRUCTURED_REPLY_MAGIC; ++} ++ ++const char *nbd_reply_type_lookup(uint16_t type); ++const char *nbd_opt_lookup(uint32_t opt); ++const char *nbd_rep_lookup(uint32_t rep); ++const char *nbd_info_lookup(uint16_t info); ++const char *nbd_cmd_lookup(uint16_t info); ++const char *nbd_err_lookup(int err); ++const char *nbd_mode_lookup(NBDMode mode); ++ ++/* nbd/client-connection.c */ ++void nbd_client_connection_enable_retry(NBDClientConnection *conn); ++ ++NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr, ++ bool do_negotiation, ++ const char *export_name, ++ const char *x_dirty_bitmap, ++ QCryptoTLSCreds *tlscreds, ++ const char *tlshostname); ++void nbd_client_connection_release(NBDClientConnection *conn); ++ ++QIOChannel *coroutine_fn ++nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info, ++ bool blocking, Error **errp); ++ ++void nbd_co_establish_connection_cancel(NBDClientConnection *conn); ++ ++#endif +diff --git a/include/block/qapi.h b/include/block/qapi.h +new file mode 100644 +index 00000000..54c48de2 +--- /dev/null ++++ b/include/block/qapi.h +@@ -0,0 +1,52 @@ ++/* ++ * Block layer qmp and info dump related functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCK_QAPI_H ++#define BLOCK_QAPI_H ++ ++#include "block/graph-lock.h" ++#include "block/snapshot.h" ++#include "qapi/qapi-types-block-core.h" ++ ++BlockDeviceInfo * GRAPH_RDLOCK ++bdrv_block_device_info(BlockBackend *blk, BlockDriverState *bs, ++ bool flat, Error **errp); ++ ++int GRAPH_RDLOCK ++bdrv_query_snapshot_info_list(BlockDriverState *bs, ++ SnapshotInfoList **p_list, ++ Error **errp); ++void GRAPH_RDLOCK ++bdrv_query_image_info(BlockDriverState *bs, ImageInfo **p_info, bool flat, ++ bool skip_implicit_filters, Error **errp); ++void GRAPH_RDLOCK ++bdrv_query_block_graph_info(BlockDriverState *bs, BlockGraphInfo **p_info, ++ Error **errp); ++ ++void bdrv_snapshot_dump(QEMUSnapshotInfo *sn); ++void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec, ++ const char *prefix, ++ int indentation); ++void bdrv_node_info_dump(BlockNodeInfo *info, int indentation, bool protocol); ++#endif +diff --git a/include/block/qdict.h b/include/block/qdict.h +new file mode 100644 +index 00000000..b4c28d96 +--- /dev/null ++++ b/include/block/qdict.h +@@ -0,0 +1,35 @@ ++/* ++ * Special QDict functions used by the block layer ++ * ++ * Copyright (c) 2013-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BLOCK_QDICT_H ++#define BLOCK_QDICT_H ++ ++#include "qapi/qmp/qdict.h" ++ ++QObject *qdict_crumple(const QDict *src, Error **errp); ++void qdict_flatten(QDict *qdict); ++ ++void qdict_copy_default(QDict *dst, QDict *src, const char *key); ++void qdict_set_default_str(QDict *dst, const char *key, const char *val); ++ ++void qdict_join(QDict *dest, QDict *src, bool overwrite); ++ ++void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); ++void qdict_array_split(QDict *src, QList **dst); ++int qdict_array_entries(QDict *src, const char *subqdict); ++ ++typedef struct QDictRenames { ++ const char *from; ++ const char *to; ++} QDictRenames; ++bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp); ++ ++Visitor *qobject_input_visitor_new_flat_confused(QDict *qdict, ++ Error **errp); ++#endif +diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h +new file mode 100644 +index 00000000..62670682 +--- /dev/null ++++ b/include/block/raw-aio.h +@@ -0,0 +1,94 @@ ++/* ++ * Declarations for AIO in the raw protocol ++ * ++ * Copyright IBM, Corp. 2008 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#ifndef QEMU_RAW_AIO_H ++#define QEMU_RAW_AIO_H ++ ++#include "block/aio.h" ++#include "qemu/iov.h" ++ ++/* AIO request types */ ++#define QEMU_AIO_READ 0x0001 ++#define QEMU_AIO_WRITE 0x0002 ++#define QEMU_AIO_IOCTL 0x0004 ++#define QEMU_AIO_FLUSH 0x0008 ++#define QEMU_AIO_DISCARD 0x0010 ++#define QEMU_AIO_WRITE_ZEROES 0x0020 ++#define QEMU_AIO_COPY_RANGE 0x0040 ++#define QEMU_AIO_TRUNCATE 0x0080 ++#define QEMU_AIO_ZONE_REPORT 0x0100 ++#define QEMU_AIO_ZONE_MGMT 0x0200 ++#define QEMU_AIO_ZONE_APPEND 0x0400 ++#define QEMU_AIO_TYPE_MASK \ ++ (QEMU_AIO_READ | \ ++ QEMU_AIO_WRITE | \ ++ QEMU_AIO_IOCTL | \ ++ QEMU_AIO_FLUSH | \ ++ QEMU_AIO_DISCARD | \ ++ QEMU_AIO_WRITE_ZEROES | \ ++ QEMU_AIO_COPY_RANGE | \ ++ QEMU_AIO_TRUNCATE | \ ++ QEMU_AIO_ZONE_REPORT | \ ++ QEMU_AIO_ZONE_MGMT | \ ++ QEMU_AIO_ZONE_APPEND) ++ ++/* AIO flags */ ++#define QEMU_AIO_MISALIGNED 0x1000 ++#define QEMU_AIO_BLKDEV 0x2000 ++#define QEMU_AIO_NO_FALLBACK 0x4000 ++ ++ ++/* linux-aio.c - Linux native implementation */ ++#ifdef CONFIG_LINUX_AIO ++typedef struct LinuxAioState LinuxAioState; ++LinuxAioState *laio_init(Error **errp); ++void laio_cleanup(LinuxAioState *s); ++ ++/* laio_co_submit: submit I/O requests in the thread's current AioContext. */ ++int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, ++ int type, uint64_t dev_max_batch); ++ ++bool laio_has_fdsync(int); ++void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context); ++void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context); ++#endif ++/* io_uring.c - Linux io_uring implementation */ ++#ifdef CONFIG_LINUX_IO_URING ++LuringState *luring_init(Error **errp); ++void luring_cleanup(LuringState *s); ++ ++/* luring_co_submit: submit I/O requests in the thread's current AioContext. */ ++int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset, ++ QEMUIOVector *qiov, int type); ++void luring_detach_aio_context(LuringState *s, AioContext *old_context); ++void luring_attach_aio_context(LuringState *s, AioContext *new_context); ++#endif ++ ++#ifdef _WIN32 ++typedef struct QEMUWin32AIOState QEMUWin32AIOState; ++QEMUWin32AIOState *win32_aio_init(void); ++void win32_aio_cleanup(QEMUWin32AIOState *aio); ++int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile); ++BlockAIOCB *win32_aio_submit(BlockDriverState *bs, ++ QEMUWin32AIOState *aio, HANDLE hfile, ++ uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, ++ BlockCompletionFunc *cb, void *opaque, int type); ++void win32_aio_detach_aio_context(QEMUWin32AIOState *aio, ++ AioContext *old_context); ++void win32_aio_attach_aio_context(QEMUWin32AIOState *aio, ++ AioContext *new_context); ++#endif ++ ++#endif /* QEMU_RAW_AIO_H */ +diff --git a/include/block/snapshot.h b/include/block/snapshot.h +new file mode 100644 +index 00000000..304cc6ea +--- /dev/null ++++ b/include/block/snapshot.h +@@ -0,0 +1,113 @@ ++/* ++ * Block layer snapshot related functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef SNAPSHOT_H ++#define SNAPSHOT_H ++ ++#include "block/graph-lock.h" ++#include "qapi/qapi-builtin-types.h" ++ ++#define SNAPSHOT_OPT_BASE "snapshot." ++#define SNAPSHOT_OPT_ID "snapshot.id" ++#define SNAPSHOT_OPT_NAME "snapshot.name" ++ ++extern QemuOptsList internal_snapshot_opts; ++ ++typedef struct QEMUSnapshotInfo { ++ char id_str[128]; /* unique snapshot id */ ++ /* the following fields are informative. They are not needed for ++ the consistency of the snapshot */ ++ char name[256]; /* user chosen name */ ++ uint64_t vm_state_size; /* VM state info size */ ++ uint32_t date_sec; /* UTC date of the snapshot */ ++ uint32_t date_nsec; ++ uint64_t vm_clock_nsec; /* VM clock relative to boot */ ++ uint64_t icount; /* record/replay step */ ++} QEMUSnapshotInfo; ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, ++ const char *name); ++bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, ++ const char *id, ++ const char *name, ++ QEMUSnapshotInfo *sn_info, ++ Error **errp); ++ ++int GRAPH_RDLOCK bdrv_can_snapshot(BlockDriverState *bs); ++ ++int GRAPH_RDLOCK ++bdrv_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); ++ ++int GRAPH_UNLOCKED ++bdrv_snapshot_goto(BlockDriverState *bs, const char *snapshot_id, Error **errp); ++ ++int GRAPH_RDLOCK ++bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id, ++ const char *name, Error **errp); ++ ++int bdrv_snapshot_list(BlockDriverState *bs, ++ QEMUSnapshotInfo **psn_info); ++int bdrv_snapshot_load_tmp(BlockDriverState *bs, ++ const char *snapshot_id, ++ const char *name, ++ Error **errp); ++int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, ++ const char *id_or_name, ++ Error **errp); ++ ++ ++/* ++ * Group operations. All block drivers are involved. ++ */ ++ ++bool bdrv_all_can_snapshot(bool has_devices, strList *devices, ++ Error **errp); ++int bdrv_all_delete_snapshot(const char *name, ++ bool has_devices, strList *devices, ++ Error **errp); ++int bdrv_all_goto_snapshot(const char *name, ++ bool has_devices, strList *devices, ++ Error **errp); ++int bdrv_all_has_snapshot(const char *name, ++ bool has_devices, strList *devices, ++ Error **errp); ++int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, ++ BlockDriverState *vm_state_bs, ++ uint64_t vm_state_size, ++ bool has_devices, ++ strList *devices, ++ Error **errp); ++ ++BlockDriverState *bdrv_all_find_vmstate_bs(const char *vmstate_bs, ++ bool has_devices, strList *devices, ++ Error **errp); ++ ++#endif +diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h +new file mode 100644 +index 00000000..948ff5f3 +--- /dev/null ++++ b/include/block/thread-pool.h +@@ -0,0 +1,43 @@ ++/* ++ * QEMU block layer thread pool ++ * ++ * Copyright IBM, Corp. 2008 ++ * Copyright Red Hat, Inc. 2012 ++ * ++ * Authors: ++ * Anthony Liguori ++ * Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#ifndef QEMU_THREAD_POOL_H ++#define QEMU_THREAD_POOL_H ++ ++#include "block/aio.h" ++ ++#define THREAD_POOL_MAX_THREADS_DEFAULT 64 ++ ++typedef int ThreadPoolFunc(void *opaque); ++ ++typedef struct ThreadPool ThreadPool; ++ ++ThreadPool *thread_pool_new(struct AioContext *ctx); ++void thread_pool_free(ThreadPool *pool); ++ ++/* ++ * thread_pool_submit* API: submit I/O requests in the thread's ++ * current AioContext. ++ */ ++BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, ++ BlockCompletionFunc *cb, void *opaque); ++int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg); ++void thread_pool_submit(ThreadPoolFunc *func, void *arg); ++ ++void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx); ++ ++#endif +diff --git a/include/crypto/hash.h b/include/crypto/hash.h +new file mode 100644 +index 00000000..54d87aa2 +--- /dev/null ++++ b/include/crypto/hash.h +@@ -0,0 +1,192 @@ ++/* ++ * QEMU Crypto hash algorithms ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QCRYPTO_HASH_H ++#define QCRYPTO_HASH_H ++ ++#include "qapi/qapi-types-crypto.h" ++ ++/* See also "QCryptoHashAlgorithm" defined in qapi/crypto.json */ ++ ++/** ++ * qcrypto_hash_supports: ++ * @alg: the hash algorithm ++ * ++ * Determine if @alg hash algorithm is supported by the ++ * current configured build. ++ * ++ * Returns: true if the algorithm is supported, false otherwise ++ */ ++gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg); ++ ++ ++/** ++ * qcrypto_hash_digest_len: ++ * @alg: the hash algorithm ++ * ++ * Determine the size of the hash digest in bytes ++ * ++ * Returns: the digest length in bytes ++ */ ++size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg); ++ ++/** ++ * qcrypto_hash_bytesv: ++ * @alg: the hash algorithm ++ * @iov: the array of memory regions to hash ++ * @niov: the length of @iov ++ * @result: pointer to hold output hash ++ * @resultlen: pointer to hold length of @result ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Computes the hash across all the memory regions ++ * present in @iov. The @result pointer will be ++ * filled with raw bytes representing the computed ++ * hash, which will have length @resultlen. The ++ * memory pointer in @result must be released ++ * with a call to g_free() when no longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, ++ const struct iovec *iov, ++ size_t niov, ++ uint8_t **result, ++ size_t *resultlen, ++ Error **errp); ++ ++/** ++ * qcrypto_hash_bytes: ++ * @alg: the hash algorithm ++ * @buf: the memory region to hash ++ * @len: the length of @buf ++ * @result: pointer to hold output hash ++ * @resultlen: pointer to hold length of @result ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Computes the hash across all the memory region ++ * @buf of length @len. The @result pointer will be ++ * filled with raw bytes representing the computed ++ * hash, which will have length @resultlen. The ++ * memory pointer in @result must be released ++ * with a call to g_free() when no longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, ++ const char *buf, ++ size_t len, ++ uint8_t **result, ++ size_t *resultlen, ++ Error **errp); ++ ++/** ++ * qcrypto_hash_digestv: ++ * @alg: the hash algorithm ++ * @iov: the array of memory regions to hash ++ * @niov: the length of @iov ++ * @digest: pointer to hold output hash ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Computes the hash across all the memory regions ++ * present in @iov. The @digest pointer will be ++ * filled with the printable hex digest of the computed ++ * hash, which will be terminated by '\0'. The ++ * memory pointer in @digest must be released ++ * with a call to g_free() when no longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qcrypto_hash_digestv(QCryptoHashAlgorithm alg, ++ const struct iovec *iov, ++ size_t niov, ++ char **digest, ++ Error **errp); ++ ++/** ++ * qcrypto_hash_digest: ++ * @alg: the hash algorithm ++ * @buf: the memory region to hash ++ * @len: the length of @buf ++ * @digest: pointer to hold output hash ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Computes the hash across all the memory region ++ * @buf of length @len. The @digest pointer will be ++ * filled with the printable hex digest of the computed ++ * hash, which will be terminated by '\0'. The ++ * memory pointer in @digest must be released ++ * with a call to g_free() when no longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qcrypto_hash_digest(QCryptoHashAlgorithm alg, ++ const char *buf, ++ size_t len, ++ char **digest, ++ Error **errp); ++ ++/** ++ * qcrypto_hash_base64v: ++ * @alg: the hash algorithm ++ * @iov: the array of memory regions to hash ++ * @niov: the length of @iov ++ * @base64: pointer to hold output hash ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Computes the hash across all the memory regions ++ * present in @iov. The @base64 pointer will be ++ * filled with the base64 encoding of the computed ++ * hash, which will be terminated by '\0'. The ++ * memory pointer in @base64 must be released ++ * with a call to g_free() when no longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, ++ const struct iovec *iov, ++ size_t niov, ++ char **base64, ++ Error **errp); ++ ++/** ++ * qcrypto_hash_base64: ++ * @alg: the hash algorithm ++ * @buf: the memory region to hash ++ * @len: the length of @buf ++ * @base64: pointer to hold output hash ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Computes the hash across all the memory region ++ * @buf of length @len. The @base64 pointer will be ++ * filled with the base64 encoding of the computed ++ * hash, which will be terminated by '\0'. The ++ * memory pointer in @base64 must be released ++ * with a call to g_free() when no longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qcrypto_hash_base64(QCryptoHashAlgorithm alg, ++ const char *buf, ++ size_t len, ++ char **base64, ++ Error **errp); ++ ++#endif /* QCRYPTO_HASH_H */ +diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h +new file mode 100644 +index 00000000..2a8a8570 +--- /dev/null ++++ b/include/crypto/tlscreds.h +@@ -0,0 +1,67 @@ ++/* ++ * QEMU crypto TLS credential support ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QCRYPTO_TLSCREDS_H ++#define QCRYPTO_TLSCREDS_H ++ ++#include "qapi/qapi-types-crypto.h" ++#include "qom/object.h" ++ ++#define TYPE_QCRYPTO_TLS_CREDS "tls-creds" ++typedef struct QCryptoTLSCreds QCryptoTLSCreds; ++typedef struct QCryptoTLSCredsClass QCryptoTLSCredsClass; ++DECLARE_OBJ_CHECKERS(QCryptoTLSCreds, QCryptoTLSCredsClass, QCRYPTO_TLS_CREDS, ++ TYPE_QCRYPTO_TLS_CREDS) ++ ++ ++#define QCRYPTO_TLS_CREDS_DH_PARAMS "dh-params.pem" ++ ++ ++typedef bool (*CryptoTLSCredsReload)(QCryptoTLSCreds *, Error **); ++/** ++ * QCryptoTLSCreds: ++ * ++ * The QCryptoTLSCreds object is an abstract base for different ++ * types of TLS handshake credentials. Most commonly the ++ * QCryptoTLSCredsX509 subclass will be used to provide x509 ++ * certificate credentials. ++ */ ++ ++struct QCryptoTLSCredsClass { ++ ObjectClass parent_class; ++ CryptoTLSCredsReload reload; ++}; ++ ++/** ++ * qcrypto_tls_creds_check_endpoint: ++ * @creds: pointer to a TLS credentials object ++ * @endpoint: type of network endpoint that will be using the credentials ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Check whether the credentials is setup according to ++ * the type of @endpoint argument. ++ * ++ * Returns true if the credentials is setup for the endpoint, false otherwise ++ */ ++bool qcrypto_tls_creds_check_endpoint(QCryptoTLSCreds *creds, ++ QCryptoTLSCredsEndpoint endpoint, ++ Error **errp); ++ ++#endif /* QCRYPTO_TLSCREDS_H */ +diff --git a/include/crypto/tlscredsanon.h b/include/crypto/tlscredsanon.h +new file mode 100644 +index 00000000..bd3023f9 +--- /dev/null ++++ b/include/crypto/tlscredsanon.h +@@ -0,0 +1,100 @@ ++/* ++ * QEMU crypto TLS anonymous credential support ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QCRYPTO_TLSCREDSANON_H ++#define QCRYPTO_TLSCREDSANON_H ++ ++#include "crypto/tlscreds.h" ++#include "qom/object.h" ++ ++#define TYPE_QCRYPTO_TLS_CREDS_ANON "tls-creds-anon" ++typedef struct QCryptoTLSCredsAnon QCryptoTLSCredsAnon; ++DECLARE_INSTANCE_CHECKER(QCryptoTLSCredsAnon, QCRYPTO_TLS_CREDS_ANON, ++ TYPE_QCRYPTO_TLS_CREDS_ANON) ++ ++ ++typedef struct QCryptoTLSCredsAnonClass QCryptoTLSCredsAnonClass; ++ ++/** ++ * QCryptoTLSCredsAnon: ++ * ++ * The QCryptoTLSCredsAnon object provides a representation ++ * of anonymous credentials used perform a TLS handshake. ++ * This is primarily provided for backwards compatibility and ++ * its use is discouraged as it has poor security characteristics ++ * due to lacking MITM attack protection amongst other problems. ++ * ++ * This is a user creatable object, which can be instantiated ++ * via object_new_propv(): ++ * ++ * ++ * Creating anonymous TLS credential objects in code ++ * ++ * Object *obj; ++ * Error *err = NULL; ++ * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_ANON, ++ * "tlscreds0", ++ * &err, ++ * "endpoint", "server", ++ * "dir", "/path/x509/cert/dir", ++ * "verify-peer", "yes", ++ * NULL); ++ * ++ * ++ * ++ * Or via QMP: ++ * ++ * ++ * Creating anonymous TLS credential objects via QMP ++ * ++ * { ++ * "execute": "object-add", "arguments": { ++ * "id": "tlscreds0", ++ * "qom-type": "tls-creds-anon", ++ * "props": { ++ * "endpoint": "server", ++ * "dir": "/path/to/x509/cert/dir", ++ * "verify-peer": false ++ * } ++ * } ++ * } ++ * ++ * ++ * ++ * ++ * Or via the CLI: ++ * ++ * ++ * Creating anonymous TLS credential objects via CLI ++ * ++ * qemu-system-x86_64 -object tls-creds-anon,id=tlscreds0,\ ++ * endpoint=server,verify-peer=off,\ ++ * dir=/path/to/x509/certdir/ ++ * ++ * ++ * ++ */ ++ ++struct QCryptoTLSCredsAnonClass { ++ QCryptoTLSCredsClass parent_class; ++}; ++ ++ ++#endif /* QCRYPTO_TLSCREDSANON_H */ +diff --git a/include/crypto/tlscredspsk.h b/include/crypto/tlscredspsk.h +new file mode 100644 +index 00000000..bcd07dc4 +--- /dev/null ++++ b/include/crypto/tlscredspsk.h +@@ -0,0 +1,95 @@ ++/* ++ * QEMU crypto TLS Pre-Shared Key (PSK) support ++ * ++ * Copyright (c) 2018 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QCRYPTO_TLSCREDSPSK_H ++#define QCRYPTO_TLSCREDSPSK_H ++ ++#include "crypto/tlscreds.h" ++#include "qom/object.h" ++ ++#define TYPE_QCRYPTO_TLS_CREDS_PSK "tls-creds-psk" ++typedef struct QCryptoTLSCredsPSK QCryptoTLSCredsPSK; ++DECLARE_INSTANCE_CHECKER(QCryptoTLSCredsPSK, QCRYPTO_TLS_CREDS_PSK, ++ TYPE_QCRYPTO_TLS_CREDS_PSK) ++ ++typedef struct QCryptoTLSCredsPSKClass QCryptoTLSCredsPSKClass; ++ ++#define QCRYPTO_TLS_CREDS_PSKFILE "keys.psk" ++ ++/** ++ * QCryptoTLSCredsPSK: ++ * ++ * The QCryptoTLSCredsPSK object provides a representation ++ * of the Pre-Shared Key credential used to perform a TLS handshake. ++ * ++ * This is a user creatable object, which can be instantiated ++ * via object_new_propv(): ++ * ++ * ++ * Creating TLS-PSK credential objects in code ++ * ++ * Object *obj; ++ * Error *err = NULL; ++ * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_PSK, ++ * "tlscreds0", ++ * &err, ++ * "dir", "/path/to/dir", ++ * "endpoint", "client", ++ * NULL); ++ * ++ * ++ * ++ * Or via QMP: ++ * ++ * ++ * Creating TLS-PSK credential objects via QMP ++ * ++ * { ++ * "execute": "object-add", "arguments": { ++ * "id": "tlscreds0", ++ * "qom-type": "tls-creds-psk", ++ * "props": { ++ * "dir": "/path/to/dir", ++ * "endpoint": "client" ++ * } ++ * } ++ * } ++ * ++ * ++ * ++ * Or via the CLI: ++ * ++ * ++ * Creating TLS-PSK credential objects via CLI ++ * ++ * qemu-system-x86_64 --object tls-creds-psk,id=tlscreds0,\ ++ * endpoint=client,dir=/path/to/dir[,username=qemu] ++ * ++ * ++ * ++ * The PSK file can be created and managed using psktool. ++ */ ++ ++struct QCryptoTLSCredsPSKClass { ++ QCryptoTLSCredsClass parent_class; ++}; ++ ++ ++#endif /* QCRYPTO_TLSCREDSPSK_H */ +diff --git a/include/crypto/tlscredsx509.h b/include/crypto/tlscredsx509.h +new file mode 100644 +index 00000000..c4daba21 +--- /dev/null ++++ b/include/crypto/tlscredsx509.h +@@ -0,0 +1,104 @@ ++/* ++ * QEMU crypto TLS x509 credential support ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QCRYPTO_TLSCREDSX509_H ++#define QCRYPTO_TLSCREDSX509_H ++ ++#include "crypto/tlscreds.h" ++#include "qom/object.h" ++ ++#define TYPE_QCRYPTO_TLS_CREDS_X509 "tls-creds-x509" ++typedef struct QCryptoTLSCredsX509 QCryptoTLSCredsX509; ++DECLARE_INSTANCE_CHECKER(QCryptoTLSCredsX509, QCRYPTO_TLS_CREDS_X509, ++ TYPE_QCRYPTO_TLS_CREDS_X509) ++ ++typedef struct QCryptoTLSCredsX509Class QCryptoTLSCredsX509Class; ++ ++#define QCRYPTO_TLS_CREDS_X509_CA_CERT "ca-cert.pem" ++#define QCRYPTO_TLS_CREDS_X509_CA_CRL "ca-crl.pem" ++#define QCRYPTO_TLS_CREDS_X509_SERVER_KEY "server-key.pem" ++#define QCRYPTO_TLS_CREDS_X509_SERVER_CERT "server-cert.pem" ++#define QCRYPTO_TLS_CREDS_X509_CLIENT_KEY "client-key.pem" ++#define QCRYPTO_TLS_CREDS_X509_CLIENT_CERT "client-cert.pem" ++ ++ ++/** ++ * QCryptoTLSCredsX509: ++ * ++ * The QCryptoTLSCredsX509 object provides a representation ++ * of x509 credentials used to perform a TLS handshake. ++ * ++ * This is a user creatable object, which can be instantiated ++ * via object_new_propv(): ++ * ++ * ++ * Creating x509 TLS credential objects in code ++ * ++ * Object *obj; ++ * Error *err = NULL; ++ * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_X509, ++ * "tlscreds0", ++ * &err, ++ * "endpoint", "server", ++ * "dir", "/path/x509/cert/dir", ++ * "verify-peer", "yes", ++ * NULL); ++ * ++ * ++ * ++ * Or via QMP: ++ * ++ * ++ * Creating x509 TLS credential objects via QMP ++ * ++ * { ++ * "execute": "object-add", "arguments": { ++ * "id": "tlscreds0", ++ * "qom-type": "tls-creds-x509", ++ * "props": { ++ * "endpoint": "server", ++ * "dir": "/path/to/x509/cert/dir", ++ * "verify-peer": false ++ * } ++ * } ++ * } ++ * ++ * ++ * ++ * ++ * Or via the CLI: ++ * ++ * ++ * Creating x509 TLS credential objects via CLI ++ * ++ * qemu-system-x86_64 -object tls-creds-x509,id=tlscreds0,\ ++ * endpoint=server,verify-peer=off,\ ++ * dir=/path/to/x509/certdir/ ++ * ++ * ++ * ++ */ ++ ++struct QCryptoTLSCredsX509Class { ++ QCryptoTLSCredsClass parent_class; ++}; ++ ++ ++#endif /* QCRYPTO_TLSCREDSX509_H */ +diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h +new file mode 100644 +index 00000000..f694a5c3 +--- /dev/null ++++ b/include/crypto/tlssession.h +@@ -0,0 +1,356 @@ ++/* ++ * QEMU crypto TLS session support ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QCRYPTO_TLSSESSION_H ++#define QCRYPTO_TLSSESSION_H ++ ++#include "crypto/tlscreds.h" ++ ++/** ++ * QCryptoTLSSession: ++ * ++ * The QCryptoTLSSession object encapsulates the ++ * logic to integrate with a TLS providing library such ++ * as GNUTLS, to setup and run TLS sessions. ++ * ++ * The API is designed such that it has no assumption about ++ * the type of transport it is running over. It may be a ++ * traditional TCP socket, or something else entirely. The ++ * only requirement is a full-duplex stream of some kind. ++ * ++ * ++ * Using TLS session objects ++ * ++ * static ssize_t mysock_send(const char *buf, size_t len, ++ * void *opaque) ++ * { ++ * int fd = GPOINTER_TO_INT(opaque); ++ * ++ * return write(*fd, buf, len); ++ * } ++ * ++ * static ssize_t mysock_recv(const char *buf, size_t len, ++ * void *opaque) ++ * { ++ * int fd = GPOINTER_TO_INT(opaque); ++ * ++ * return read(*fd, buf, len); ++ * } ++ * ++ * static int mysock_run_tls(int sockfd, ++ * QCryptoTLSCreds *creds, ++ * Error **errp) ++ * { ++ * QCryptoTLSSession *sess; ++ * ++ * sess = qcrypto_tls_session_new(creds, ++ * "vnc.example.com", ++ * NULL, ++ * QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, ++ * errp); ++ * if (sess == NULL) { ++ * return -1; ++ * } ++ * ++ * qcrypto_tls_session_set_callbacks(sess, ++ * mysock_send, ++ * mysock_recv ++ * GINT_TO_POINTER(fd)); ++ * ++ * while (1) { ++ * if (qcrypto_tls_session_handshake(sess, errp) < 0) { ++ * qcrypto_tls_session_free(sess); ++ * return -1; ++ * } ++ * ++ * switch(qcrypto_tls_session_get_handshake_status(sess)) { ++ * case QCRYPTO_TLS_HANDSHAKE_COMPLETE: ++ * if (qcrypto_tls_session_check_credentials(sess, errp) < )) { ++ * qcrypto_tls_session_free(sess); ++ * return -1; ++ * } ++ * goto done; ++ * case QCRYPTO_TLS_HANDSHAKE_RECVING: ++ * ...wait for GIO_IN event on fd... ++ * break; ++ * case QCRYPTO_TLS_HANDSHAKE_SENDING: ++ * ...wait for GIO_OUT event on fd... ++ * break; ++ * } ++ * } ++ * done: ++ * ++ * ....send/recv payload data on sess... ++ * ++ * qcrypto_tls_session_free(sess): ++ * } ++ * ++ * ++ */ ++ ++typedef struct QCryptoTLSSession QCryptoTLSSession; ++ ++#define QCRYPTO_TLS_SESSION_ERR_BLOCK -2 ++ ++/** ++ * qcrypto_tls_session_new: ++ * @creds: pointer to a TLS credentials object ++ * @hostname: optional hostname to validate ++ * @aclname: optional ACL to validate peer credentials against ++ * @endpoint: role of the TLS session, client or server ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Create a new TLS session object that will be used to ++ * negotiate a TLS session over an arbitrary data channel. ++ * The session object can operate as either the server or ++ * client, according to the value of the @endpoint argument. ++ * ++ * For clients, the @hostname parameter should hold the full ++ * unmodified hostname as requested by the user. This will ++ * be used to verify the against the hostname reported in ++ * the server's credentials (aka x509 certificate). ++ * ++ * The @aclname parameter (optionally) specifies the name ++ * of an access control list that will be used to validate ++ * the peer's credentials. For x509 credentials, the ACL ++ * will be matched against the CommonName shown in the peer's ++ * certificate. If the session is acting as a server, setting ++ * an ACL will require that the client provide a validate ++ * x509 client certificate. ++ * ++ * After creating the session object, the I/O callbacks ++ * must be set using the qcrypto_tls_session_set_callbacks() ++ * method. A TLS handshake sequence must then be completed ++ * using qcrypto_tls_session_handshake(), before payload ++ * data is permitted to be sent/received. ++ * ++ * The session object must be released by calling ++ * qcrypto_tls_session_free() when no longer required ++ * ++ * Returns: a TLS session object, or NULL on error. ++ */ ++QCryptoTLSSession *qcrypto_tls_session_new(QCryptoTLSCreds *creds, ++ const char *hostname, ++ const char *aclname, ++ QCryptoTLSCredsEndpoint endpoint, ++ Error **errp); ++ ++/** ++ * qcrypto_tls_session_free: ++ * @sess: the TLS session object ++ * ++ * Release all memory associated with the TLS session ++ * object previously allocated by qcrypto_tls_session_new() ++ */ ++void qcrypto_tls_session_free(QCryptoTLSSession *sess); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free) ++ ++/** ++ * qcrypto_tls_session_check_credentials: ++ * @sess: the TLS session object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Validate the peer's credentials after a successful ++ * TLS handshake. It is an error to call this before ++ * qcrypto_tls_session_get_handshake_status() returns ++ * QCRYPTO_TLS_HANDSHAKE_COMPLETE ++ * ++ * Returns 0 if the credentials validated, -1 on error ++ */ ++int qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess, ++ Error **errp); ++ ++/* ++ * These must return QCRYPTO_TLS_SESSION_ERR_BLOCK if the I/O ++ * would block, but on other errors, must fill 'errp' ++ */ ++typedef ssize_t (*QCryptoTLSSessionWriteFunc)(const char *buf, ++ size_t len, ++ void *opaque, ++ Error **errp); ++typedef ssize_t (*QCryptoTLSSessionReadFunc)(char *buf, ++ size_t len, ++ void *opaque, ++ Error **errp); ++ ++/** ++ * qcrypto_tls_session_set_callbacks: ++ * @sess: the TLS session object ++ * @writeFunc: callback for sending data ++ * @readFunc: callback to receiving data ++ * @opaque: data to pass to callbacks ++ * ++ * Sets the callback functions that are to be used for sending ++ * and receiving data on the underlying data channel. Typically ++ * the callbacks to write/read to/from a TCP socket, but there ++ * is no assumption made about the type of channel used. ++ * ++ * The @writeFunc callback will be passed the encrypted ++ * data to send to the remote peer. ++ * ++ * The @readFunc callback will be passed a pointer to fill ++ * with encrypted data received from the remote peer ++ */ ++void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess, ++ QCryptoTLSSessionWriteFunc writeFunc, ++ QCryptoTLSSessionReadFunc readFunc, ++ void *opaque); ++ ++/** ++ * qcrypto_tls_session_write: ++ * @sess: the TLS session object ++ * @buf: the plain text to send ++ * @len: the length of @buf ++ * @errp: pointer to hold returned error object ++ * ++ * Encrypt @len bytes of the data in @buf and send ++ * it to the remote peer using the callback previously ++ * registered with qcrypto_tls_session_set_callbacks() ++ * ++ * It is an error to call this before ++ * qcrypto_tls_session_get_handshake_status() returns ++ * QCRYPTO_TLS_HANDSHAKE_COMPLETE ++ * ++ * Returns: the number of bytes sent, ++ * or QCRYPTO_TLS_SESSION_ERR_BLOCK if the write would block, ++ * or -1 on error. ++ */ ++ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, ++ const char *buf, ++ size_t len, ++ Error **errp); ++ ++/** ++ * qcrypto_tls_session_read: ++ * @sess: the TLS session object ++ * @buf: to fill with plain text received ++ * @len: the length of @buf ++ * @gracefulTermination: treat premature termination as graceful EOF ++ * @errp: pointer to hold returned error object ++ * ++ * Receive up to @len bytes of data from the remote peer ++ * using the callback previously registered with ++ * qcrypto_tls_session_set_callbacks(), decrypt it and ++ * store it in @buf. ++ * ++ * If @gracefulTermination is true, then a premature termination ++ * of the TLS session will be treated as indicating EOF, as ++ * opposed to an error. ++ * ++ * It is an error to call this before ++ * qcrypto_tls_session_get_handshake_status() returns ++ * QCRYPTO_TLS_HANDSHAKE_COMPLETE ++ * ++ * Returns: the number of bytes received, ++ * or QCRYPTO_TLS_SESSION_ERR_BLOCK if the receive would block, ++ * or -1 on error. ++ */ ++ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, ++ char *buf, ++ size_t len, ++ bool gracefulTermination, ++ Error **errp); ++ ++/** ++ * qcrypto_tls_session_check_pending: ++ * @sess: the TLS session object ++ * ++ * Check if there are unread data in the TLS buffers that have ++ * already been read from the underlying data source. ++ * ++ * Returns: the number of bytes available or zero ++ */ ++size_t qcrypto_tls_session_check_pending(QCryptoTLSSession *sess); ++ ++/** ++ * qcrypto_tls_session_handshake: ++ * @sess: the TLS session object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Start, or continue, a TLS handshake sequence. If ++ * the underlying data channel is non-blocking, then ++ * this method may return control before the handshake ++ * is complete. On non-blocking channels the ++ * qcrypto_tls_session_get_handshake_status() method ++ * should be used to determine whether the handshake ++ * has completed, or is waiting to send or receive ++ * data. In the latter cases, the caller should setup ++ * an event loop watch and call this method again ++ * once the underlying data channel is ready to read ++ * or write again ++ */ ++int qcrypto_tls_session_handshake(QCryptoTLSSession *sess, ++ Error **errp); ++ ++typedef enum { ++ QCRYPTO_TLS_HANDSHAKE_COMPLETE, ++ QCRYPTO_TLS_HANDSHAKE_SENDING, ++ QCRYPTO_TLS_HANDSHAKE_RECVING, ++} QCryptoTLSSessionHandshakeStatus; ++ ++/** ++ * qcrypto_tls_session_get_handshake_status: ++ * @sess: the TLS session object ++ * ++ * Check the status of the TLS handshake. This ++ * is used with non-blocking data channels to ++ * determine whether the handshake is waiting ++ * to send or receive further data to/from the ++ * remote peer. ++ * ++ * Once this returns QCRYPTO_TLS_HANDSHAKE_COMPLETE ++ * it is permitted to send/receive payload data on ++ * the channel ++ */ ++QCryptoTLSSessionHandshakeStatus ++qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess); ++ ++/** ++ * qcrypto_tls_session_get_key_size: ++ * @sess: the TLS session object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Check the size of the data channel encryption key ++ * ++ * Returns: the length in bytes of the encryption key ++ * or -1 on error ++ */ ++int qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, ++ Error **errp); ++ ++/** ++ * qcrypto_tls_session_get_peer_name: ++ * @sess: the TLS session object ++ * ++ * Get the identified name of the remote peer. If the ++ * TLS session was negotiated using x509 certificate ++ * credentials, this will return the CommonName from ++ * the peer's certificate. If no identified name is ++ * available it will return NULL. ++ * ++ * The returned data must be released with g_free() ++ * when no longer required. ++ * ++ * Returns: the peer's name or NULL. ++ */ ++char *qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess); ++ ++#endif /* QCRYPTO_TLSSESSION_H */ +diff --git a/include/exec/hwaddr.h b/include/exec/hwaddr.h +new file mode 100644 +index 00000000..50fbb2d9 +--- /dev/null ++++ b/include/exec/hwaddr.h +@@ -0,0 +1,26 @@ ++/* Define hwaddr if it exists. */ ++ ++#ifndef HWADDR_H ++#define HWADDR_H ++ ++ ++#define HWADDR_BITS 64 ++/* hwaddr is the type of a physical address (its size can ++ be different from 'target_ulong'). */ ++ ++typedef uint64_t hwaddr; ++#define HWADDR_MAX UINT64_MAX ++#define HWADDR_FMT_plx "%016" PRIx64 ++#define HWADDR_PRId PRId64 ++#define HWADDR_PRIi PRIi64 ++#define HWADDR_PRIo PRIo64 ++#define HWADDR_PRIu PRIu64 ++#define HWADDR_PRIx PRIx64 ++#define HWADDR_PRIX PRIX64 ++ ++typedef struct MemMapEntry { ++ hwaddr base; ++ hwaddr size; ++} MemMapEntry; ++ ++#endif +diff --git a/include/glib-compat.h b/include/glib-compat.h +new file mode 100644 +index 00000000..86be439b +--- /dev/null ++++ b/include/glib-compat.h +@@ -0,0 +1,131 @@ ++/* ++ * GLIB Compatibility Functions ++ * ++ * Copyright IBM, Corp. 2013 ++ * ++ * Authors: ++ * Anthony Liguori ++ * Michael Tokarev ++ * Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_GLIB_COMPAT_H ++#define QEMU_GLIB_COMPAT_H ++ ++/* Ask for warnings for anything that was marked deprecated in ++ * the defined version, or before. It is a candidate for rewrite. ++ */ ++#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_66 ++ ++/* Ask for warnings if code tries to use function that did not ++ * exist in the defined version. These risk breaking builds ++ */ ++#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_66 ++ ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wdeprecated-declarations" ++ ++#include ++#if defined(G_OS_UNIX) ++#include ++#include ++#include ++#endif ++ ++/* ++ * Note that because of the GLIB_VERSION_MAX_ALLOWED constant above, allowing ++ * use of functions from newer GLib via this compat header needs a little ++ * trickery to prevent warnings being emitted. ++ * ++ * Consider a function from newer glib-X.Y that we want to use ++ * ++ * int g_foo(const char *wibble) ++ * ++ * We must define a static inline function with the same signature that does ++ * what we need, but with a "_compat" suffix e.g. ++ * ++ * static inline void g_foo_compat(const char *wibble) ++ * { ++ * #if GLIB_CHECK_VERSION(X, Y, 0) ++ * g_foo(wibble) ++ * #else ++ * g_something_equivalent_in_older_glib(wibble); ++ * #endif ++ * } ++ * ++ * The #pragma at the top of this file turns off -Wdeprecated-declarations, ++ * ensuring this wrapper function impl doesn't trigger the compiler warning ++ * about using too new glib APIs. Finally we can do ++ * ++ * #define g_foo(a) g_foo_compat(a) ++ * ++ * So now the code elsewhere in QEMU, which *does* have the ++ * -Wdeprecated-declarations warning active, can call g_foo(...) as normal, ++ * without generating warnings. ++ */ ++ ++/* ++ * g_memdup2_qemu: ++ * @mem: (nullable): the memory to copy. ++ * @byte_size: the number of bytes to copy. ++ * ++ * Allocates @byte_size bytes of memory, and copies @byte_size bytes into it ++ * from @mem. If @mem is %NULL it returns %NULL. ++ * ++ * This replaces g_memdup(), which was prone to integer overflows when ++ * converting the argument from a #gsize to a #guint. ++ * ++ * This static inline version is a backport of the new public API from ++ * GLib 2.68, kept internal to GLib for backport to older stable releases. ++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2319. ++ * ++ * Returns: (nullable): a pointer to the newly-allocated copy of the memory, ++ * or %NULL if @mem is %NULL. ++ */ ++static inline gpointer g_memdup2_qemu(gconstpointer mem, gsize byte_size) ++{ ++#if GLIB_CHECK_VERSION(2, 68, 0) ++ return g_memdup2(mem, byte_size); ++#else ++ gpointer new_mem; ++ ++ if (mem && byte_size != 0) { ++ new_mem = g_malloc(byte_size); ++ memcpy(new_mem, mem, byte_size); ++ } else { ++ new_mem = NULL; ++ } ++ ++ return new_mem; ++#endif ++} ++#define g_memdup2(m, s) g_memdup2_qemu(m, s) ++ ++static inline bool ++qemu_g_test_slow(void) ++{ ++ static int cached = -1; ++ if (cached == -1) { ++ cached = g_test_slow() || getenv("G_TEST_SLOW") != NULL; ++ } ++ return cached; ++} ++ ++#undef g_test_slow ++#undef g_test_thorough ++#undef g_test_quick ++#define g_test_slow() qemu_g_test_slow() ++#define g_test_thorough() qemu_g_test_slow() ++#define g_test_quick() (!qemu_g_test_slow()) ++ ++#pragma GCC diagnostic pop ++ ++#ifndef G_NORETURN ++#define G_NORETURN G_GNUC_NORETURN ++#endif ++ ++#endif +diff --git a/include/hw/block/block.h b/include/hw/block/block.h +new file mode 100644 +index 00000000..de3946a5 +--- /dev/null ++++ b/include/hw/block/block.h +@@ -0,0 +1,110 @@ ++/* ++ * Common code for block device models ++ * ++ * Copyright (C) 2012 Red Hat, Inc. ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef HW_BLOCK_H ++#define HW_BLOCK_H ++ ++#include "exec/hwaddr.h" ++#include "qapi/qapi-types-block-core.h" ++#include "hw/qdev-properties-system.h" ++ ++/* Configuration */ ++ ++typedef struct BlockConf { ++ BlockBackend *blk; ++ OnOffAuto backend_defaults; ++ uint32_t physical_block_size; ++ uint32_t logical_block_size; ++ uint32_t min_io_size; ++ uint32_t opt_io_size; ++ int32_t bootindex; ++ uint32_t discard_granularity; ++ /* geometry, not all devices use this */ ++ uint32_t cyls, heads, secs; ++ uint32_t lcyls, lheads, lsecs; ++ OnOffAuto wce; ++ bool share_rw; ++ OnOffAuto account_invalid, account_failed; ++ BlockdevOnError rerror; ++ BlockdevOnError werror; ++} BlockConf; ++ ++static inline unsigned int get_physical_block_exp(BlockConf *conf) ++{ ++ unsigned int exp = 0, size; ++ ++ for (size = conf->physical_block_size; ++ size > conf->logical_block_size; ++ size >>= 1) { ++ exp++; ++ } ++ ++ return exp; ++} ++ ++#define DEFINE_BLOCK_PROPERTIES_BASE(_state, _conf) \ ++ DEFINE_PROP_ON_OFF_AUTO("backend_defaults", _state, \ ++ _conf.backend_defaults, ON_OFF_AUTO_AUTO), \ ++ DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \ ++ _conf.logical_block_size), \ ++ DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \ ++ _conf.physical_block_size), \ ++ DEFINE_PROP_SIZE32("min_io_size", _state, _conf.min_io_size, 0), \ ++ DEFINE_PROP_SIZE32("opt_io_size", _state, _conf.opt_io_size, 0), \ ++ DEFINE_PROP_SIZE32("discard_granularity", _state, \ ++ _conf.discard_granularity, -1), \ ++ DEFINE_PROP_ON_OFF_AUTO("write-cache", _state, _conf.wce, \ ++ ON_OFF_AUTO_AUTO), \ ++ DEFINE_PROP_BOOL("share-rw", _state, _conf.share_rw, false), \ ++ DEFINE_PROP_ON_OFF_AUTO("account-invalid", _state, \ ++ _conf.account_invalid, ON_OFF_AUTO_AUTO), \ ++ DEFINE_PROP_ON_OFF_AUTO("account-failed", _state, \ ++ _conf.account_failed, ON_OFF_AUTO_AUTO) ++ ++#define DEFINE_BLOCK_PROPERTIES(_state, _conf) \ ++ DEFINE_PROP_DRIVE("drive", _state, _conf.blk), \ ++ DEFINE_BLOCK_PROPERTIES_BASE(_state, _conf) ++ ++#define DEFINE_BLOCK_CHS_PROPERTIES(_state, _conf) \ ++ DEFINE_PROP_UINT32("cyls", _state, _conf.cyls, 0), \ ++ DEFINE_PROP_UINT32("heads", _state, _conf.heads, 0), \ ++ DEFINE_PROP_UINT32("secs", _state, _conf.secs, 0), \ ++ DEFINE_PROP_UINT32("lcyls", _state, _conf.lcyls, 0), \ ++ DEFINE_PROP_UINT32("lheads", _state, _conf.lheads, 0), \ ++ DEFINE_PROP_UINT32("lsecs", _state, _conf.lsecs, 0) ++ ++#define DEFINE_BLOCK_ERROR_PROPERTIES(_state, _conf) \ ++ DEFINE_PROP_BLOCKDEV_ON_ERROR("rerror", _state, _conf.rerror, \ ++ BLOCKDEV_ON_ERROR_AUTO), \ ++ DEFINE_PROP_BLOCKDEV_ON_ERROR("werror", _state, _conf.werror, \ ++ BLOCKDEV_ON_ERROR_AUTO) ++ ++/* Backend access helpers */ ++ ++bool blk_check_size_and_read_all(BlockBackend *blk, DeviceState *dev, ++ void *buf, hwaddr size, Error **errp); ++ ++/* Configuration helpers */ ++ ++bool blkconf_geometry(BlockConf *conf, int *trans, ++ unsigned cyls_max, unsigned heads_max, unsigned secs_max, ++ Error **errp); ++bool blkconf_blocksizes(BlockConf *conf, Error **errp); ++bool blkconf_apply_backend_options(BlockConf *conf, bool readonly, ++ bool resizable, Error **errp); ++ ++/* Hard disk geometry */ ++ ++void hd_geometry_guess(BlockBackend *blk, ++ uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs, ++ int *ptrans); ++int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs); ++ ++#endif +diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h +new file mode 100644 +index 00000000..77bfcbdf +--- /dev/null ++++ b/include/hw/qdev-core.h +@@ -0,0 +1,1111 @@ ++#ifndef QDEV_CORE_H ++#define QDEV_CORE_H ++ ++#include "qemu/atomic.h" ++#include "qemu/queue.h" ++#include "qemu/bitmap.h" ++#include "qemu/rcu.h" ++#include "qemu/rcu_queue.h" ++#include "qom/object.h" ++#include "hw/hotplug.h" ++#include "hw/resettable.h" ++ ++/** ++ * DOC: The QEMU Device API ++ * ++ * All modern devices should represented as a derived QOM class of ++ * TYPE_DEVICE. The device API introduces the additional methods of ++ * @realize and @unrealize to represent additional stages in a device ++ * objects life cycle. ++ * ++ * Realization ++ * ----------- ++ * ++ * Devices are constructed in two stages: ++ * ++ * 1) object instantiation via object_initialize() and ++ * 2) device realization via the #DeviceState.realized property ++ * ++ * The former may not fail (and must not abort or exit, since it is called ++ * during device introspection already), and the latter may return error ++ * information to the caller and must be re-entrant. ++ * Trivial field initializations should go into #TypeInfo.instance_init. ++ * Operations depending on @props static properties should go into @realize. ++ * After successful realization, setting static properties will fail. ++ * ++ * As an interim step, the #DeviceState.realized property can also be ++ * set with qdev_realize(). In the future, devices will propagate this ++ * state change to their children and along busses they expose. The ++ * point in time will be deferred to machine creation, so that values ++ * set in @realize will not be introspectable beforehand. Therefore ++ * devices must not create children during @realize; they should ++ * initialize them via object_initialize() in their own ++ * #TypeInfo.instance_init and forward the realization events ++ * appropriately. ++ * ++ * Any type may override the @realize and/or @unrealize callbacks but needs ++ * to call the parent type's implementation if keeping their functionality ++ * is desired. Refer to QOM documentation for further discussion and examples. ++ * ++ * .. note:: ++ * Since TYPE_DEVICE doesn't implement @realize and @unrealize, types ++ * derived directly from it need not call their parent's @realize and ++ * @unrealize. For other types consult the documentation and ++ * implementation of the respective parent types. ++ * ++ * Hiding a device ++ * --------------- ++ * ++ * To hide a device, a DeviceListener function hide_device() needs to ++ * be registered. It can be used to defer adding a device and ++ * therefore hide it from the guest. The handler registering to this ++ * DeviceListener can save the QOpts passed to it for re-using it ++ * later. It must return if it wants the device to be hidden or ++ * visible. When the handler function decides the device shall be ++ * visible it will be added with qdev_device_add() and realized as any ++ * other device. Otherwise qdev_device_add() will return early without ++ * adding the device. The guest will not see a "hidden" device until ++ * it was marked visible and qdev_device_add called again. ++ * ++ */ ++ ++enum { ++ DEV_NVECTORS_UNSPECIFIED = -1, ++}; ++ ++#define TYPE_DEVICE "device" ++OBJECT_DECLARE_TYPE(DeviceState, DeviceClass, DEVICE) ++ ++typedef enum DeviceCategory { ++ DEVICE_CATEGORY_BRIDGE, ++ DEVICE_CATEGORY_USB, ++ DEVICE_CATEGORY_STORAGE, ++ DEVICE_CATEGORY_NETWORK, ++ DEVICE_CATEGORY_INPUT, ++ DEVICE_CATEGORY_DISPLAY, ++ DEVICE_CATEGORY_SOUND, ++ DEVICE_CATEGORY_MISC, ++ DEVICE_CATEGORY_CPU, ++ DEVICE_CATEGORY_WATCHDOG, ++ DEVICE_CATEGORY_MAX ++} DeviceCategory; ++ ++typedef void (*DeviceRealize)(DeviceState *dev, Error **errp); ++typedef void (*DeviceUnrealize)(DeviceState *dev); ++typedef void (*DeviceReset)(DeviceState *dev); ++typedef void (*BusRealize)(BusState *bus, Error **errp); ++typedef void (*BusUnrealize)(BusState *bus); ++ ++/** ++ * struct DeviceClass - The base class for all devices. ++ * @props: Properties accessing state fields. ++ * @realize: Callback function invoked when the #DeviceState:realized ++ * property is changed to %true. ++ * @unrealize: Callback function invoked when the #DeviceState:realized ++ * property is changed to %false. ++ * @hotpluggable: indicates if #DeviceClass is hotpluggable, available ++ * as readonly "hotpluggable" property of #DeviceState instance ++ * ++ */ ++struct DeviceClass { ++ /* private: */ ++ ObjectClass parent_class; ++ ++ /* public: */ ++ ++ /** ++ * @categories: device categories device belongs to ++ */ ++ DECLARE_BITMAP(categories, DEVICE_CATEGORY_MAX); ++ /** ++ * @fw_name: name used to identify device to firmware interfaces ++ */ ++ const char *fw_name; ++ /** ++ * @desc: human readable description of device ++ */ ++ const char *desc; ++ ++ /** ++ * @props_: properties associated with device, should only be ++ * assigned by using device_class_set_props(). The underscore ++ * ensures a compile-time error if someone attempts to assign ++ * dc->props directly. ++ */ ++ Property *props_; ++ ++ /** ++ * @user_creatable: Can user instantiate with -device / device_add? ++ * ++ * All devices should support instantiation with device_add, and ++ * this flag should not exist. But we're not there, yet. Some ++ * devices fail to instantiate with cryptic error messages. ++ * Others instantiate, but don't work. Exposing users to such ++ * behavior would be cruel; clearing this flag will protect them. ++ * It should never be cleared without a comment explaining why it ++ * is cleared. ++ * ++ * TODO remove once we're there ++ */ ++ bool user_creatable; ++ bool hotpluggable; ++ ++ /* callbacks */ ++ /** ++ * @reset: deprecated device reset method pointer ++ * ++ * Modern code should use the ResettableClass interface to ++ * implement a multi-phase reset. ++ * ++ * TODO: remove once every reset callback is unused ++ */ ++ DeviceReset reset; ++ DeviceRealize realize; ++ DeviceUnrealize unrealize; ++ ++ /** ++ * @vmsd: device state serialisation description for ++ * migration/save/restore ++ */ ++ const VMStateDescription *vmsd; ++ ++ /** ++ * @bus_type: bus type ++ * private: to qdev / bus. ++ */ ++ const char *bus_type; ++}; ++ ++typedef struct NamedGPIOList NamedGPIOList; ++ ++struct NamedGPIOList { ++ char *name; ++ qemu_irq *in; ++ int num_in; ++ int num_out; ++ QLIST_ENTRY(NamedGPIOList) node; ++}; ++ ++typedef struct Clock Clock; ++typedef struct NamedClockList NamedClockList; ++ ++struct NamedClockList { ++ char *name; ++ Clock *clock; ++ bool output; ++ bool alias; ++ QLIST_ENTRY(NamedClockList) node; ++}; ++ ++typedef struct { ++ bool engaged_in_io; ++} MemReentrancyGuard; ++ ++ ++typedef QLIST_HEAD(, NamedGPIOList) NamedGPIOListHead; ++typedef QLIST_HEAD(, NamedClockList) NamedClockListHead; ++typedef QLIST_HEAD(, BusState) BusStateHead; ++ ++/** ++ * struct DeviceState - common device state, accessed with qdev helpers ++ * ++ * This structure should not be accessed directly. We declare it here ++ * so that it can be embedded in individual device state structures. ++ */ ++struct DeviceState { ++ /* private: */ ++ Object parent_obj; ++ /* public: */ ++ ++ /** ++ * @id: global device id ++ */ ++ char *id; ++ /** ++ * @canonical_path: canonical path of realized device in the QOM tree ++ */ ++ char *canonical_path; ++ /** ++ * @realized: has device been realized? ++ */ ++ bool realized; ++ /** ++ * @pending_deleted_event: track pending deletion events during unplug ++ */ ++ bool pending_deleted_event; ++ /** ++ * @pending_deleted_expires_ms: optional timeout for deletion events ++ */ ++ int64_t pending_deleted_expires_ms; ++ /** ++ * @opts: QDict of options for the device ++ */ ++ QDict *opts; ++ /** ++ * @hotplugged: was device added after PHASE_MACHINE_READY? ++ */ ++ int hotplugged; ++ /** ++ * @allow_unplug_during_migration: can device be unplugged during migration ++ */ ++ bool allow_unplug_during_migration; ++ /** ++ * @parent_bus: bus this device belongs to ++ */ ++ BusState *parent_bus; ++ /** ++ * @gpios: QLIST of named GPIOs the device provides. ++ */ ++ NamedGPIOListHead gpios; ++ /** ++ * @clocks: QLIST of named clocks the device provides. ++ */ ++ NamedClockListHead clocks; ++ /** ++ * @child_bus: QLIST of child buses ++ */ ++ BusStateHead child_bus; ++ /** ++ * @num_child_bus: number of @child_bus entries ++ */ ++ int num_child_bus; ++ /** ++ * @instance_id_alias: device alias for handling legacy migration setups ++ */ ++ int instance_id_alias; ++ /** ++ * @alias_required_for_version: indicates @instance_id_alias is ++ * needed for migration ++ */ ++ int alias_required_for_version; ++ /** ++ * @reset: ResettableState for the device; handled by Resettable interface. ++ */ ++ ResettableState reset; ++ /** ++ * @unplug_blockers: list of reasons to block unplugging of device ++ */ ++ GSList *unplug_blockers; ++ /** ++ * @mem_reentrancy_guard: Is the device currently in mmio/pio/dma? ++ * ++ * Used to prevent re-entrancy confusing things. ++ */ ++ MemReentrancyGuard mem_reentrancy_guard; ++}; ++ ++typedef struct DeviceListener DeviceListener; ++struct DeviceListener { ++ void (*realize)(DeviceListener *listener, DeviceState *dev); ++ void (*unrealize)(DeviceListener *listener, DeviceState *dev); ++ /* ++ * This callback is called upon init of the DeviceState and ++ * informs qdev if a device should be visible or hidden. We can ++ * hide a failover device depending for example on the device ++ * opts. ++ * ++ * On errors, it returns false and errp is set. Device creation ++ * should fail in this case. ++ */ ++ bool (*hide_device)(DeviceListener *listener, const QDict *device_opts, ++ bool from_json, Error **errp); ++ QTAILQ_ENTRY(DeviceListener) link; ++}; ++ ++#define TYPE_BUS "bus" ++DECLARE_OBJ_CHECKERS(BusState, BusClass, ++ BUS, TYPE_BUS) ++ ++struct BusClass { ++ ObjectClass parent_class; ++ ++ /* FIXME first arg should be BusState */ ++ void (*print_dev)(Monitor *mon, DeviceState *dev, int indent); ++ char *(*get_dev_path)(DeviceState *dev); ++ ++ /* ++ * This callback is used to create Open Firmware device path in accordance ++ * with OF spec http://forthworks.com/standards/of1275.pdf. Individual bus ++ * bindings can be found at http://playground.sun.com/1275/bindings/. ++ */ ++ char *(*get_fw_dev_path)(DeviceState *dev); ++ ++ /* ++ * Return whether the device can be added to @bus, ++ * based on the address that was set (via device properties) ++ * before realize. If not, on return @errp contains the ++ * human-readable error message. ++ */ ++ bool (*check_address)(BusState *bus, DeviceState *dev, Error **errp); ++ ++ BusRealize realize; ++ BusUnrealize unrealize; ++ ++ /* maximum devices allowed on the bus, 0: no limit. */ ++ int max_dev; ++ /* number of automatically allocated bus ids (e.g. ide.0) */ ++ int automatic_ids; ++}; ++ ++typedef struct BusChild { ++ struct rcu_head rcu; ++ DeviceState *child; ++ int index; ++ QTAILQ_ENTRY(BusChild) sibling; ++} BusChild; ++ ++#define QDEV_HOTPLUG_HANDLER_PROPERTY "hotplug-handler" ++ ++typedef QTAILQ_HEAD(, BusChild) BusChildHead; ++typedef QLIST_ENTRY(BusState) BusStateEntry; ++ ++/** ++ * struct BusState: ++ * @obj: parent object ++ * @parent: parent Device ++ * @name: name of bus ++ * @hotplug_handler: link to a hotplug handler associated with bus. ++ * @max_index: max number of child buses ++ * @realized: is the bus itself realized? ++ * @full: is the bus full? ++ * @num_children: current number of child buses ++ */ ++struct BusState { ++ /* private: */ ++ Object obj; ++ /* public: */ ++ DeviceState *parent; ++ char *name; ++ HotplugHandler *hotplug_handler; ++ int max_index; ++ bool realized; ++ bool full; ++ int num_children; ++ ++ /** ++ * @children: an RCU protected QTAILQ, thus readers must use RCU ++ * to access it, and writers must hold the big qemu lock ++ */ ++ BusChildHead children; ++ /** ++ * @sibling: next bus ++ */ ++ BusStateEntry sibling; ++ /** ++ * @reset: ResettableState for the bus; handled by Resettable interface. ++ */ ++ ResettableState reset; ++}; ++ ++/** ++ * typedef GlobalProperty - a global property type ++ * ++ * @used: Set to true if property was used when initializing a device. ++ * @optional: If set to true, GlobalProperty will be skipped without errors ++ * if the property doesn't exist. ++ * ++ * An error is fatal for non-hotplugged devices, when the global is applied. ++ */ ++typedef struct GlobalProperty { ++ const char *driver; ++ const char *property; ++ const char *value; ++ bool used; ++ bool optional; ++} GlobalProperty; ++ ++static inline void ++compat_props_add(GPtrArray *arr, ++ GlobalProperty props[], size_t nelem) ++{ ++ int i; ++ for (i = 0; i < nelem; i++) { ++ g_ptr_array_add(arr, (void *)&props[i]); ++ } ++} ++ ++/*** Board API. This should go away once we have a machine config file. ***/ ++ ++/** ++ * qdev_new: Create a device on the heap ++ * @name: device type to create (we assert() that this type exists) ++ * ++ * This only allocates the memory and initializes the device state ++ * structure, ready for the caller to set properties if they wish. ++ * The device still needs to be realized. ++ * ++ * Return: a derived DeviceState object with a reference count of 1. ++ */ ++DeviceState *qdev_new(const char *name); ++ ++/** ++ * qdev_try_new: Try to create a device on the heap ++ * @name: device type to create ++ * ++ * This is like qdev_new(), except it returns %NULL when type @name ++ * does not exist, rather than asserting. ++ * ++ * Return: a derived DeviceState object with a reference count of 1 or ++ * NULL if type @name does not exist. ++ */ ++DeviceState *qdev_try_new(const char *name); ++ ++/** ++ * qdev_is_realized() - check if device is realized ++ * @dev: The device to check. ++ * ++ * Context: May be called outside big qemu lock. ++ * Return: true if the device has been fully constructed, false otherwise. ++ */ ++static inline bool qdev_is_realized(DeviceState *dev) ++{ ++ return qatomic_load_acquire(&dev->realized); ++} ++ ++/** ++ * qdev_realize: Realize @dev. ++ * @dev: device to realize ++ * @bus: bus to plug it into (may be NULL) ++ * @errp: pointer to error object ++ * ++ * "Realize" the device, i.e. perform the second phase of device ++ * initialization. ++ * @dev must not be plugged into a bus already. ++ * If @bus, plug @dev into @bus. This takes a reference to @dev. ++ * If @dev has no QOM parent, make one up, taking another reference. ++ * ++ * If you created @dev using qdev_new(), you probably want to use ++ * qdev_realize_and_unref() instead. ++ * ++ * Return: true on success, else false setting @errp with error ++ */ ++bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp); ++ ++/** ++ * qdev_realize_and_unref: Realize @dev and drop a reference ++ * @dev: device to realize ++ * @bus: bus to plug it into (may be NULL) ++ * @errp: pointer to error object ++ * ++ * Realize @dev and drop a reference. ++ * This is like qdev_realize(), except the caller must hold a ++ * (private) reference, which is dropped on return regardless of ++ * success or failure. Intended use:: ++ * ++ * dev = qdev_new(); ++ * [...] ++ * qdev_realize_and_unref(dev, bus, errp); ++ * ++ * Now @dev can go away without further ado. ++ * ++ * If you are embedding the device into some other QOM device and ++ * initialized it via some variant on object_initialize_child() then ++ * do not use this function, because that family of functions arrange ++ * for the only reference to the child device to be held by the parent ++ * via the child<> property, and so the reference-count-drop done here ++ * would be incorrect. For that use case you want qdev_realize(). ++ * ++ * Return: true on success, else false setting @errp with error ++ */ ++bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp); ++ ++/** ++ * qdev_unrealize: Unrealize a device ++ * @dev: device to unrealize ++ * ++ * This function will "unrealize" a device, which is the first phase ++ * of correctly destroying a device that has been realized. It will: ++ * ++ * - unrealize any child buses by calling qbus_unrealize() ++ * (this will recursively unrealize any devices on those buses) ++ * - call the unrealize method of @dev ++ * ++ * The device can then be freed by causing its reference count to go ++ * to zero. ++ * ++ * Warning: most devices in QEMU do not expect to be unrealized. Only ++ * devices which are hot-unpluggable should be unrealized (as part of ++ * the unplugging process); all other devices are expected to last for ++ * the life of the simulation and should not be unrealized and freed. ++ */ ++void qdev_unrealize(DeviceState *dev); ++void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, ++ int required_for_version); ++HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); ++HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); ++bool qdev_hotplug_allowed(DeviceState *dev, Error **errp); ++ ++/** ++ * qdev_get_hotplug_handler() - Get handler responsible for device wiring ++ * @dev: the device we want the HOTPLUG_HANDLER for. ++ * ++ * Note: in case @dev has a parent bus, it will be returned as handler unless ++ * machine handler overrides it. ++ * ++ * Return: pointer to object that implements TYPE_HOTPLUG_HANDLER interface ++ * or NULL if there aren't any. ++ */ ++HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev); ++void qdev_unplug(DeviceState *dev, Error **errp); ++void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, ++ DeviceState *dev, Error **errp); ++void qdev_machine_creation_done(void); ++bool qdev_machine_modified(void); ++ ++/** ++ * qdev_add_unplug_blocker: Add an unplug blocker to a device ++ * ++ * @dev: Device to be blocked from unplug ++ * @reason: Reason for blocking ++ */ ++void qdev_add_unplug_blocker(DeviceState *dev, Error *reason); ++ ++/** ++ * qdev_del_unplug_blocker: Remove an unplug blocker from a device ++ * ++ * @dev: Device to be unblocked ++ * @reason: Pointer to the Error used with qdev_add_unplug_blocker. ++ * Used as a handle to lookup the blocker for deletion. ++ */ ++void qdev_del_unplug_blocker(DeviceState *dev, Error *reason); ++ ++/** ++ * qdev_unplug_blocked: Confirm if a device is blocked from unplug ++ * ++ * @dev: Device to be tested ++ * @errp: The reasons why the device is blocked, if any ++ * ++ * Returns: true (also setting @errp) if device is blocked from unplug, ++ * false otherwise ++ */ ++bool qdev_unplug_blocked(DeviceState *dev, Error **errp); ++ ++/** ++ * typedef GpioPolarity - Polarity of a GPIO line ++ * ++ * GPIO lines use either positive (active-high) logic, ++ * or negative (active-low) logic. ++ * ++ * In active-high logic (%GPIO_POLARITY_ACTIVE_HIGH), a pin is ++ * active when the voltage on the pin is high (relative to ground); ++ * whereas in active-low logic (%GPIO_POLARITY_ACTIVE_LOW), a pin ++ * is active when the voltage on the pin is low (or grounded). ++ */ ++typedef enum { ++ GPIO_POLARITY_ACTIVE_LOW, ++ GPIO_POLARITY_ACTIVE_HIGH ++} GpioPolarity; ++ ++/** ++ * qdev_get_gpio_in: Get one of a device's anonymous input GPIO lines ++ * @dev: Device whose GPIO we want ++ * @n: Number of the anonymous GPIO line (which must be in range) ++ * ++ * Returns the qemu_irq corresponding to an anonymous input GPIO line ++ * (which the device has set up with qdev_init_gpio_in()). The index ++ * @n of the GPIO line must be valid (i.e. be at least 0 and less than ++ * the total number of anonymous input GPIOs the device has); this ++ * function will assert() if passed an invalid index. ++ * ++ * This function is intended to be used by board code or SoC "container" ++ * device models to wire up the GPIO lines; usually the return value ++ * will be passed to qdev_connect_gpio_out() or a similar function to ++ * connect another device's output GPIO line to this input. ++ * ++ * For named input GPIO lines, use qdev_get_gpio_in_named(). ++ * ++ * Return: qemu_irq corresponding to anonymous input GPIO line ++ */ ++qemu_irq qdev_get_gpio_in(DeviceState *dev, int n); ++ ++/** ++ * qdev_get_gpio_in_named: Get one of a device's named input GPIO lines ++ * @dev: Device whose GPIO we want ++ * @name: Name of the input GPIO array ++ * @n: Number of the GPIO line in that array (which must be in range) ++ * ++ * Returns the qemu_irq corresponding to a single input GPIO line ++ * in a named array of input GPIO lines on a device (which the device ++ * has set up with qdev_init_gpio_in_named()). ++ * The @name string must correspond to an input GPIO array which exists on ++ * the device, and the index @n of the GPIO line must be valid (i.e. ++ * be at least 0 and less than the total number of input GPIOs in that ++ * array); this function will assert() if passed an invalid name or index. ++ * ++ * For anonymous input GPIO lines, use qdev_get_gpio_in(). ++ * ++ * Return: qemu_irq corresponding to named input GPIO line ++ */ ++qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n); ++ ++/** ++ * qdev_connect_gpio_out: Connect one of a device's anonymous output GPIO lines ++ * @dev: Device whose GPIO to connect ++ * @n: Number of the anonymous output GPIO line (which must be in range) ++ * @pin: qemu_irq to connect the output line to ++ * ++ * This function connects an anonymous output GPIO line on a device ++ * up to an arbitrary qemu_irq, so that when the device asserts that ++ * output GPIO line, the qemu_irq's callback is invoked. ++ * The index @n of the GPIO line must be valid (i.e. be at least 0 and ++ * less than the total number of anonymous output GPIOs the device has ++ * created with qdev_init_gpio_out()); otherwise this function will assert(). ++ * ++ * Outbound GPIO lines can be connected to any qemu_irq, but the common ++ * case is connecting them to another device's inbound GPIO line, using ++ * the qemu_irq returned by qdev_get_gpio_in() or qdev_get_gpio_in_named(). ++ * ++ * It is not valid to try to connect one outbound GPIO to multiple ++ * qemu_irqs at once, or to connect multiple outbound GPIOs to the ++ * same qemu_irq. (Warning: there is no assertion or other guard to ++ * catch this error: the model will just not do the right thing.) ++ * Instead, for fan-out you can use the TYPE_SPLIT_IRQ device: connect ++ * a device's outbound GPIO to the splitter's input, and connect each ++ * of the splitter's outputs to a different device. For fan-in you ++ * can use the TYPE_OR_IRQ device, which is a model of a logical OR ++ * gate with multiple inputs and one output. ++ * ++ * For named output GPIO lines, use qdev_connect_gpio_out_named(). ++ */ ++void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin); ++ ++/** ++ * qdev_connect_gpio_out_named: Connect one of a device's named output ++ * GPIO lines ++ * @dev: Device whose GPIO to connect ++ * @name: Name of the output GPIO array ++ * @n: Number of the output GPIO line within that array (which must be in range) ++ * @input_pin: qemu_irq to connect the output line to ++ * ++ * This function connects a single GPIO output in a named array of output ++ * GPIO lines on a device up to an arbitrary qemu_irq, so that when the ++ * device asserts that output GPIO line, the qemu_irq's callback is invoked. ++ * The @name string must correspond to an output GPIO array which exists on ++ * the device, and the index @n of the GPIO line must be valid (i.e. ++ * be at least 0 and less than the total number of output GPIOs in that ++ * array); this function will assert() if passed an invalid name or index. ++ * ++ * Outbound GPIO lines can be connected to any qemu_irq, but the common ++ * case is connecting them to another device's inbound GPIO line, using ++ * the qemu_irq returned by qdev_get_gpio_in() or qdev_get_gpio_in_named(). ++ * ++ * It is not valid to try to connect one outbound GPIO to multiple ++ * qemu_irqs at once, or to connect multiple outbound GPIOs to the ++ * same qemu_irq; see qdev_connect_gpio_out() for details. ++ * ++ * For anonymous output GPIO lines, use qdev_connect_gpio_out(). ++ */ ++void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, ++ qemu_irq input_pin); ++ ++/** ++ * qdev_get_gpio_out_connector: Get the qemu_irq connected to an output GPIO ++ * @dev: Device whose output GPIO we are interested in ++ * @name: Name of the output GPIO array ++ * @n: Number of the output GPIO line within that array ++ * ++ * Returns whatever qemu_irq is currently connected to the specified ++ * output GPIO line of @dev. This will be NULL if the output GPIO line ++ * has never been wired up to the anything. Note that the qemu_irq ++ * returned does not belong to @dev -- it will be the input GPIO or ++ * IRQ of whichever device the board code has connected up to @dev's ++ * output GPIO. ++ * ++ * You probably don't need to use this function -- it is used only ++ * by the platform-bus subsystem. ++ * ++ * Return: qemu_irq associated with GPIO or NULL if un-wired. ++ */ ++qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n); ++ ++/** ++ * qdev_intercept_gpio_out: Intercept an existing GPIO connection ++ * @dev: Device to intercept the outbound GPIO line from ++ * @icpt: New qemu_irq to connect instead ++ * @name: Name of the output GPIO array ++ * @n: Number of the GPIO line in the array ++ * ++ * .. note:: ++ * This function is provided only for use by the qtest testing framework ++ * and is not suitable for use in non-testing parts of QEMU. ++ * ++ * This function breaks an existing connection of an outbound GPIO ++ * line from @dev, and replaces it with the new qemu_irq @icpt, as if ++ * ``qdev_connect_gpio_out_named(dev, icpt, name, n)`` had been called. ++ * The previously connected qemu_irq is returned, so it can be restored ++ * by a second call to qdev_intercept_gpio_out() if desired. ++ * ++ * Return: old disconnected qemu_irq if one existed ++ */ ++qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt, ++ const char *name, int n); ++ ++BusState *qdev_get_child_bus(DeviceState *dev, const char *name); ++ ++/*** Device API. ***/ ++ ++/** ++ * qdev_init_gpio_in: create an array of anonymous input GPIO lines ++ * @dev: Device to create input GPIOs for ++ * @handler: Function to call when GPIO line value is set ++ * @n: Number of GPIO lines to create ++ * ++ * Devices should use functions in the qdev_init_gpio_in* family in ++ * their instance_init or realize methods to create any input GPIO ++ * lines they need. There is no functional difference between ++ * anonymous and named GPIO lines. Stylistically, named GPIOs are ++ * preferable (easier to understand at callsites) unless a device ++ * has exactly one uniform kind of GPIO input whose purpose is obvious. ++ * Note that input GPIO lines can serve as 'sinks' for IRQ lines. ++ * ++ * See qdev_get_gpio_in() for how code that uses such a device can get ++ * hold of an input GPIO line to manipulate it. ++ */ ++void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n); ++ ++/** ++ * qdev_init_gpio_out: create an array of anonymous output GPIO lines ++ * @dev: Device to create output GPIOs for ++ * @pins: Pointer to qemu_irq or qemu_irq array for the GPIO lines ++ * @n: Number of GPIO lines to create ++ * ++ * Devices should use functions in the qdev_init_gpio_out* family ++ * in their instance_init or realize methods to create any output ++ * GPIO lines they need. There is no functional difference between ++ * anonymous and named GPIO lines. Stylistically, named GPIOs are ++ * preferable (easier to understand at callsites) unless a device ++ * has exactly one uniform kind of GPIO output whose purpose is obvious. ++ * ++ * The @pins argument should be a pointer to either a "qemu_irq" ++ * (if @n == 1) or a "qemu_irq []" array (if @n > 1) in the device's ++ * state structure. The device implementation can then raise and ++ * lower the GPIO line by calling qemu_set_irq(). (If anything is ++ * connected to the other end of the GPIO this will cause the handler ++ * function for that input GPIO to be called.) ++ * ++ * See qdev_connect_gpio_out() for how code that uses such a device ++ * can connect to one of its output GPIO lines. ++ * ++ * There is no need to release the @pins allocated array because it ++ * will be automatically released when @dev calls its instance_finalize() ++ * handler. ++ */ ++void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n); ++ ++/** ++ * qdev_init_gpio_out_named: create an array of named output GPIO lines ++ * @dev: Device to create output GPIOs for ++ * @pins: Pointer to qemu_irq or qemu_irq array for the GPIO lines ++ * @name: Name to give this array of GPIO lines ++ * @n: Number of GPIO lines to create in this array ++ * ++ * Like qdev_init_gpio_out(), but creates an array of GPIO output lines ++ * with a name. Code using the device can then connect these GPIO lines ++ * using qdev_connect_gpio_out_named(). ++ */ ++void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, ++ const char *name, int n); ++ ++/** ++ * qdev_init_gpio_in_named_with_opaque() - create an array of input GPIO lines ++ * @dev: Device to create input GPIOs for ++ * @handler: Function to call when GPIO line value is set ++ * @opaque: Opaque data pointer to pass to @handler ++ * @name: Name of the GPIO input (must be unique for this device) ++ * @n: Number of GPIO lines in this input set ++ */ ++void qdev_init_gpio_in_named_with_opaque(DeviceState *dev, ++ qemu_irq_handler handler, ++ void *opaque, ++ const char *name, int n); ++ ++/** ++ * qdev_init_gpio_in_named() - create an array of input GPIO lines ++ * @dev: device to add array to ++ * @handler: a &typedef qemu_irq_handler function to call when GPIO is set ++ * @name: Name of the GPIO input (must be unique for this device) ++ * @n: Number of GPIO lines in this input set ++ * ++ * Like qdev_init_gpio_in_named_with_opaque(), but the opaque pointer ++ * passed to the handler is @dev (which is the most commonly desired behaviour). ++ */ ++static inline void qdev_init_gpio_in_named(DeviceState *dev, ++ qemu_irq_handler handler, ++ const char *name, int n) ++{ ++ qdev_init_gpio_in_named_with_opaque(dev, handler, dev, name, n); ++} ++ ++/** ++ * qdev_pass_gpios: create GPIO lines on container which pass through to device ++ * @dev: Device which has GPIO lines ++ * @container: Container device which needs to expose them ++ * @name: Name of GPIO array to pass through (NULL for the anonymous GPIO array) ++ * ++ * In QEMU, complicated devices like SoCs are often modelled with a ++ * "container" QOM device which itself contains other QOM devices and ++ * which wires them up appropriately. This function allows the container ++ * to create GPIO arrays on itself which simply pass through to a GPIO ++ * array of one of its internal devices. ++ * ++ * If @dev has both input and output GPIOs named @name then both will ++ * be passed through. It is not possible to pass a subset of the array ++ * with this function. ++ * ++ * To users of the container device, the GPIO array created on @container ++ * behaves exactly like any other. ++ */ ++void qdev_pass_gpios(DeviceState *dev, DeviceState *container, ++ const char *name); ++ ++BusState *qdev_get_parent_bus(const DeviceState *dev); ++ ++/*** BUS API. ***/ ++ ++DeviceState *qdev_find_recursive(BusState *bus, const char *id); ++ ++/* Returns 0 to walk children, > 0 to skip walk, < 0 to terminate walk. */ ++typedef int (qbus_walkerfn)(BusState *bus, void *opaque); ++typedef int (qdev_walkerfn)(DeviceState *dev, void *opaque); ++ ++void qbus_init(void *bus, size_t size, const char *typename, ++ DeviceState *parent, const char *name); ++BusState *qbus_new(const char *typename, DeviceState *parent, const char *name); ++bool qbus_realize(BusState *bus, Error **errp); ++void qbus_unrealize(BusState *bus); ++ ++/* Returns > 0 if either devfn or busfn skip walk somewhere in cursion, ++ * < 0 if either devfn or busfn terminate walk somewhere in cursion, ++ * 0 otherwise. */ ++int qbus_walk_children(BusState *bus, ++ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn, ++ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, ++ void *opaque); ++int qdev_walk_children(DeviceState *dev, ++ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn, ++ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, ++ void *opaque); ++ ++/** ++ * device_cold_reset() - perform a recursive cold reset on a device ++ * @dev: device to reset. ++ * ++ * Reset device @dev and perform a recursive processing using the resettable ++ * interface. It triggers a RESET_TYPE_COLD. ++ */ ++void device_cold_reset(DeviceState *dev); ++ ++/** ++ * bus_cold_reset() - perform a recursive cold reset on a bus ++ * @bus: bus to reset ++ * ++ * Reset bus @bus and perform a recursive processing using the resettable ++ * interface. It triggers a RESET_TYPE_COLD. ++ */ ++void bus_cold_reset(BusState *bus); ++ ++/** ++ * device_is_in_reset() - check device reset state ++ * @dev: device to check ++ * ++ * Return: true if the device @dev is currently being reset. ++ */ ++bool device_is_in_reset(DeviceState *dev); ++ ++/** ++ * bus_is_in_reset() - check bus reset state ++ * @bus: bus to check ++ * ++ * Return: true if the bus @bus is currently being reset. ++ */ ++bool bus_is_in_reset(BusState *bus); ++ ++/* This should go away once we get rid of the NULL bus hack */ ++BusState *sysbus_get_default(void); ++ ++char *qdev_get_fw_dev_path(DeviceState *dev); ++char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev); ++ ++/** ++ * device_class_set_props(): add a set of properties to an device ++ * @dc: the parent DeviceClass all devices inherit ++ * @props: an array of properties, terminate by DEFINE_PROP_END_OF_LIST() ++ * ++ * This will add a set of properties to the object. It will fault if ++ * you attempt to add an existing property defined by a parent class. ++ * To modify an inherited property you need to use???? ++ */ ++void device_class_set_props(DeviceClass *dc, Property *props); ++ ++/** ++ * device_class_set_parent_reset() - legacy set device reset handlers ++ * @dc: device class ++ * @dev_reset: function pointer to reset handler ++ * @parent_reset: function pointer to parents reset handler ++ * ++ * Modern code should use the ResettableClass interface to ++ * implement a multi-phase reset instead. ++ * ++ * TODO: remove the function when DeviceClass's reset method ++ * is not used anymore. ++ */ ++void device_class_set_parent_reset(DeviceClass *dc, ++ DeviceReset dev_reset, ++ DeviceReset *parent_reset); ++ ++/** ++ * device_class_set_parent_realize() - set up for chaining realize fns ++ * @dc: The device class ++ * @dev_realize: the device realize function ++ * @parent_realize: somewhere to save the parents realize function ++ * ++ * This is intended to be used when the new realize function will ++ * eventually call its parent realization function during creation. ++ * This requires storing the function call somewhere (usually in the ++ * instance structure) so you can eventually call ++ * dc->parent_realize(dev, errp) ++ */ ++void device_class_set_parent_realize(DeviceClass *dc, ++ DeviceRealize dev_realize, ++ DeviceRealize *parent_realize); ++ ++ ++/** ++ * device_class_set_parent_unrealize() - set up for chaining unrealize fns ++ * @dc: The device class ++ * @dev_unrealize: the device realize function ++ * @parent_unrealize: somewhere to save the parents unrealize function ++ * ++ * This is intended to be used when the new unrealize function will ++ * eventually call its parent unrealization function during the ++ * unrealize phase. This requires storing the function call somewhere ++ * (usually in the instance structure) so you can eventually call ++ * dc->parent_unrealize(dev); ++ */ ++void device_class_set_parent_unrealize(DeviceClass *dc, ++ DeviceUnrealize dev_unrealize, ++ DeviceUnrealize *parent_unrealize); ++ ++const VMStateDescription *qdev_get_vmsd(DeviceState *dev); ++ ++const char *qdev_fw_name(DeviceState *dev); ++ ++void qdev_assert_realized_properly(void); ++Object *qdev_get_machine(void); ++ ++/** ++ * qdev_get_human_name() - Return a human-readable name for a device ++ * @dev: The device. Must be a valid and non-NULL pointer. ++ * ++ * .. note:: ++ * This function is intended for user friendly error messages. ++ * ++ * Returns: A newly allocated string containing the device id if not null, ++ * else the object canonical path. ++ * ++ * Use g_free() to free it. ++ */ ++char *qdev_get_human_name(DeviceState *dev); ++ ++/* FIXME: make this a link<> */ ++bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp); ++ ++extern bool qdev_hot_removed; ++ ++char *qdev_get_dev_path(DeviceState *dev); ++ ++void qbus_set_hotplug_handler(BusState *bus, Object *handler); ++void qbus_set_bus_hotplug_handler(BusState *bus); ++ ++static inline bool qbus_is_hotpluggable(BusState *bus) ++{ ++ HotplugHandler *plug_handler = bus->hotplug_handler; ++ bool ret = !!plug_handler; ++ ++ if (plug_handler) { ++ HotplugHandlerClass *hdc; ++ ++ hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); ++ if (hdc->is_hotpluggable_bus) { ++ ret = hdc->is_hotpluggable_bus(plug_handler, bus); ++ } ++ } ++ return ret; ++} ++ ++/** ++ * qbus_mark_full: Mark this bus as full, so no more devices can be attached ++ * @bus: Bus to mark as full ++ * ++ * By default, QEMU will allow devices to be plugged into a bus up ++ * to the bus class's device count limit. Calling this function ++ * marks a particular bus as full, so that no more devices can be ++ * plugged into it. In particular this means that the bus will not ++ * be considered as a candidate for plugging in devices created by ++ * the user on the commandline or via the monitor. ++ * If a machine has multiple buses of a given type, such as I2C, ++ * where some of those buses in the real hardware are used only for ++ * internal devices and some are exposed via expansion ports, you ++ * can use this function to mark the internal-only buses as full ++ * after you have created all their internal devices. Then user ++ * created devices will appear on the expansion-port bus where ++ * guest software expects them. ++ */ ++static inline void qbus_mark_full(BusState *bus) ++{ ++ bus->full = true; ++} ++ ++void device_listener_register(DeviceListener *listener); ++void device_listener_unregister(DeviceListener *listener); ++ ++/** ++ * qdev_should_hide_device() - check if device should be hidden ++ * ++ * @opts: options QDict ++ * @from_json: true if @opts entries are typed, false for all strings ++ * @errp: pointer to error object ++ * ++ * When a device is added via qdev_device_add() this will be called. ++ * ++ * Return: if the device should be added now or not. ++ */ ++bool qdev_should_hide_device(const QDict *opts, bool from_json, Error **errp); ++ ++typedef enum MachineInitPhase { ++ /* current_machine is NULL. */ ++ PHASE_NO_MACHINE, ++ ++ /* current_machine is not NULL, but current_machine->accel is NULL. */ ++ PHASE_MACHINE_CREATED, ++ ++ /* ++ * current_machine->accel is not NULL, but the machine properties have ++ * not been validated and machine_class->init has not yet been called. ++ */ ++ PHASE_ACCEL_CREATED, ++ ++ /* ++ * Late backend objects have been created and initialized. ++ */ ++ PHASE_LATE_BACKENDS_CREATED, ++ ++ /* ++ * machine_class->init has been called, thus creating any embedded ++ * devices and validating machine properties. Devices created at ++ * this time are considered to be cold-plugged. ++ */ ++ PHASE_MACHINE_INITIALIZED, ++ ++ /* ++ * QEMU is ready to start CPUs and devices created at this time ++ * are considered to be hot-plugged. The monitor is not restricted ++ * to "preconfig" commands. ++ */ ++ PHASE_MACHINE_READY, ++} MachineInitPhase; ++ ++bool phase_check(MachineInitPhase phase); ++void phase_advance(MachineInitPhase phase); ++ ++#endif +diff --git a/include/io/channel-file.h b/include/io/channel-file.h +new file mode 100644 +index 00000000..d373a4e4 +--- /dev/null ++++ b/include/io/channel-file.h +@@ -0,0 +1,110 @@ ++/* ++ * QEMU I/O channels files driver ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_CHANNEL_FILE_H ++#define QIO_CHANNEL_FILE_H ++ ++#include "io/channel.h" ++#include "qom/object.h" ++ ++#define TYPE_QIO_CHANNEL_FILE "qio-channel-file" ++OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelFile, QIO_CHANNEL_FILE) ++ ++ ++/** ++ * QIOChannelFile: ++ * ++ * The QIOChannelFile object provides a channel implementation ++ * that is able to perform I/O on block devices, character ++ * devices, FIFOs, pipes and plain files. While it is technically ++ * able to work on sockets too on the UNIX platform, this is not ++ * portable to Windows and lacks some extra sockets specific ++ * functionality. So the QIOChannelSocket object is recommended ++ * for that use case. ++ * ++ */ ++ ++struct QIOChannelFile { ++ QIOChannel parent; ++ int fd; ++}; ++ ++ ++/** ++ * qio_channel_file_new_fd: ++ * @fd: the file descriptor ++ * ++ * Create a new IO channel object for a file represented ++ * by the @fd parameter. @fd can be associated with a ++ * block device, character device, fifo, pipe, or a ++ * regular file. For sockets, the QIOChannelSocket class ++ * should be used instead, as this provides greater ++ * functionality and cross platform portability. ++ * ++ * The channel will own the passed in file descriptor ++ * and will take responsibility for closing it, so the ++ * caller must not close it. If appropriate the caller ++ * should dup() its FD before opening the channel. ++ * ++ * Returns: the new channel object ++ */ ++QIOChannelFile * ++qio_channel_file_new_fd(int fd); ++ ++/** ++ * qio_channel_file_new_dupfd: ++ * @fd: the file descriptor ++ * @errp: pointer to initialized error object ++ * ++ * Create a new IO channel object for a file represented by the @fd ++ * parameter. Like qio_channel_file_new_fd(), but the @fd is first ++ * duplicated with dup(). ++ * ++ * The channel will own the duplicated file descriptor and will take ++ * responsibility for closing it, the original FD is owned by the ++ * caller. ++ * ++ * Returns: the new channel object ++ */ ++QIOChannelFile * ++qio_channel_file_new_dupfd(int fd, Error **errp); ++ ++/** ++ * qio_channel_file_new_path: ++ * @path: the file path ++ * @flags: the open flags (O_RDONLY|O_WRONLY|O_RDWR, etc) ++ * @mode: the file creation mode if O_CREAT is set in @flags ++ * @errp: pointer to initialized error object ++ * ++ * Create a new IO channel object for a file represented ++ * by the @path parameter. @path can point to any ++ * type of file on which sequential I/O can be ++ * performed, whether it be a plain file, character ++ * device or block device. ++ * ++ * Returns: the new channel object ++ */ ++QIOChannelFile * ++qio_channel_file_new_path(const char *path, ++ int flags, ++ mode_t mode, ++ Error **errp); ++ ++#endif /* QIO_CHANNEL_FILE_H */ +diff --git a/include/io/channel-socket.h b/include/io/channel-socket.h +new file mode 100644 +index 00000000..ab15577d +--- /dev/null ++++ b/include/io/channel-socket.h +@@ -0,0 +1,265 @@ ++/* ++ * QEMU I/O channels sockets driver ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_CHANNEL_SOCKET_H ++#define QIO_CHANNEL_SOCKET_H ++ ++#include "io/channel.h" ++#include "io/task.h" ++#include "qemu/sockets.h" ++#include "qom/object.h" ++ ++#define TYPE_QIO_CHANNEL_SOCKET "qio-channel-socket" ++OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelSocket, QIO_CHANNEL_SOCKET) ++ ++ ++/** ++ * QIOChannelSocket: ++ * ++ * The QIOChannelSocket class provides a channel implementation ++ * that can transport data over a UNIX socket or TCP socket. ++ * Beyond the core channel API, it also provides functionality ++ * for accepting client connections, tuning some socket ++ * parameters and getting socket address strings. ++ */ ++ ++struct QIOChannelSocket { ++ QIOChannel parent; ++ int fd; ++ struct sockaddr_storage localAddr; ++ socklen_t localAddrLen; ++ struct sockaddr_storage remoteAddr; ++ socklen_t remoteAddrLen; ++ ssize_t zero_copy_queued; ++ ssize_t zero_copy_sent; ++}; ++ ++ ++/** ++ * qio_channel_socket_new: ++ * ++ * Create a channel for performing I/O on a socket ++ * connection, that is initially closed. After ++ * creating the socket, it must be setup as a client ++ * connection or server. ++ * ++ * Returns: the socket channel object ++ */ ++QIOChannelSocket * ++qio_channel_socket_new(void); ++ ++/** ++ * qio_channel_socket_new_fd: ++ * @fd: the socket file descriptor ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Create a channel for performing I/O on the socket ++ * connection represented by the file descriptor @fd. ++ * ++ * Returns: the socket channel object, or NULL on error ++ */ ++QIOChannelSocket * ++qio_channel_socket_new_fd(int fd, ++ Error **errp); ++ ++ ++/** ++ * qio_channel_socket_connect_sync: ++ * @ioc: the socket channel object ++ * @addr: the address to connect to ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Attempt to connect to the address @addr. This method ++ * will run in the foreground so the caller will not regain ++ * execution control until the connection is established or ++ * an error occurs. ++ */ ++int qio_channel_socket_connect_sync(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ Error **errp); ++ ++/** ++ * qio_channel_socket_connect_async: ++ * @ioc: the socket channel object ++ * @addr: the address to connect to ++ * @callback: the function to invoke on completion ++ * @opaque: user data to pass to @callback ++ * @destroy: the function to free @opaque ++ * @context: the context to run the async task. If %NULL, the default ++ * context will be used. ++ * ++ * Attempt to connect to the address @addr. This method ++ * will run in the background so the caller will regain ++ * execution control immediately. The function @callback ++ * will be invoked on completion or failure. The @addr ++ * parameter will be copied, so may be freed as soon ++ * as this function returns without waiting for completion. ++ */ ++void qio_channel_socket_connect_async(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ QIOTaskFunc callback, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context); ++ ++ ++/** ++ * qio_channel_socket_listen_sync: ++ * @ioc: the socket channel object ++ * @addr: the address to listen to ++ * @num: the expected amount of connections ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Attempt to listen to the address @addr. This method ++ * will run in the foreground so the caller will not regain ++ * execution control until the connection is established or ++ * an error occurs. ++ */ ++int qio_channel_socket_listen_sync(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ int num, ++ Error **errp); ++ ++/** ++ * qio_channel_socket_listen_async: ++ * @ioc: the socket channel object ++ * @addr: the address to listen to ++ * @num: the expected amount of connections ++ * @callback: the function to invoke on completion ++ * @opaque: user data to pass to @callback ++ * @destroy: the function to free @opaque ++ * @context: the context to run the async task. If %NULL, the default ++ * context will be used. ++ * ++ * Attempt to listen to the address @addr. This method ++ * will run in the background so the caller will regain ++ * execution control immediately. The function @callback ++ * will be invoked on completion or failure. The @addr ++ * parameter will be copied, so may be freed as soon ++ * as this function returns without waiting for completion. ++ */ ++void qio_channel_socket_listen_async(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ int num, ++ QIOTaskFunc callback, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context); ++ ++ ++/** ++ * qio_channel_socket_dgram_sync: ++ * @ioc: the socket channel object ++ * @localAddr: the address to local bind address ++ * @remoteAddr: the address to remote peer address ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Attempt to initialize a datagram socket bound to ++ * @localAddr and communicating with peer @remoteAddr. ++ * This method will run in the foreground so the caller ++ * will not regain execution control until the socket ++ * is established or an error occurs. ++ */ ++int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc, ++ SocketAddress *localAddr, ++ SocketAddress *remoteAddr, ++ Error **errp); ++ ++/** ++ * qio_channel_socket_dgram_async: ++ * @ioc: the socket channel object ++ * @localAddr: the address to local bind address ++ * @remoteAddr: the address to remote peer address ++ * @callback: the function to invoke on completion ++ * @opaque: user data to pass to @callback ++ * @destroy: the function to free @opaque ++ * @context: the context to run the async task. If %NULL, the default ++ * context will be used. ++ * ++ * Attempt to initialize a datagram socket bound to ++ * @localAddr and communicating with peer @remoteAddr. ++ * This method will run in the background so the caller ++ * will regain execution control immediately. The function ++ * @callback will be invoked on completion or failure. ++ * The @localAddr and @remoteAddr parameters will be copied, ++ * so may be freed as soon as this function returns without ++ * waiting for completion. ++ */ ++void qio_channel_socket_dgram_async(QIOChannelSocket *ioc, ++ SocketAddress *localAddr, ++ SocketAddress *remoteAddr, ++ QIOTaskFunc callback, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context); ++ ++ ++/** ++ * qio_channel_socket_get_local_address: ++ * @ioc: the socket channel object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Get the string representation of the local socket ++ * address. A pointer to the allocated address information ++ * struct will be returned, which the caller is required to ++ * release with a call qapi_free_SocketAddress() when no ++ * longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++SocketAddress * ++qio_channel_socket_get_local_address(QIOChannelSocket *ioc, ++ Error **errp); ++ ++/** ++ * qio_channel_socket_get_remote_address: ++ * @ioc: the socket channel object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Get the string representation of the local socket ++ * address. A pointer to the allocated address information ++ * struct will be returned, which the caller is required to ++ * release with a call qapi_free_SocketAddress() when no ++ * longer required. ++ * ++ * Returns: the socket address struct, or NULL on error ++ */ ++SocketAddress * ++qio_channel_socket_get_remote_address(QIOChannelSocket *ioc, ++ Error **errp); ++ ++ ++/** ++ * qio_channel_socket_accept: ++ * @ioc: the socket channel object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * If the socket represents a server, then this accepts ++ * a new client connection. The returned channel will ++ * represent the connected client socket. ++ * ++ * Returns: the new client channel, or NULL on error ++ */ ++QIOChannelSocket * ++qio_channel_socket_accept(QIOChannelSocket *ioc, ++ Error **errp); ++ ++ ++#endif /* QIO_CHANNEL_SOCKET_H */ +diff --git a/include/io/channel-tls.h b/include/io/channel-tls.h +new file mode 100644 +index 00000000..26c67f17 +--- /dev/null ++++ b/include/io/channel-tls.h +@@ -0,0 +1,146 @@ ++/* ++ * QEMU I/O channels TLS driver ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_CHANNEL_TLS_H ++#define QIO_CHANNEL_TLS_H ++ ++#include "io/channel.h" ++#include "io/task.h" ++#include "crypto/tlssession.h" ++#include "qom/object.h" ++ ++#define TYPE_QIO_CHANNEL_TLS "qio-channel-tls" ++OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelTLS, QIO_CHANNEL_TLS) ++ ++ ++/** ++ * QIOChannelTLS ++ * ++ * The QIOChannelTLS class provides a channel wrapper which ++ * can transparently run the TLS encryption protocol. It is ++ * usually used over a TCP socket, but there is actually no ++ * technical restriction on which type of master channel is ++ * used as the transport. ++ * ++ * This channel object is capable of running as either a ++ * TLS server or TLS client. ++ */ ++ ++struct QIOChannelTLS { ++ QIOChannel parent; ++ QIOChannel *master; ++ QCryptoTLSSession *session; ++ QIOChannelShutdown shutdown; ++ guint hs_ioc_tag; ++}; ++ ++/** ++ * qio_channel_tls_new_server: ++ * @master: the underlying channel object ++ * @creds: the credentials to use for TLS handshake ++ * @aclname: the access control list for validating clients ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Create a new TLS channel that runs the server side of ++ * a TLS session. The TLS session handshake will use the ++ * credentials provided in @creds. If the @aclname parameter ++ * is non-NULL, then the client will have to provide ++ * credentials (ie a x509 client certificate) which will ++ * then be validated against the ACL. ++ * ++ * After creating the channel, it is mandatory to call ++ * the qio_channel_tls_handshake() method before attempting ++ * todo any I/O on the channel. ++ * ++ * Once the handshake has completed, all I/O should be done ++ * via the new TLS channel object and not the original ++ * master channel ++ * ++ * Returns: the new TLS channel object, or NULL ++ */ ++QIOChannelTLS * ++qio_channel_tls_new_server(QIOChannel *master, ++ QCryptoTLSCreds *creds, ++ const char *aclname, ++ Error **errp); ++ ++/** ++ * qio_channel_tls_new_client: ++ * @master: the underlying channel object ++ * @creds: the credentials to use for TLS handshake ++ * @hostname: the user specified server hostname ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Create a new TLS channel that runs the client side of ++ * a TLS session. The TLS session handshake will use the ++ * credentials provided in @creds. The @hostname parameter ++ * should provide the user specified hostname of the server ++ * and will be validated against the server's credentials ++ * (ie CommonName of the x509 certificate) ++ * ++ * After creating the channel, it is mandatory to call ++ * the qio_channel_tls_handshake() method before attempting ++ * todo any I/O on the channel. ++ * ++ * Once the handshake has completed, all I/O should be done ++ * via the new TLS channel object and not the original ++ * master channel ++ * ++ * Returns: the new TLS channel object, or NULL ++ */ ++QIOChannelTLS * ++qio_channel_tls_new_client(QIOChannel *master, ++ QCryptoTLSCreds *creds, ++ const char *hostname, ++ Error **errp); ++ ++/** ++ * qio_channel_tls_handshake: ++ * @ioc: the TLS channel object ++ * @func: the callback to invoke when completed ++ * @opaque: opaque data to pass to @func ++ * @destroy: optional callback to free @opaque ++ * @context: the context that TLS handshake will run with. If %NULL, ++ * the default context will be used ++ * ++ * Perform the TLS session handshake. This method ++ * will return immediately and the handshake will ++ * continue in the background, provided the main ++ * loop is running. When the handshake is complete, ++ * or fails, the @func callback will be invoked. ++ */ ++void qio_channel_tls_handshake(QIOChannelTLS *ioc, ++ QIOTaskFunc func, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context); ++ ++/** ++ * qio_channel_tls_get_session: ++ * @ioc: the TLS channel object ++ * ++ * Get the TLS session used by the channel. ++ * ++ * Returns: the TLS session ++ */ ++QCryptoTLSSession * ++qio_channel_tls_get_session(QIOChannelTLS *ioc); ++ ++#endif /* QIO_CHANNEL_TLS_H */ +diff --git a/include/io/channel-util.h b/include/io/channel-util.h +new file mode 100644 +index 00000000..fa18a375 +--- /dev/null ++++ b/include/io/channel-util.h +@@ -0,0 +1,75 @@ ++/* ++ * QEMU I/O channels utility APIs ++ * ++ * Copyright (c) 2016 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_CHANNEL_UTIL_H ++#define QIO_CHANNEL_UTIL_H ++ ++#include "io/channel.h" ++ ++/* ++ * This module provides helper functions that are useful when dealing ++ * with QIOChannel objects ++ */ ++ ++ ++/** ++ * qio_channel_new_fd: ++ * @fd: the file descriptor ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Create a channel for performing I/O on the file ++ * descriptor @fd. The particular subclass of QIOChannel ++ * that is returned will depend on what underlying object ++ * the file descriptor is associated with. It may be either ++ * a QIOChannelSocket or a QIOChannelFile instance. Upon ++ * success, the returned QIOChannel instance will own ++ * the @fd file descriptor, and take responsibility for ++ * closing it when no longer required. On failure, the ++ * caller is responsible for closing @fd. ++ * ++ * Returns: the channel object, or NULL on error ++ */ ++QIOChannel *qio_channel_new_fd(int fd, ++ Error **errp); ++ ++/** ++ * qio_channel_util_set_aio_fd_handler: ++ * @read_fd: the file descriptor for the read handler ++ * @read_ctx: the AioContext for the read handler ++ * @io_read: the read handler ++ * @write_fd: the file descriptor for the write handler ++ * @write_ctx: the AioContext for the write handler ++ * @io_write: the write handler ++ * @opaque: the opaque argument to the read and write handler ++ * ++ * Set the read and write handlers when @read_ctx and @write_ctx are non-NULL, ++ * respectively. To leave a handler in its current state, pass a NULL ++ * AioContext. To clear a handler, pass a non-NULL AioContext and a NULL ++ * handler. ++ */ ++void qio_channel_util_set_aio_fd_handler(int read_fd, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ int write_fd, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque); ++ ++#endif /* QIO_CHANNEL_UTIL_H */ +diff --git a/include/io/channel-watch.h b/include/io/channel-watch.h +new file mode 100644 +index 00000000..a36aab8f +--- /dev/null ++++ b/include/io/channel-watch.h +@@ -0,0 +1,90 @@ ++/* ++ * QEMU I/O channels watch helper APIs ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_CHANNEL_WATCH_H ++#define QIO_CHANNEL_WATCH_H ++ ++#include "io/channel.h" ++ ++/* ++ * This module provides helper functions that will be needed by ++ * the various QIOChannel implementations, for creating watches ++ * on file descriptors / sockets ++ */ ++ ++/** ++ * qio_channel_create_fd_watch: ++ * @ioc: the channel object ++ * @fd: the file descriptor ++ * @condition: the I/O condition ++ * ++ * Create a new main loop source that is able to ++ * monitor the file descriptor @fd for the ++ * I/O conditions in @condition. This is able ++ * monitor block devices, character devices, ++ * pipes but not plain files or, on Win32, sockets. ++ * ++ * Returns: the new main loop source ++ */ ++GSource *qio_channel_create_fd_watch(QIOChannel *ioc, ++ int fd, ++ GIOCondition condition); ++ ++/** ++ * qio_channel_create_socket_watch: ++ * @ioc: the channel object ++ * @fd: the file descriptor ++ * @condition: the I/O condition ++ * ++ * Create a new main loop source that is able to ++ * monitor the file descriptor @fd for the ++ * I/O conditions in @condition. This is equivalent ++ * to qio_channel_create_fd_watch on POSIX systems ++ * but not on Windows. ++ * ++ * Returns: the new main loop source ++ */ ++GSource *qio_channel_create_socket_watch(QIOChannel *ioc, ++ int fd, ++ GIOCondition condition); ++ ++/** ++ * qio_channel_create_fd_pair_watch: ++ * @ioc: the channel object ++ * @fdread: the file descriptor for reading ++ * @fdwrite: the file descriptor for writing ++ * @condition: the I/O condition ++ * ++ * Create a new main loop source that is able to ++ * monitor the pair of file descriptors @fdread ++ * and @fdwrite for the I/O conditions in @condition. ++ * This is intended for monitoring unidirectional ++ * file descriptors such as pipes, where a pair ++ * of descriptors is required for bidirectional ++ * I/O ++ * ++ * Returns: the new main loop source ++ */ ++GSource *qio_channel_create_fd_pair_watch(QIOChannel *ioc, ++ int fdread, ++ int fdwrite, ++ GIOCondition condition); ++ ++#endif /* QIO_CHANNEL_WATCH_H */ +diff --git a/include/io/channel.h b/include/io/channel.h +new file mode 100644 +index 00000000..bdf0bca9 +--- /dev/null ++++ b/include/io/channel.h +@@ -0,0 +1,1005 @@ ++/* ++ * QEMU I/O channels ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_CHANNEL_H ++#define QIO_CHANNEL_H ++ ++#include "qom/object.h" ++#include "qemu/coroutine-core.h" ++#include "block/aio.h" ++ ++#define TYPE_QIO_CHANNEL "qio-channel" ++OBJECT_DECLARE_TYPE(QIOChannel, QIOChannelClass, ++ QIO_CHANNEL) ++ ++ ++#define QIO_CHANNEL_ERR_BLOCK -2 ++ ++#define QIO_CHANNEL_WRITE_FLAG_ZERO_COPY 0x1 ++ ++#define QIO_CHANNEL_READ_FLAG_MSG_PEEK 0x1 ++ ++typedef enum QIOChannelFeature QIOChannelFeature; ++ ++enum QIOChannelFeature { ++ QIO_CHANNEL_FEATURE_FD_PASS, ++ QIO_CHANNEL_FEATURE_SHUTDOWN, ++ QIO_CHANNEL_FEATURE_LISTEN, ++ QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY, ++ QIO_CHANNEL_FEATURE_READ_MSG_PEEK, ++ QIO_CHANNEL_FEATURE_SEEKABLE, ++}; ++ ++ ++typedef enum QIOChannelShutdown QIOChannelShutdown; ++ ++enum QIOChannelShutdown { ++ QIO_CHANNEL_SHUTDOWN_READ = 1, ++ QIO_CHANNEL_SHUTDOWN_WRITE = 2, ++ QIO_CHANNEL_SHUTDOWN_BOTH = 3, ++}; ++ ++typedef gboolean (*QIOChannelFunc)(QIOChannel *ioc, ++ GIOCondition condition, ++ gpointer data); ++ ++/** ++ * QIOChannel: ++ * ++ * The QIOChannel defines the core API for a generic I/O channel ++ * class hierarchy. It is inspired by GIOChannel, but has the ++ * following differences ++ * ++ * - Use QOM to properly support arbitrary subclassing ++ * - Support use of iovecs for efficient I/O with multiple blocks ++ * - None of the character set translation, binary data exclusively ++ * - Direct support for QEMU Error object reporting ++ * - File descriptor passing ++ * ++ * This base class is abstract so cannot be instantiated. There ++ * will be subclasses for dealing with sockets, files, and higher ++ * level protocols such as TLS, WebSocket, etc. ++ */ ++ ++struct QIOChannel { ++ Object parent; ++ unsigned int features; /* bitmask of QIOChannelFeatures */ ++ char *name; ++ AioContext *read_ctx; ++ Coroutine *read_coroutine; ++ AioContext *write_ctx; ++ Coroutine *write_coroutine; ++ bool follow_coroutine_ctx; ++#ifdef _WIN32 ++ HANDLE event; /* For use with GSource on Win32 */ ++#endif ++}; ++ ++/** ++ * QIOChannelClass: ++ * ++ * This class defines the contract that all subclasses ++ * must follow to provide specific channel implementations. ++ * The first five callbacks are mandatory to support, others ++ * provide additional optional features. ++ * ++ * Consult the corresponding public API docs for a description ++ * of the semantics of each callback. io_shutdown in particular ++ * must be thread-safe, terminate quickly and must not block. ++ */ ++struct QIOChannelClass { ++ ObjectClass parent; ++ ++ /* Mandatory callbacks */ ++ ssize_t (*io_writev)(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, ++ size_t nfds, ++ int flags, ++ Error **errp); ++ ssize_t (*io_readv)(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, ++ size_t *nfds, ++ int flags, ++ Error **errp); ++ int (*io_close)(QIOChannel *ioc, ++ Error **errp); ++ GSource * (*io_create_watch)(QIOChannel *ioc, ++ GIOCondition condition); ++ int (*io_set_blocking)(QIOChannel *ioc, ++ bool enabled, ++ Error **errp); ++ ++ /* Optional callbacks */ ++ ssize_t (*io_pwritev)(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ off_t offset, ++ Error **errp); ++ ssize_t (*io_preadv)(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ off_t offset, ++ Error **errp); ++ int (*io_shutdown)(QIOChannel *ioc, ++ QIOChannelShutdown how, ++ Error **errp); ++ void (*io_set_cork)(QIOChannel *ioc, ++ bool enabled); ++ void (*io_set_delay)(QIOChannel *ioc, ++ bool enabled); ++ off_t (*io_seek)(QIOChannel *ioc, ++ off_t offset, ++ int whence, ++ Error **errp); ++ void (*io_set_aio_fd_handler)(QIOChannel *ioc, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque); ++ int (*io_flush)(QIOChannel *ioc, ++ Error **errp); ++ int (*io_peerpid)(QIOChannel *ioc, ++ unsigned int *pid, ++ Error **errp); ++}; ++ ++/* General I/O handling functions */ ++ ++/** ++ * qio_channel_has_feature: ++ * @ioc: the channel object ++ * @feature: the feature to check support of ++ * ++ * Determine whether the channel implementation supports ++ * the optional feature named in @feature. ++ * ++ * Returns: true if supported, false otherwise. ++ */ ++bool qio_channel_has_feature(QIOChannel *ioc, ++ QIOChannelFeature feature); ++ ++/** ++ * qio_channel_set_feature: ++ * @ioc: the channel object ++ * @feature: the feature to set support for ++ * ++ * Add channel support for the feature named in @feature. ++ */ ++void qio_channel_set_feature(QIOChannel *ioc, ++ QIOChannelFeature feature); ++ ++/** ++ * qio_channel_set_name: ++ * @ioc: the channel object ++ * @name: the name of the channel ++ * ++ * Sets the name of the channel, which serves as an aid ++ * to debugging. The name is used when creating GSource ++ * watches for this channel. ++ */ ++void qio_channel_set_name(QIOChannel *ioc, ++ const char *name); ++ ++/** ++ * qio_channel_readv_full: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data into ++ * @niov: the length of the @iov array ++ * @fds: pointer to an array that will received file handles ++ * @nfds: pointer filled with number of elements in @fds on return ++ * @flags: read flags (QIO_CHANNEL_READ_FLAG_*) ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Read data from the IO channel, storing it in the ++ * memory regions referenced by @iov. Each element ++ * in the @iov will be fully populated with data ++ * before the next one is used. The @niov parameter ++ * specifies the total number of elements in @iov. ++ * ++ * It is not required for all @iov to be filled with ++ * data. If the channel is in blocking mode, at least ++ * one byte of data will be read, but no more is ++ * guaranteed. If the channel is non-blocking and no ++ * data is available, it will return QIO_CHANNEL_ERR_BLOCK ++ * ++ * If the channel has passed any file descriptors, ++ * the @fds array pointer will be allocated and ++ * the elements filled with the received file ++ * descriptors. The @nfds pointer will be updated ++ * to indicate the size of the @fds array that ++ * was allocated. It is the callers responsibility ++ * to call close() on each file descriptor and to ++ * call g_free() on the array pointer in @fds. ++ * ++ * It is an error to pass a non-NULL @fds parameter ++ * unless qio_channel_has_feature() returns a true ++ * value for the QIO_CHANNEL_FEATURE_FD_PASS constant. ++ * ++ * Returns: the number of bytes read, or -1 on error, ++ * or QIO_CHANNEL_ERR_BLOCK if no data is available ++ * and the channel is non-blocking ++ */ ++ssize_t qio_channel_readv_full(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, ++ size_t *nfds, ++ int flags, ++ Error **errp); ++ ++ ++/** ++ * qio_channel_writev_full: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to write data from ++ * @niov: the length of the @iov array ++ * @fds: an array of file handles to send ++ * @nfds: number of file handles in @fds ++ * @flags: write flags (QIO_CHANNEL_WRITE_FLAG_*) ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Write data to the IO channel, reading it from the ++ * memory regions referenced by @iov. Each element ++ * in the @iov will be fully sent, before the next ++ * one is used. The @niov parameter specifies the ++ * total number of elements in @iov. ++ * ++ * It is not required for all @iov data to be fully ++ * sent. If the channel is in blocking mode, at least ++ * one byte of data will be sent, but no more is ++ * guaranteed. If the channel is non-blocking and no ++ * data can be sent, it will return QIO_CHANNEL_ERR_BLOCK ++ * ++ * If there are file descriptors to send, the @fds ++ * array should be non-NULL and provide the handles. ++ * All file descriptors will be sent if at least one ++ * byte of data was sent. ++ * ++ * It is an error to pass a non-NULL @fds parameter ++ * unless qio_channel_has_feature() returns a true ++ * value for the QIO_CHANNEL_FEATURE_FD_PASS constant. ++ * ++ * Returns: the number of bytes sent, or -1 on error, ++ * or QIO_CHANNEL_ERR_BLOCK if no data is can be sent ++ * and the channel is non-blocking ++ */ ++ssize_t qio_channel_writev_full(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, ++ size_t nfds, ++ int flags, ++ Error **errp); ++ ++/** ++ * qio_channel_readv_all_eof: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data into ++ * @niov: the length of the @iov array ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Read data from the IO channel, storing it in the ++ * memory regions referenced by @iov. Each element ++ * in the @iov will be fully populated with data ++ * before the next one is used. The @niov parameter ++ * specifies the total number of elements in @iov. ++ * ++ * The function will wait for all requested data ++ * to be read, yielding from the current coroutine ++ * if required. ++ * ++ * If end-of-file occurs before any data is read, ++ * no error is reported; otherwise, if it occurs ++ * before all requested data has been read, an error ++ * will be reported. ++ * ++ * Returns: 1 if all bytes were read, 0 if end-of-file ++ * occurs without data, or -1 on error ++ */ ++int coroutine_mixed_fn qio_channel_readv_all_eof(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp); ++ ++/** ++ * qio_channel_readv_all: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data into ++ * @niov: the length of the @iov array ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Read data from the IO channel, storing it in the ++ * memory regions referenced by @iov. Each element ++ * in the @iov will be fully populated with data ++ * before the next one is used. The @niov parameter ++ * specifies the total number of elements in @iov. ++ * ++ * The function will wait for all requested data ++ * to be read, yielding from the current coroutine ++ * if required. ++ * ++ * If end-of-file occurs before all requested data ++ * has been read, an error will be reported. ++ * ++ * Returns: 0 if all bytes were read, or -1 on error ++ */ ++int coroutine_mixed_fn qio_channel_readv_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp); ++ ++ ++/** ++ * qio_channel_writev_all: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to write data from ++ * @niov: the length of the @iov array ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Write data to the IO channel, reading it from the ++ * memory regions referenced by @iov. Each element ++ * in the @iov will be fully sent, before the next ++ * one is used. The @niov parameter specifies the ++ * total number of elements in @iov. ++ * ++ * The function will wait for all requested data ++ * to be written, yielding from the current coroutine ++ * if required. ++ * ++ * Returns: 0 if all bytes were written, or -1 on error ++ */ ++int coroutine_mixed_fn qio_channel_writev_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp); ++ ++/** ++ * qio_channel_readv: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data into ++ * @niov: the length of the @iov array ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Behaves as qio_channel_readv_full() but does not support ++ * receiving of file handles. ++ */ ++ssize_t qio_channel_readv(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp); ++ ++/** ++ * qio_channel_writev: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to write data from ++ * @niov: the length of the @iov array ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Behaves as qio_channel_writev_full() but does not support ++ * sending of file handles. ++ */ ++ssize_t qio_channel_writev(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp); ++ ++/** ++ * qio_channel_read: ++ * @ioc: the channel object ++ * @buf: the memory region to read data into ++ * @buflen: the length of @buf ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Behaves as qio_channel_readv_full() but does not support ++ * receiving of file handles, and only supports reading into ++ * a single memory region. ++ */ ++ssize_t qio_channel_read(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp); ++ ++/** ++ * qio_channel_write: ++ * @ioc: the channel object ++ * @buf: the memory regions to send data from ++ * @buflen: the length of @buf ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Behaves as qio_channel_writev_full() but does not support ++ * sending of file handles, and only supports writing from a ++ * single memory region. ++ */ ++ssize_t qio_channel_write(QIOChannel *ioc, ++ const char *buf, ++ size_t buflen, ++ Error **errp); ++ ++/** ++ * qio_channel_read_all_eof: ++ * @ioc: the channel object ++ * @buf: the memory region to read data into ++ * @buflen: the number of bytes to @buf ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Reads @buflen bytes into @buf, possibly blocking or (if the ++ * channel is non-blocking) yielding from the current coroutine ++ * multiple times until the entire content is read. If end-of-file ++ * occurs immediately it is not an error, but if it occurs after ++ * data has been read it will return an error rather than a ++ * short-read. Otherwise behaves as qio_channel_read(). ++ * ++ * Returns: 1 if all bytes were read, 0 if end-of-file occurs ++ * without data, or -1 on error ++ */ ++int coroutine_mixed_fn qio_channel_read_all_eof(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp); ++ ++/** ++ * qio_channel_read_all: ++ * @ioc: the channel object ++ * @buf: the memory region to read data into ++ * @buflen: the number of bytes to @buf ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Reads @buflen bytes into @buf, possibly blocking or (if the ++ * channel is non-blocking) yielding from the current coroutine ++ * multiple times until the entire content is read. If end-of-file ++ * occurs it will return an error rather than a short-read. Otherwise ++ * behaves as qio_channel_read(). ++ * ++ * Returns: 0 if all bytes were read, or -1 on error ++ */ ++int coroutine_mixed_fn qio_channel_read_all(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp); ++ ++/** ++ * qio_channel_write_all: ++ * @ioc: the channel object ++ * @buf: the memory region to write data into ++ * @buflen: the number of bytes to @buf ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Writes @buflen bytes from @buf, possibly blocking or (if the ++ * channel is non-blocking) yielding from the current coroutine ++ * multiple times until the entire content is written. Otherwise ++ * behaves as qio_channel_write(). ++ * ++ * Returns: 0 if all bytes were written, or -1 on error ++ */ ++int coroutine_mixed_fn qio_channel_write_all(QIOChannel *ioc, ++ const char *buf, ++ size_t buflen, ++ Error **errp); ++ ++/** ++ * qio_channel_set_blocking: ++ * @ioc: the channel object ++ * @enabled: the blocking flag state ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * If @enabled is true, then the channel is put into ++ * blocking mode, otherwise it will be non-blocking. ++ * ++ * In non-blocking mode, read/write operations may ++ * return QIO_CHANNEL_ERR_BLOCK if they would otherwise ++ * block on I/O ++ */ ++int qio_channel_set_blocking(QIOChannel *ioc, ++ bool enabled, ++ Error **errp); ++ ++/** ++ * qio_channel_set_follow_coroutine_ctx: ++ * @ioc: the channel object ++ * @enabled: whether or not to follow the coroutine's AioContext ++ * ++ * If @enabled is true, calls to qio_channel_yield() use the current ++ * coroutine's AioContext. Usually this is desirable. ++ * ++ * If @enabled is false, calls to qio_channel_yield() use the global iohandler ++ * AioContext. This is may be used by coroutines that run in the main loop and ++ * do not wish to respond to I/O during nested event loops. This is the ++ * default for compatibility with code that is not aware of AioContexts. ++ */ ++void qio_channel_set_follow_coroutine_ctx(QIOChannel *ioc, bool enabled); ++ ++/** ++ * qio_channel_close: ++ * @ioc: the channel object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Close the channel, flushing any pending I/O ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qio_channel_close(QIOChannel *ioc, ++ Error **errp); ++ ++/** ++ * qio_channel_pwritev ++ * @ioc: the channel object ++ * @iov: the array of memory regions to write data from ++ * @niov: the length of the @iov array ++ * @offset: offset in the channel where writes should begin ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Not all implementations will support this facility, so may report ++ * an error. To avoid errors, the caller may check for the feature ++ * flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method. ++ * ++ * Behaves as qio_channel_writev_full, apart from not supporting ++ * sending of file handles as well as beginning the write at the ++ * passed @offset ++ * ++ */ ++ssize_t qio_channel_pwritev(QIOChannel *ioc, const struct iovec *iov, ++ size_t niov, off_t offset, Error **errp); ++ ++/** ++ * qio_channel_pwrite ++ * @ioc: the channel object ++ * @buf: the memory region to write data into ++ * @buflen: the number of bytes to @buf ++ * @offset: offset in the channel where writes should begin ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Not all implementations will support this facility, so may report ++ * an error. To avoid errors, the caller may check for the feature ++ * flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method. ++ * ++ */ ++ssize_t qio_channel_pwrite(QIOChannel *ioc, char *buf, size_t buflen, ++ off_t offset, Error **errp); ++ ++/** ++ * qio_channel_preadv ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data into ++ * @niov: the length of the @iov array ++ * @offset: offset in the channel where writes should begin ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Not all implementations will support this facility, so may report ++ * an error. To avoid errors, the caller may check for the feature ++ * flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method. ++ * ++ * Behaves as qio_channel_readv_full, apart from not supporting ++ * receiving of file handles as well as beginning the read at the ++ * passed @offset ++ * ++ */ ++ssize_t qio_channel_preadv(QIOChannel *ioc, const struct iovec *iov, ++ size_t niov, off_t offset, Error **errp); ++ ++/** ++ * qio_channel_pread ++ * @ioc: the channel object ++ * @buf: the memory region to write data into ++ * @buflen: the number of bytes to @buf ++ * @offset: offset in the channel where writes should begin ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Not all implementations will support this facility, so may report ++ * an error. To avoid errors, the caller may check for the feature ++ * flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method. ++ * ++ */ ++ssize_t qio_channel_pread(QIOChannel *ioc, char *buf, size_t buflen, ++ off_t offset, Error **errp); ++ ++/** ++ * qio_channel_shutdown: ++ * @ioc: the channel object ++ * @how: the direction to shutdown ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Shutdowns transmission and/or receiving of data ++ * without closing the underlying transport. ++ * ++ * Not all implementations will support this facility, ++ * so may report an error. To avoid errors, the ++ * caller may check for the feature flag ++ * QIO_CHANNEL_FEATURE_SHUTDOWN prior to calling ++ * this method. ++ * ++ * This function is thread-safe, terminates quickly and does not block. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qio_channel_shutdown(QIOChannel *ioc, ++ QIOChannelShutdown how, ++ Error **errp); ++ ++/** ++ * qio_channel_set_delay: ++ * @ioc: the channel object ++ * @enabled: the new flag state ++ * ++ * Controls whether the underlying transport is ++ * permitted to delay writes in order to merge ++ * small packets. If @enabled is true, then the ++ * writes may be delayed in order to opportunistically ++ * merge small packets into larger ones. If @enabled ++ * is false, writes are dispatched immediately with ++ * no delay. ++ * ++ * When @enabled is false, applications may wish to ++ * use the qio_channel_set_cork() method to explicitly ++ * control write merging. ++ * ++ * On channels which are backed by a socket, this ++ * API corresponds to the inverse of TCP_NODELAY flag, ++ * controlling whether the Nagle algorithm is active. ++ * ++ * This setting is merely a hint, so implementations are ++ * free to ignore this without it being considered an ++ * error. ++ */ ++void qio_channel_set_delay(QIOChannel *ioc, ++ bool enabled); ++ ++/** ++ * qio_channel_set_cork: ++ * @ioc: the channel object ++ * @enabled: the new flag state ++ * ++ * Controls whether the underlying transport is ++ * permitted to dispatch data that is written. ++ * If @enabled is true, then any data written will ++ * be queued in local buffers until @enabled is ++ * set to false once again. ++ * ++ * This feature is typically used when the automatic ++ * write coalescing facility is disabled via the ++ * qio_channel_set_delay() method. ++ * ++ * On channels which are backed by a socket, this ++ * API corresponds to the TCP_CORK flag. ++ * ++ * This setting is merely a hint, so implementations are ++ * free to ignore this without it being considered an ++ * error. ++ */ ++void qio_channel_set_cork(QIOChannel *ioc, ++ bool enabled); ++ ++ ++/** ++ * qio_channel_seek: ++ * @ioc: the channel object ++ * @offset: the position to seek to, relative to @whence ++ * @whence: one of the (POSIX) SEEK_* constants listed below ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Moves the current I/O position within the channel ++ * @ioc, to be @offset. The value of @offset is ++ * interpreted relative to @whence: ++ * ++ * SEEK_SET - the position is set to @offset bytes ++ * SEEK_CUR - the position is moved by @offset bytes ++ * SEEK_END - the position is set to end of the file plus @offset bytes ++ * ++ * Not all implementations will support this facility, ++ * so may report an error. ++ * ++ * Returns: the new position on success, (off_t)-1 on failure ++ */ ++off_t qio_channel_io_seek(QIOChannel *ioc, ++ off_t offset, ++ int whence, ++ Error **errp); ++ ++ ++/** ++ * qio_channel_create_watch: ++ * @ioc: the channel object ++ * @condition: the I/O condition to monitor ++ * ++ * Create a new main loop source that is used to watch ++ * for the I/O condition @condition. Typically the ++ * qio_channel_add_watch() method would be used instead ++ * of this, since it directly attaches a callback to ++ * the source ++ * ++ * Returns: the new main loop source. ++ */ ++GSource *qio_channel_create_watch(QIOChannel *ioc, ++ GIOCondition condition); ++ ++/** ++ * qio_channel_add_watch: ++ * @ioc: the channel object ++ * @condition: the I/O condition to monitor ++ * @func: callback to invoke when the source becomes ready ++ * @user_data: opaque data to pass to @func ++ * @notify: callback to free @user_data ++ * ++ * Create a new main loop source that is used to watch ++ * for the I/O condition @condition. The callback @func ++ * will be registered against the source, to be invoked ++ * when the source becomes ready. The optional @user_data ++ * will be passed to @func when it is invoked. The @notify ++ * callback will be used to free @user_data when the ++ * watch is deleted ++ * ++ * The returned source ID can be used with g_source_remove() ++ * to remove and free the source when no longer required. ++ * Alternatively the @func callback can return a FALSE ++ * value. ++ * ++ * Returns: the source ID ++ */ ++guint qio_channel_add_watch(QIOChannel *ioc, ++ GIOCondition condition, ++ QIOChannelFunc func, ++ gpointer user_data, ++ GDestroyNotify notify); ++ ++/** ++ * qio_channel_add_watch_full: ++ * @ioc: the channel object ++ * @condition: the I/O condition to monitor ++ * @func: callback to invoke when the source becomes ready ++ * @user_data: opaque data to pass to @func ++ * @notify: callback to free @user_data ++ * @context: the context to run the watch source ++ * ++ * Similar as qio_channel_add_watch(), but allows to specify context ++ * to run the watch source. ++ * ++ * Returns: the source ID ++ */ ++guint qio_channel_add_watch_full(QIOChannel *ioc, ++ GIOCondition condition, ++ QIOChannelFunc func, ++ gpointer user_data, ++ GDestroyNotify notify, ++ GMainContext *context); ++ ++/** ++ * qio_channel_add_watch_source: ++ * @ioc: the channel object ++ * @condition: the I/O condition to monitor ++ * @func: callback to invoke when the source becomes ready ++ * @user_data: opaque data to pass to @func ++ * @notify: callback to free @user_data ++ * @context: gcontext to bind the source to ++ * ++ * Similar as qio_channel_add_watch(), but allows to specify context ++ * to run the watch source, meanwhile return the GSource object ++ * instead of tag ID, with the GSource referenced already. ++ * ++ * Note: callers is responsible to unref the source when not needed. ++ * ++ * Returns: the source pointer ++ */ ++GSource *qio_channel_add_watch_source(QIOChannel *ioc, ++ GIOCondition condition, ++ QIOChannelFunc func, ++ gpointer user_data, ++ GDestroyNotify notify, ++ GMainContext *context); ++ ++/** ++ * qio_channel_yield: ++ * @ioc: the channel object ++ * @condition: the I/O condition to wait for ++ * ++ * Yields execution from the current coroutine until the condition ++ * indicated by @condition becomes available. @condition must ++ * be either %G_IO_IN or %G_IO_OUT; it cannot contain both. In ++ * addition, no two coroutine can be waiting on the same condition ++ * and channel at the same time. ++ * ++ * This must only be called from coroutine context. It is safe to ++ * reenter the coroutine externally while it is waiting; in this ++ * case the function will return even if @condition is not yet ++ * available. ++ */ ++void coroutine_fn qio_channel_yield(QIOChannel *ioc, ++ GIOCondition condition); ++ ++/** ++ * qio_channel_wake_read: ++ * @ioc: the channel object ++ * ++ * If qio_channel_yield() is currently waiting for the channel to become ++ * readable, interrupt it and reenter immediately. This function is safe to call ++ * from any thread. ++ */ ++void qio_channel_wake_read(QIOChannel *ioc); ++ ++/** ++ * qio_channel_wait: ++ * @ioc: the channel object ++ * @condition: the I/O condition to wait for ++ * ++ * Block execution from the current thread until ++ * the condition indicated by @condition becomes ++ * available. ++ * ++ * This will enter a nested event loop to perform ++ * the wait. ++ */ ++void qio_channel_wait(QIOChannel *ioc, ++ GIOCondition condition); ++ ++/** ++ * qio_channel_set_aio_fd_handler: ++ * @ioc: the channel object ++ * @read_ctx: the AioContext to set the read handler on or NULL ++ * @io_read: the read handler ++ * @write_ctx: the AioContext to set the write handler on or NULL ++ * @io_write: the write handler ++ * @opaque: the opaque value passed to the handler ++ * ++ * This is used internally by qio_channel_yield(). It can ++ * be used by channel implementations to forward the handlers ++ * to another channel (e.g. from #QIOChannelTLS to the ++ * underlying socket). ++ * ++ * When @read_ctx is NULL, don't touch the read handler. When @write_ctx is ++ * NULL, don't touch the write handler. Note that setting the read handler ++ * clears the write handler, and vice versa, if they share the same AioContext. ++ * Therefore the caller must pass both handlers together when sharing the same ++ * AioContext. ++ */ ++void qio_channel_set_aio_fd_handler(QIOChannel *ioc, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque); ++ ++/** ++ * qio_channel_readv_full_all_eof: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data to ++ * @niov: the length of the @iov array ++ * @fds: an array of file handles to read ++ * @nfds: number of file handles in @fds ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * ++ * Performs same function as qio_channel_readv_all_eof. ++ * Additionally, attempts to read file descriptors shared ++ * over the channel. The function will wait for all ++ * requested data to be read, yielding from the current ++ * coroutine if required. data refers to both file ++ * descriptors and the iovs. ++ * ++ * Returns: 1 if all bytes were read, 0 if end-of-file ++ * occurs without data, or -1 on error ++ */ ++ ++int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, size_t *nfds, ++ Error **errp); ++ ++/** ++ * qio_channel_readv_full_all: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data to ++ * @niov: the length of the @iov array ++ * @fds: an array of file handles to read ++ * @nfds: number of file handles in @fds ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * ++ * Performs same function as qio_channel_readv_all_eof. ++ * Additionally, attempts to read file descriptors shared ++ * over the channel. The function will wait for all ++ * requested data to be read, yielding from the current ++ * coroutine if required. data refers to both file ++ * descriptors and the iovs. ++ * ++ * Returns: 0 if all bytes were read, or -1 on error ++ */ ++ ++int coroutine_mixed_fn qio_channel_readv_full_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, size_t *nfds, ++ Error **errp); ++ ++/** ++ * qio_channel_writev_full_all: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to write data from ++ * @niov: the length of the @iov array ++ * @fds: an array of file handles to send ++ * @nfds: number of file handles in @fds ++ * @flags: write flags (QIO_CHANNEL_WRITE_FLAG_*) ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * ++ * Behaves like qio_channel_writev_full but will attempt ++ * to send all data passed (file handles and memory regions). ++ * The function will wait for all requested data ++ * to be written, yielding from the current coroutine ++ * if required. ++ * ++ * If QIO_CHANNEL_WRITE_FLAG_ZERO_COPY is passed in flags, ++ * instead of waiting for all requested data to be written, ++ * this function will wait until it's all queued for writing. ++ * In this case, if the buffer gets changed between queueing and ++ * sending, the updated buffer will be sent. If this is not a ++ * desired behavior, it's suggested to call qio_channel_flush() ++ * before reusing the buffer. ++ * ++ * Returns: 0 if all bytes were written, or -1 on error ++ */ ++ ++int coroutine_mixed_fn qio_channel_writev_full_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, size_t nfds, ++ int flags, Error **errp); ++ ++/** ++ * qio_channel_flush: ++ * @ioc: the channel object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Will block until every packet queued with ++ * qio_channel_writev_full() + QIO_CHANNEL_WRITE_FLAG_ZERO_COPY ++ * is sent, or return in case of any error. ++ * ++ * If not implemented, acts as a no-op, and returns 0. ++ * ++ * Returns -1 if any error is found, ++ * 1 if every send failed to use zero copy. ++ * 0 otherwise. ++ */ ++ ++int qio_channel_flush(QIOChannel *ioc, ++ Error **errp); ++ ++/** ++ * qio_channel_get_peercred: ++ * @ioc: the channel object ++ * @pid: pointer to pid ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Returns the pid of the peer process connected to this socket. ++ * ++ * The use of this function is possible only for connected ++ * AF_UNIX stream sockets and for AF_UNIX stream and datagram ++ * socket pairs on Linux. ++ * Return -1 on error with pid -1 for the non-Linux OS. ++ * ++ */ ++int qio_channel_get_peerpid(QIOChannel *ioc, ++ unsigned int *pid, ++ Error **errp); ++ ++#endif /* QIO_CHANNEL_H */ +diff --git a/include/io/task.h b/include/io/task.h +new file mode 100644 +index 00000000..0b5342ee +--- /dev/null ++++ b/include/io/task.h +@@ -0,0 +1,350 @@ ++/* ++ * QEMU I/O task ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_TASK_H ++#define QIO_TASK_H ++ ++typedef struct QIOTask QIOTask; ++ ++typedef void (*QIOTaskFunc)(QIOTask *task, ++ gpointer opaque); ++ ++typedef void (*QIOTaskWorker)(QIOTask *task, ++ gpointer opaque); ++ ++/** ++ * QIOTask: ++ * ++ * The QIOTask object provides a simple mechanism for reporting ++ * success / failure of long running background operations. ++ * ++ * A object on which the operation is to be performed could have ++ * a public API which accepts a task callback: ++ * ++ * ++ * Task function signature ++ * ++ * void myobject_operation(QMyObject *obj, ++ * QIOTaskFunc *func, ++ * gpointer opaque, ++ * GDestroyNotify notify); ++ * ++ * ++ * ++ * The 'func' parameter is the callback to be invoked, and 'opaque' ++ * is data to pass to it. The optional 'notify' function is used ++ * to free 'opaque' when no longer needed. ++ * ++ * When the operation completes, the 'func' callback will be ++ * invoked, allowing the calling code to determine the result ++ * of the operation. An example QIOTaskFunc implementation may ++ * look like ++ * ++ * ++ * Task callback implementation ++ * ++ * static void myobject_operation_notify(QIOTask *task, ++ * gpointer opaque) ++ * { ++ * Error *err = NULL; ++ * if (qio_task_propagate_error(task, &err)) { ++ * ...deal with the failure... ++ * error_free(err); ++ * } else { ++ * QMyObject *src = QMY_OBJECT(qio_task_get_source(task)); ++ * ...deal with the completion... ++ * } ++ * } ++ * ++ * ++ * ++ * Now, lets say the implementation of the method using the ++ * task wants to set a timer to run once a second checking ++ * for completion of some activity. It would do something ++ * like ++ * ++ * ++ * Task function implementation ++ * ++ * void myobject_operation(QMyObject *obj, ++ * QIOTaskFunc *func, ++ * gpointer opaque, ++ * GDestroyNotify notify) ++ * { ++ * QIOTask *task; ++ * ++ * task = qio_task_new(OBJECT(obj), func, opaque, notify); ++ * ++ * g_timeout_add_full(G_PRIORITY_DEFAULT, ++ * 1000, ++ * myobject_operation_timer, ++ * task, ++ * NULL); ++ * } ++ * ++ * ++ * ++ * It could equally have setup a watch on a file descriptor or ++ * created a background thread, or something else entirely. ++ * Notice that the source object is passed to the task, and ++ * QIOTask will hold a reference on that. This ensure that ++ * the QMyObject instance cannot be garbage collected while ++ * the async task is still in progress. ++ * ++ * In this case, myobject_operation_timer will fire after ++ * 3 secs and do ++ * ++ * ++ * Task timer function ++ * ++ * gboolean myobject_operation_timer(gpointer opaque) ++ * { ++ * QIOTask *task = QIO_TASK(opaque); ++ * Error *err = NULL; ++ * ++ * ...check something important... ++ * if (err) { ++ * qio_task_set_error(task, err); ++ * qio_task_complete(task); ++ * return FALSE; ++ * } else if (...work is completed ...) { ++ * qio_task_complete(task); ++ * return FALSE; ++ * } ++ * ...carry on polling ... ++ * return TRUE; ++ * } ++ * ++ * ++ * ++ * The 'qio_task_complete' call in this method will trigger ++ * the callback func 'myobject_operation_notify' shown ++ * earlier to deal with the results. ++ * ++ * Once this function returns false, object_unref will be called ++ * automatically on the task causing it to be released and the ++ * ref on QMyObject dropped too. ++ * ++ * The QIOTask module can also be used to perform operations ++ * in a background thread context, while still reporting the ++ * results in the main event thread. This allows code which ++ * cannot easily be rewritten to be asynchronous (such as DNS ++ * lookups) to be easily run non-blocking. Reporting the ++ * results in the main thread context means that the caller ++ * typically does not need to be concerned about thread ++ * safety wrt the BQL. ++ * ++ * For example, the socket_listen() method will block the caller ++ * while DNS lookups take place if given a name, instead of IP ++ * address. The C library often do not provide a practical async ++ * DNS API, so the to get non-blocking DNS lookups in a portable ++ * manner requires use of a thread. So achieve a non-blocking ++ * socket listen using QIOTask would require: ++ * ++ * ++ * static void myobject_listen_worker(QIOTask *task, ++ * gpointer opaque) ++ * { ++ * QMyObject obj = QMY_OBJECT(qio_task_get_source(task)); ++ * SocketAddress *addr = opaque; ++ * Error *err = NULL; ++ * ++ * obj->fd = socket_listen(addr, &err); ++ * ++ qio_task_set_error(task, err); ++ * } ++ * ++ * void myobject_listen_async(QMyObject *obj, ++ * SocketAddress *addr, ++ * QIOTaskFunc *func, ++ * gpointer opaque, ++ * GDestroyNotify notify) ++ * { ++ * QIOTask *task; ++ * SocketAddress *addrCopy; ++ * ++ * addrCopy = QAPI_CLONE(SocketAddress, addr); ++ * task = qio_task_new(OBJECT(obj), func, opaque, notify); ++ * ++ * qio_task_run_in_thread(task, myobject_listen_worker, ++ * addrCopy, ++ * qapi_free_SocketAddress); ++ * } ++ * ++ * ++ * NB, The 'func' callback passed into myobject_listen_async ++ * will be invoked from the main event thread, despite the ++ * actual operation being performed in a different thread. ++ */ ++ ++/** ++ * qio_task_new: ++ * @source: the object on which the operation is invoked ++ * @func: the callback to invoke when the task completes ++ * @opaque: opaque data to pass to @func when invoked ++ * @destroy: optional callback to free @opaque ++ * ++ * Creates a new task struct to track completion of a ++ * background operation running on the object @source. ++ * When the operation completes or fails, the callback ++ * @func will be invoked. The callback can access the ++ * 'err' attribute in the task object to determine if ++ * the operation was successful or not. ++ * ++ * The returned task will be released when qio_task_complete() ++ * is invoked. ++ * ++ * Returns: the task struct ++ */ ++QIOTask *qio_task_new(Object *source, ++ QIOTaskFunc func, ++ gpointer opaque, ++ GDestroyNotify destroy); ++ ++/** ++ * qio_task_run_in_thread: ++ * @task: the task struct ++ * @worker: the function to invoke in a thread ++ * @opaque: opaque data to pass to @worker ++ * @destroy: function to free @opaque ++ * @context: the context to run the complete hook. If %NULL, the ++ * default context will be used. ++ * ++ * Run a task in a background thread. When @worker ++ * returns it will call qio_task_complete() in ++ * the thread that is running the main loop associated ++ * with @context. ++ */ ++void qio_task_run_in_thread(QIOTask *task, ++ QIOTaskWorker worker, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context); ++ ++ ++/** ++ * qio_task_wait_thread: ++ * @task: the task struct ++ * ++ * Wait for completion of a task that was previously ++ * invoked using qio_task_run_in_thread. This MUST ++ * ONLY be invoked if the task has not already ++ * completed, since after the completion callback ++ * is invoked, @task will have been freed. ++ * ++ * To avoid racing with execution of the completion ++ * callback provided with qio_task_new, this method ++ * MUST ONLY be invoked from the thread that is ++ * running the main loop associated with @context ++ * parameter to qio_task_run_in_thread. ++ * ++ * When the thread has completed, the completion ++ * callback provided to qio_task_new will be invoked. ++ * When that callback returns @task will be freed, ++ * so @task must not be referenced after this ++ * method completes. ++ */ ++void qio_task_wait_thread(QIOTask *task); ++ ++ ++/** ++ * qio_task_complete: ++ * @task: the task struct ++ * ++ * Invoke the completion callback for @task and ++ * then free its memory. ++ */ ++void qio_task_complete(QIOTask *task); ++ ++ ++/** ++ * qio_task_set_error: ++ * @task: the task struct ++ * @err: pointer to the error, or NULL ++ * ++ * Associate an error with the task, which can later ++ * be retrieved with the qio_task_propagate_error() ++ * method. This method takes ownership of @err, so ++ * it is not valid to access it after this call ++ * completes. If @err is NULL this is a no-op. If ++ * this is call multiple times, only the first ++ * provided @err will be recorded, later ones will ++ * be discarded and freed. ++ */ ++void qio_task_set_error(QIOTask *task, ++ Error *err); ++ ++ ++/** ++ * qio_task_propagate_error: ++ * @task: the task struct ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Propagate the error associated with @task ++ * into @errp. ++ * ++ * Returns: true if an error was propagated, false otherwise ++ */ ++bool qio_task_propagate_error(QIOTask *task, ++ Error **errp); ++ ++ ++/** ++ * qio_task_set_result_pointer: ++ * @task: the task struct ++ * @result: pointer to the result data ++ * ++ * Associate an opaque result with the task, ++ * which can later be retrieved with the ++ * qio_task_get_result_pointer() method ++ * ++ */ ++void qio_task_set_result_pointer(QIOTask *task, ++ gpointer result, ++ GDestroyNotify notify); ++ ++ ++/** ++ * qio_task_get_result_pointer: ++ * @task: the task struct ++ * ++ * Retrieve the opaque result data associated ++ * with the task, if any. ++ * ++ * Returns: the task result, or NULL ++ */ ++gpointer qio_task_get_result_pointer(QIOTask *task); ++ ++ ++/** ++ * qio_task_get_source: ++ * @task: the task struct ++ * ++ * Get the source object associated with the background ++ * task. The caller does not own a reference on the ++ * returned Object, and so should call object_ref() ++ * if it wants to keep the object pointer outside the ++ * lifetime of the QIOTask object. ++ * ++ * Returns: the source object ++ */ ++Object *qio_task_get_source(QIOTask *task); ++ ++#endif /* QIO_TASK_H */ +diff --git a/include/qapi/clone-visitor.h b/include/qapi/clone-visitor.h +new file mode 100644 +index 00000000..ebc182b0 +--- /dev/null ++++ b/include/qapi/clone-visitor.h +@@ -0,0 +1,62 @@ ++/* ++ * Clone Visitor ++ * ++ * Copyright (C) 2016 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QAPI_CLONE_VISITOR_H ++#define QAPI_CLONE_VISITOR_H ++ ++#include "qapi/error.h" ++#include "qapi/visitor.h" ++ ++/* ++ * The clone visitor is for direct use only by the QAPI_CLONE() macro; ++ * it requires that the root visit occur on an object, list, or ++ * alternate, and is not usable directly on built-in QAPI types. ++ */ ++typedef struct QapiCloneVisitor QapiCloneVisitor; ++ ++Visitor *qapi_clone_visitor_new(void); ++Visitor *qapi_clone_members_visitor_new(void); ++ ++/* ++ * Deep-clone QAPI object @src of the given @type, and return the result. ++ * ++ * Not usable on QAPI scalars (integers, strings, enums), nor on a ++ * QAPI object that references the 'any' type. Safe when @src is NULL. ++ */ ++#define QAPI_CLONE(type, src) \ ++ ({ \ ++ Visitor *v_; \ ++ type *dst_ = (type *) (src); /* Cast away const */ \ ++ \ ++ if (dst_) { \ ++ v_ = qapi_clone_visitor_new(); \ ++ visit_type_ ## type(v_, NULL, &dst_, &error_abort); \ ++ visit_free(v_); \ ++ } \ ++ dst_; \ ++ }) ++ ++/* ++ * Copy deep clones of @type members from @src to @dst. ++ * ++ * Not usable on QAPI scalars (integers, strings, enums), nor on a ++ * QAPI object that references the 'any' type. ++ */ ++#define QAPI_CLONE_MEMBERS(type, dst, src) \ ++ ({ \ ++ Visitor *v_; \ ++ \ ++ v_ = qapi_clone_members_visitor_new(); \ ++ *(type *)(dst) = *(src); \ ++ visit_type_ ## type ## _members(v_, (type *)(dst), &error_abort); \ ++ visit_free(v_); \ ++ }) ++ ++#endif +diff --git a/include/qapi/compat-policy.h b/include/qapi/compat-policy.h +new file mode 100644 +index 00000000..8b7b25c0 +--- /dev/null ++++ b/include/qapi/compat-policy.h +@@ -0,0 +1,45 @@ ++/* ++ * Policy for handling "funny" management interfaces ++ * ++ * Copyright (C) 2020 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QAPI_COMPAT_POLICY_H ++#define QAPI_COMPAT_POLICY_H ++ ++#include "qapi/error.h" ++#include "qapi/qapi-types-compat.h" ++ ++extern CompatPolicy compat_policy; ++ ++bool compat_policy_input_ok(unsigned special_features, ++ const CompatPolicy *policy, ++ ErrorClass error_class, ++ const char *kind, const char *name, ++ Error **errp); ++ ++/* ++ * Create a QObject input visitor for @obj for use with QMP ++ * ++ * This is like qobject_input_visitor_new(), except it obeys the ++ * policy for handling deprecated management interfaces set with ++ * -compat. ++ */ ++Visitor *qobject_input_visitor_new_qmp(QObject *obj); ++ ++/* ++ * Create a QObject output visitor for @obj for use with QMP ++ * ++ * This is like qobject_output_visitor_new(), except it obeys the ++ * policy for handling deprecated management interfaces set with ++ * -compat. ++ */ ++Visitor *qobject_output_visitor_new_qmp(QObject **result); ++ ++#endif +diff --git a/include/qapi/dealloc-visitor.h b/include/qapi/dealloc-visitor.h +new file mode 100644 +index 00000000..c36715fd +--- /dev/null ++++ b/include/qapi/dealloc-visitor.h +@@ -0,0 +1,28 @@ ++/* ++ * Dealloc Visitor ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Michael Roth ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QAPI_DEALLOC_VISITOR_H ++#define QAPI_DEALLOC_VISITOR_H ++ ++#include "qapi/visitor.h" ++ ++typedef struct QapiDeallocVisitor QapiDeallocVisitor; ++ ++/* ++ * The dealloc visitor is primarily used only by generated ++ * qapi_free_FOO() functions, and is the only visitor designed to work ++ * correctly in the face of a partially-constructed QAPI tree. ++ */ ++Visitor *qapi_dealloc_visitor_new(void); ++ ++#endif +diff --git a/include/qapi/error.h b/include/qapi/error.h +new file mode 100644 +index 00000000..71f8fb2c +--- /dev/null ++++ b/include/qapi/error.h +@@ -0,0 +1,540 @@ ++/* ++ * QEMU Error Objects ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2011-2015 Red Hat, Inc. ++ * ++ * Authors: ++ * Anthony Liguori ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2. See ++ * the COPYING.LIB file in the top-level directory. ++ */ ++ ++/* ++ * Error reporting system loosely patterned after Glib's GError. ++ * ++ * = Rules = ++ * ++ * - Functions that use Error to report errors have an Error **errp ++ * parameter. It should be the last parameter, except for functions ++ * taking variable arguments. ++ * ++ * - You may pass NULL to not receive the error, &error_abort to abort ++ * on error, &error_fatal to exit(1) on error, or a pointer to a ++ * variable containing NULL to receive the error. ++ * ++ * - Separation of concerns: the function is responsible for detecting ++ * errors and failing cleanly; handling the error is its caller's ++ * job. Since the value of @errp is about handling the error, the ++ * function should not examine it. ++ * ++ * - The function may pass @errp to functions it calls to pass on ++ * their errors to its caller. If it dereferences @errp to check ++ * for errors, it must use ERRP_GUARD(). ++ * ++ * - On success, the function should not touch *errp. On failure, it ++ * should set a new error, e.g. with error_setg(errp, ...), or ++ * propagate an existing one, e.g. with error_propagate(errp, ...). ++ * ++ * - Whenever practical, also return a value that indicates success / ++ * failure. This can make the error checking more concise, and can ++ * avoid useless error object creation and destruction. Note that ++ * we still have many functions returning void. We recommend ++ * • bool-valued functions return true on success / false on failure, ++ * • pointer-valued functions return non-null / null pointer, and ++ * • integer-valued functions return non-negative / negative. ++ * ++ * = Creating errors = ++ * ++ * Create an error: ++ * error_setg(errp, "situation normal, all fouled up"); ++ * where @errp points to the location to receive the error. ++ * ++ * Create an error and add additional explanation: ++ * error_setg(errp, "invalid quark"); ++ * error_append_hint(errp, "Valid quarks are up, down, strange, " ++ * "charm, top, bottom.\n"); ++ * This may require use of ERRP_GUARD(); more on that below. ++ * ++ * Do *not* contract this to ++ * error_setg(errp, "invalid quark\n" // WRONG! ++ * "Valid quarks are up, down, strange, charm, top, bottom."); ++ * ++ * = Reporting and destroying errors = ++ * ++ * Report an error to the current monitor if we have one, else stderr: ++ * error_report_err(err); ++ * This frees the error object. ++ * ++ * Likewise, but with additional text prepended: ++ * error_reportf_err(err, "Could not frobnicate '%s': ", name); ++ * ++ * Report an error somewhere else: ++ * const char *msg = error_get_pretty(err); ++ * do with msg what needs to be done... ++ * error_free(err); ++ * Note that this loses hints added with error_append_hint(). ++ * ++ * Call a function ignoring errors: ++ * foo(arg, NULL); ++ * This is more concise than ++ * Error *err = NULL; ++ * foo(arg, &err); ++ * error_free(err); // don't do this ++ * ++ * Call a function aborting on errors: ++ * foo(arg, &error_abort); ++ * This is more concise and fails more nicely than ++ * Error *err = NULL; ++ * foo(arg, &err); ++ * assert(!err); // don't do this ++ * ++ * Call a function treating errors as fatal: ++ * foo(arg, &error_fatal); ++ * This is more concise than ++ * Error *err = NULL; ++ * foo(arg, &err); ++ * if (err) { // don't do this ++ * error_report_err(err); ++ * exit(1); ++ * } ++ * ++ * Handle an error without reporting it (just for completeness): ++ * error_free(err); ++ * ++ * Assert that an expected error occurred, but clean it up without ++ * reporting it (primarily useful in testsuites): ++ * error_free_or_abort(&err); ++ * ++ * = Passing errors around = ++ * ++ * Errors get passed to the caller through the conventional @errp ++ * parameter. ++ * ++ * Create a new error and pass it to the caller: ++ * error_setg(errp, "situation normal, all fouled up"); ++ * ++ * Call a function, receive an error from it, and pass it to the caller ++ * - when the function returns a value that indicates failure, say ++ * false: ++ * if (!foo(arg, errp)) { ++ * handle the error... ++ * } ++ * - when it does not, say because it is a void function: ++ * ERRP_GUARD(); ++ * foo(arg, errp); ++ * if (*errp) { ++ * handle the error... ++ * } ++ * More on ERRP_GUARD() below. ++ * ++ * Code predating ERRP_GUARD() still exists, and looks like this: ++ * Error *err = NULL; ++ * foo(arg, &err); ++ * if (err) { ++ * handle the error... ++ * error_propagate(errp, err); // deprecated ++ * } ++ * Avoid in new code. Do *not* "optimize" it to ++ * foo(arg, errp); ++ * if (*errp) { // WRONG! ++ * handle the error... ++ * } ++ * because errp may be NULL without the ERRP_GUARD() guard. ++ * ++ * But when all you do with the error is pass it on, please use ++ * foo(arg, errp); ++ * for readability. ++ * ++ * Receive an error, and handle it locally ++ * - when the function returns a value that indicates failure, say ++ * false: ++ * Error *err = NULL; ++ * if (!foo(arg, &err)) { ++ * handle the error... ++ * } ++ * - when it does not, say because it is a void function: ++ * Error *err = NULL; ++ * foo(arg, &err); ++ * if (err) { ++ * handle the error... ++ * } ++ * ++ * Pass an existing error to the caller: ++ * error_propagate(errp, err); ++ * This is rarely needed. When @err is a local variable, use of ++ * ERRP_GUARD() commonly results in more readable code. ++ * ++ * Pass an existing error to the caller with the message modified: ++ * error_propagate_prepend(errp, err, ++ * "Could not frobnicate '%s': ", name); ++ * This is more concise than ++ * error_propagate(errp, err); // don't do this ++ * error_prepend(errp, "Could not frobnicate '%s': ", name); ++ * and works even when @errp is &error_fatal. ++ * ++ * Receive and accumulate multiple errors (first one wins): ++ * Error *err = NULL, *local_err = NULL; ++ * foo(arg, &err); ++ * bar(arg, &local_err); ++ * error_propagate(&err, local_err); ++ * if (err) { ++ * handle the error... ++ * } ++ * ++ * Do *not* "optimize" this to ++ * Error *err = NULL; ++ * foo(arg, &err); ++ * bar(arg, &err); // WRONG! ++ * if (err) { ++ * handle the error... ++ * } ++ * because this may pass a non-null err to bar(). ++ * ++ * Likewise, do *not* ++ * Error *err = NULL; ++ * if (cond1) { ++ * error_setg(&err, ...); ++ * } ++ * if (cond2) { ++ * error_setg(&err, ...); // WRONG! ++ * } ++ * because this may pass a non-null err to error_setg(). ++ * ++ * = Why, when and how to use ERRP_GUARD() = ++ * ++ * Without ERRP_GUARD(), use of the @errp parameter is restricted: ++ * - It must not be dereferenced, because it may be null. ++ * - It should not be passed to error_prepend(), error_vprepend(), or ++ * error_append_hint(), because that doesn't work with &error_fatal. ++ * ERRP_GUARD() lifts these restrictions. ++ * ++ * To use ERRP_GUARD(), add it right at the beginning of the function. ++ * @errp can then be used without worrying about the argument being ++ * NULL or &error_fatal. ++ * ++ * Using it when it's not needed is safe, but please avoid cluttering ++ * the source with useless code. ++ * ++ * = Converting to ERRP_GUARD() = ++ * ++ * To convert a function to use ERRP_GUARD(): ++ * ++ * 0. If the Error ** parameter is not named @errp, rename it to ++ * @errp. ++ * ++ * 1. Add an ERRP_GUARD() invocation, by convention right at the ++ * beginning of the function. This makes @errp safe to use. ++ * ++ * 2. Replace &err by errp, and err by *errp. Delete local variable ++ * @err. ++ * ++ * 3. Delete error_propagate(errp, *errp), replace ++ * error_propagate_prepend(errp, *errp, ...) by error_prepend(errp, ...) ++ * ++ * 4. Ensure @errp is valid at return: when you destroy *errp, set ++ * *errp = NULL. ++ * ++ * Example: ++ * ++ * bool fn(..., Error **errp) ++ * { ++ * Error *err = NULL; ++ * ++ * foo(arg, &err); ++ * if (err) { ++ * handle the error... ++ * error_propagate(errp, err); ++ * return false; ++ * } ++ * ... ++ * } ++ * ++ * becomes ++ * ++ * bool fn(..., Error **errp) ++ * { ++ * ERRP_GUARD(); ++ * ++ * foo(arg, errp); ++ * if (*errp) { ++ * handle the error... ++ * return false; ++ * } ++ * ... ++ * } ++ * ++ * For mass-conversion, use scripts/coccinelle/errp-guard.cocci. ++ */ ++ ++#ifndef ERROR_H ++#define ERROR_H ++ ++#include "qapi/qapi-types-error.h" ++ ++/* ++ * Overall category of an error. ++ * Based on the qapi type QapiErrorClass, but reproduced here for nicer ++ * enum names. ++ */ ++typedef enum ErrorClass { ++ ERROR_CLASS_GENERIC_ERROR = QAPI_ERROR_CLASS_GENERICERROR, ++ ERROR_CLASS_COMMAND_NOT_FOUND = QAPI_ERROR_CLASS_COMMANDNOTFOUND, ++ ERROR_CLASS_DEVICE_NOT_ACTIVE = QAPI_ERROR_CLASS_DEVICENOTACTIVE, ++ ERROR_CLASS_DEVICE_NOT_FOUND = QAPI_ERROR_CLASS_DEVICENOTFOUND, ++ ERROR_CLASS_KVM_MISSING_CAP = QAPI_ERROR_CLASS_KVMMISSINGCAP, ++} ErrorClass; ++ ++/* ++ * Get @err's human-readable error message. ++ */ ++const char *error_get_pretty(const Error *err); ++ ++/* ++ * Get @err's error class. ++ * Note: use of error classes other than ERROR_CLASS_GENERIC_ERROR is ++ * strongly discouraged. ++ */ ++ErrorClass error_get_class(const Error *err); ++ ++/* ++ * Create a new error object and assign it to *@errp. ++ * If @errp is NULL, the error is ignored. Don't bother creating one ++ * then. ++ * If @errp is &error_abort, print a suitable message and abort(). ++ * If @errp is &error_fatal, print a suitable message and exit(1). ++ * If @errp is anything else, *@errp must be NULL. ++ * The new error's class is ERROR_CLASS_GENERIC_ERROR, and its ++ * human-readable error message is made from printf-style @fmt, ... ++ * The resulting message should be a single phrase, with no newline or ++ * trailing punctuation. ++ * Please don't error_setg(&error_fatal, ...), use error_report() and ++ * exit(), because that's more obvious. ++ * Likewise, don't error_setg(&error_abort, ...), use assert(). ++ */ ++#define error_setg(errp, fmt, ...) \ ++ error_setg_internal((errp), __FILE__, __LINE__, __func__, \ ++ (fmt), ## __VA_ARGS__) ++void error_setg_internal(Error **errp, ++ const char *src, int line, const char *func, ++ const char *fmt, ...) ++ G_GNUC_PRINTF(5, 6); ++ ++/* ++ * Just like error_setg(), with @os_error info added to the message. ++ * If @os_error is non-zero, ": " + strerror(os_error) is appended to ++ * the human-readable error message. ++ * ++ * The value of errno (which usually can get clobbered by almost any ++ * function call) will be preserved. ++ */ ++#define error_setg_errno(errp, os_error, fmt, ...) \ ++ error_setg_errno_internal((errp), __FILE__, __LINE__, __func__, \ ++ (os_error), (fmt), ## __VA_ARGS__) ++void error_setg_errno_internal(Error **errp, ++ const char *fname, int line, const char *func, ++ int os_error, const char *fmt, ...) ++ G_GNUC_PRINTF(6, 7); ++ ++#ifdef _WIN32 ++/* ++ * Just like error_setg(), with @win32_error info added to the message. ++ * If @win32_error is non-zero, ": " + g_win32_error_message(win32_err) ++ * is appended to the human-readable error message. ++ */ ++#define error_setg_win32(errp, win32_err, fmt, ...) \ ++ error_setg_win32_internal((errp), __FILE__, __LINE__, __func__, \ ++ (win32_err), (fmt), ## __VA_ARGS__) ++void error_setg_win32_internal(Error **errp, ++ const char *src, int line, const char *func, ++ int win32_err, const char *fmt, ...) ++ G_GNUC_PRINTF(6, 7); ++#endif ++ ++/* ++ * Propagate error object (if any) from @local_err to @dst_errp. ++ * If @local_err is NULL, do nothing (because there's nothing to ++ * propagate). ++ * Else, if @dst_errp is NULL, errors are being ignored. Free the ++ * error object. ++ * Else, if @dst_errp is &error_abort, print a suitable message and ++ * abort(). ++ * Else, if @dst_errp is &error_fatal, print a suitable message and ++ * exit(1). ++ * Else, if @dst_errp already contains an error, ignore this one: free ++ * the error object. ++ * Else, move the error object from @local_err to *@dst_errp. ++ * On return, @local_err is invalid. ++ * Please use ERRP_GUARD() instead when possible. ++ * Please don't error_propagate(&error_fatal, ...), use ++ * error_report_err() and exit(), because that's more obvious. ++ */ ++void error_propagate(Error **dst_errp, Error *local_err); ++ ++ ++/* ++ * Propagate error object (if any) with some text prepended. ++ * Behaves like ++ * error_prepend(&local_err, fmt, ...); ++ * error_propagate(dst_errp, local_err); ++ * Please use ERRP_GUARD() and error_prepend() instead when possible. ++ */ ++void error_propagate_prepend(Error **dst_errp, Error *local_err, ++ const char *fmt, ...) ++ G_GNUC_PRINTF(3, 4); ++ ++/* ++ * Prepend some text to @errp's human-readable error message. ++ * The text is made by formatting @fmt, @ap like vprintf(). ++ */ ++void error_vprepend(Error *const *errp, const char *fmt, va_list ap) ++ G_GNUC_PRINTF(2, 0); ++ ++/* ++ * Prepend some text to @errp's human-readable error message. ++ * The text is made by formatting @fmt, ... like printf(). ++ */ ++void error_prepend(Error *const *errp, const char *fmt, ...) ++ G_GNUC_PRINTF(2, 3); ++ ++/* ++ * Append a printf-style human-readable explanation to an existing error. ++ * If the error is later reported to a human user with ++ * error_report_err() or warn_report_err(), the hints will be shown, ++ * too. If it's reported via QMP, the hints will be ignored. ++ * Intended use is adding helpful hints on the human user interface, ++ * e.g. a list of valid values. It's not for clarifying a confusing ++ * error message. ++ * @errp may be NULL, but not &error_fatal or &error_abort. ++ * Trivially the case if you call it only after error_setg() or ++ * error_propagate(). ++ * May be called multiple times. The resulting hint should end with a ++ * newline. ++ */ ++void error_append_hint(Error *const *errp, const char *fmt, ...) ++ G_GNUC_PRINTF(2, 3); ++ ++/* ++ * Convenience function to report open() failure. ++ */ ++#define error_setg_file_open(errp, os_errno, filename) \ ++ error_setg_file_open_internal((errp), __FILE__, __LINE__, __func__, \ ++ (os_errno), (filename)) ++void error_setg_file_open_internal(Error **errp, ++ const char *src, int line, const char *func, ++ int os_errno, const char *filename); ++ ++/* ++ * Return an exact copy of @err. ++ */ ++Error *error_copy(const Error *err); ++ ++/* ++ * Free @err. ++ * @err may be NULL. ++ */ ++void error_free(Error *err); ++ ++/* ++ * Convenience function to assert that *@errp is set, then silently free it. ++ */ ++void error_free_or_abort(Error **errp); ++ ++/* ++ * Convenience function to warn_report() and free @err. ++ * The report includes hints added with error_append_hint(). ++ */ ++void warn_report_err(Error *err); ++ ++/* ++ * Convenience function to error_report() and free @err. ++ * The report includes hints added with error_append_hint(). ++ */ ++void error_report_err(Error *err); ++ ++/* ++ * Convenience function to error_prepend(), warn_report() and free @err. ++ */ ++void warn_reportf_err(Error *err, const char *fmt, ...) ++ G_GNUC_PRINTF(2, 3); ++ ++/* ++ * Convenience function to error_prepend(), error_report() and free @err. ++ */ ++void error_reportf_err(Error *err, const char *fmt, ...) ++ G_GNUC_PRINTF(2, 3); ++ ++/* ++ * Just like error_setg(), except you get to specify the error class. ++ * Note: use of error classes other than ERROR_CLASS_GENERIC_ERROR is ++ * strongly discouraged. ++ */ ++#define error_set(errp, err_class, fmt, ...) \ ++ error_set_internal((errp), __FILE__, __LINE__, __func__, \ ++ (err_class), (fmt), ## __VA_ARGS__) ++void error_set_internal(Error **errp, ++ const char *src, int line, const char *func, ++ ErrorClass err_class, const char *fmt, ...) ++ G_GNUC_PRINTF(6, 7); ++ ++/* ++ * Make @errp parameter easier to use regardless of argument value ++ * ++ * This macro is for use right at the beginning of a function that ++ * takes an Error **errp parameter to pass errors to its caller. The ++ * parameter must be named @errp. ++ * ++ * It must be used when the function dereferences @errp or passes ++ * @errp to error_prepend(), error_vprepend(), or error_append_hint(). ++ * It is safe to use even when it's not needed, but please avoid ++ * cluttering the source with useless code. ++ * ++ * If @errp is NULL or &error_fatal, rewrite it to point to a local ++ * Error variable, which will be automatically propagated to the ++ * original @errp on function exit. ++ * ++ * Note: &error_abort is not rewritten, because that would move the ++ * abort from the place where the error is created to the place where ++ * it's propagated. ++ */ ++#define ERRP_GUARD() \ ++ g_auto(ErrorPropagator) _auto_errp_prop = {.errp = errp}; \ ++ do { \ ++ if (!errp || errp == &error_fatal) { \ ++ errp = &_auto_errp_prop.local_err; \ ++ } \ ++ } while (0) ++ ++typedef struct ErrorPropagator { ++ Error *local_err; ++ Error **errp; ++} ErrorPropagator; ++ ++static inline void error_propagator_cleanup(ErrorPropagator *prop) ++{ ++ error_propagate(prop->errp, prop->local_err); ++} ++ ++G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(ErrorPropagator, error_propagator_cleanup); ++ ++/* ++ * Special error destination to warn on error. ++ * See error_setg() and error_propagate() for details. ++ */ ++extern Error *error_warn; ++ ++/* ++ * Special error destination to abort on error. ++ * See error_setg() and error_propagate() for details. ++ */ ++extern Error *error_abort; ++ ++/* ++ * Special error destination to exit(1) on error. ++ * See error_setg() and error_propagate() for details. ++ */ ++extern Error *error_fatal; ++ ++#endif +diff --git a/include/qapi/qapi-builtin-types.h b/include/qapi/qapi-builtin-types.h +new file mode 100644 +index 00000000..b217d4a3 +--- /dev/null ++++ b/include/qapi/qapi-builtin-types.h +@@ -0,0 +1,184 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Built-in QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_BUILTIN_TYPES_H ++#define QAPI_BUILTIN_TYPES_H ++ ++#include "qapi/util.h" ++ ++typedef struct strList strList; ++ ++typedef struct numberList numberList; ++ ++typedef struct intList intList; ++ ++typedef struct int8List int8List; ++ ++typedef struct int16List int16List; ++ ++typedef struct int32List int32List; ++ ++typedef struct int64List int64List; ++ ++typedef struct uint8List uint8List; ++ ++typedef struct uint16List uint16List; ++ ++typedef struct uint32List uint32List; ++ ++typedef struct uint64List uint64List; ++ ++typedef struct sizeList sizeList; ++ ++typedef struct boolList boolList; ++ ++typedef struct anyList anyList; ++ ++typedef struct nullList nullList; ++ ++typedef enum QType { ++ QTYPE_NONE, ++ QTYPE_QNULL, ++ QTYPE_QNUM, ++ QTYPE_QSTRING, ++ QTYPE_QDICT, ++ QTYPE_QLIST, ++ QTYPE_QBOOL, ++ QTYPE__MAX, ++} QType; ++ ++#define QType_str(val) \ ++ qapi_enum_lookup(&QType_lookup, (val)) ++ ++extern const QEnumLookup QType_lookup; ++ ++struct strList { ++ strList *next; ++ char *value; ++}; ++ ++void qapi_free_strList(strList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(strList, qapi_free_strList) ++ ++struct numberList { ++ numberList *next; ++ double value; ++}; ++ ++void qapi_free_numberList(numberList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(numberList, qapi_free_numberList) ++ ++struct intList { ++ intList *next; ++ int64_t value; ++}; ++ ++void qapi_free_intList(intList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(intList, qapi_free_intList) ++ ++struct int8List { ++ int8List *next; ++ int8_t value; ++}; ++ ++void qapi_free_int8List(int8List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(int8List, qapi_free_int8List) ++ ++struct int16List { ++ int16List *next; ++ int16_t value; ++}; ++ ++void qapi_free_int16List(int16List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(int16List, qapi_free_int16List) ++ ++struct int32List { ++ int32List *next; ++ int32_t value; ++}; ++ ++void qapi_free_int32List(int32List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(int32List, qapi_free_int32List) ++ ++struct int64List { ++ int64List *next; ++ int64_t value; ++}; ++ ++void qapi_free_int64List(int64List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(int64List, qapi_free_int64List) ++ ++struct uint8List { ++ uint8List *next; ++ uint8_t value; ++}; ++ ++void qapi_free_uint8List(uint8List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(uint8List, qapi_free_uint8List) ++ ++struct uint16List { ++ uint16List *next; ++ uint16_t value; ++}; ++ ++void qapi_free_uint16List(uint16List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(uint16List, qapi_free_uint16List) ++ ++struct uint32List { ++ uint32List *next; ++ uint32_t value; ++}; ++ ++void qapi_free_uint32List(uint32List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(uint32List, qapi_free_uint32List) ++ ++struct uint64List { ++ uint64List *next; ++ uint64_t value; ++}; ++ ++void qapi_free_uint64List(uint64List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(uint64List, qapi_free_uint64List) ++ ++struct sizeList { ++ sizeList *next; ++ uint64_t value; ++}; ++ ++void qapi_free_sizeList(sizeList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(sizeList, qapi_free_sizeList) ++ ++struct boolList { ++ boolList *next; ++ bool value; ++}; ++ ++void qapi_free_boolList(boolList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(boolList, qapi_free_boolList) ++ ++struct anyList { ++ anyList *next; ++ QObject *value; ++}; ++ ++void qapi_free_anyList(anyList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(anyList, qapi_free_anyList) ++ ++struct nullList { ++ nullList *next; ++ QNull *value; ++}; ++ ++void qapi_free_nullList(nullList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(nullList, qapi_free_nullList) ++ ++#endif /* QAPI_BUILTIN_TYPES_H */ +diff --git a/include/qapi/qapi-builtin-visit.h b/include/qapi/qapi-builtin-visit.h +new file mode 100644 +index 00000000..14b44234 +--- /dev/null ++++ b/include/qapi/qapi-builtin-visit.h +@@ -0,0 +1,68 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Built-in QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_BUILTIN_VISIT_H ++#define QAPI_BUILTIN_VISIT_H ++ ++#include "qapi/visitor.h" ++#include "qapi/qapi-builtin-types.h" ++ ++ ++bool visit_type_strList(Visitor *v, const char *name, ++ strList **obj, Error **errp); ++ ++bool visit_type_numberList(Visitor *v, const char *name, ++ numberList **obj, Error **errp); ++ ++bool visit_type_intList(Visitor *v, const char *name, ++ intList **obj, Error **errp); ++ ++bool visit_type_int8List(Visitor *v, const char *name, ++ int8List **obj, Error **errp); ++ ++bool visit_type_int16List(Visitor *v, const char *name, ++ int16List **obj, Error **errp); ++ ++bool visit_type_int32List(Visitor *v, const char *name, ++ int32List **obj, Error **errp); ++ ++bool visit_type_int64List(Visitor *v, const char *name, ++ int64List **obj, Error **errp); ++ ++bool visit_type_uint8List(Visitor *v, const char *name, ++ uint8List **obj, Error **errp); ++ ++bool visit_type_uint16List(Visitor *v, const char *name, ++ uint16List **obj, Error **errp); ++ ++bool visit_type_uint32List(Visitor *v, const char *name, ++ uint32List **obj, Error **errp); ++ ++bool visit_type_uint64List(Visitor *v, const char *name, ++ uint64List **obj, Error **errp); ++ ++bool visit_type_sizeList(Visitor *v, const char *name, ++ sizeList **obj, Error **errp); ++ ++bool visit_type_boolList(Visitor *v, const char *name, ++ boolList **obj, Error **errp); ++ ++bool visit_type_anyList(Visitor *v, const char *name, ++ anyList **obj, Error **errp); ++ ++bool visit_type_nullList(Visitor *v, const char *name, ++ nullList **obj, Error **errp); ++ ++bool visit_type_QType(Visitor *v, const char *name, ++ QType *obj, Error **errp); ++ ++#endif /* QAPI_BUILTIN_VISIT_H */ +diff --git a/include/qapi/qapi-commands-block-core.h b/include/qapi/qapi-commands-block-core.h +new file mode 100644 +index 00000000..e698e23c +--- /dev/null ++++ b/include/qapi/qapi-commands-block-core.h +@@ -0,0 +1,103 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP commands ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_COMMANDS_BLOCK_CORE_H ++#define QAPI_COMMANDS_BLOCK_CORE_H ++ ++#include "qapi-commands-common.h" ++#include "qapi-commands-crypto.h" ++#include "qapi-commands-job.h" ++#include "qapi-commands-sockets.h" ++#include "qapi-types-block-core.h" ++ ++BlockInfoList *qmp_query_block(Error **errp); ++void qmp_marshal_query_block(QDict *args, QObject **ret, Error **errp); ++BlockStatsList *qmp_query_blockstats(bool has_query_nodes, bool query_nodes, Error **errp); ++void qmp_marshal_query_blockstats(QDict *args, QObject **ret, Error **errp); ++BlockJobInfoList *qmp_query_block_jobs(Error **errp); ++void qmp_marshal_query_block_jobs(QDict *args, QObject **ret, Error **errp); ++void coroutine_fn qmp_block_resize(const char *device, const char *node_name, int64_t size, Error **errp); ++void coroutine_fn qmp_marshal_block_resize(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_snapshot_sync(const char *device, const char *node_name, const char *snapshot_file, const char *snapshot_node_name, const char *format, bool has_mode, NewImageMode mode, Error **errp); ++void qmp_marshal_blockdev_snapshot_sync(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_snapshot(const char *node, const char *overlay, Error **errp); ++void qmp_marshal_blockdev_snapshot(QDict *args, QObject **ret, Error **errp); ++void qmp_change_backing_file(const char *device, const char *image_node_name, const char *backing_file, Error **errp); ++void qmp_marshal_change_backing_file(QDict *args, QObject **ret, Error **errp); ++void qmp_block_commit(const char *job_id, const char *device, const char *base_node, const char *base, const char *top_node, const char *top, const char *backing_file, bool has_backing_mask_protocol, bool backing_mask_protocol, bool has_speed, int64_t speed, bool has_on_error, BlockdevOnError on_error, const char *filter_node_name, bool has_auto_finalize, bool auto_finalize, bool has_auto_dismiss, bool auto_dismiss, Error **errp); ++void qmp_marshal_block_commit(QDict *args, QObject **ret, Error **errp); ++void qmp_drive_backup(DriveBackup *arg, Error **errp); ++void qmp_marshal_drive_backup(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp); ++void qmp_marshal_blockdev_backup(QDict *args, QObject **ret, Error **errp); ++BlockDeviceInfoList *qmp_query_named_block_nodes(bool has_flat, bool flat, Error **errp); ++void qmp_marshal_query_named_block_nodes(QDict *args, QObject **ret, Error **errp); ++XDbgBlockGraph *qmp_x_debug_query_block_graph(Error **errp); ++void qmp_marshal_x_debug_query_block_graph(QDict *args, QObject **ret, Error **errp); ++void qmp_drive_mirror(DriveMirror *arg, Error **errp); ++void qmp_marshal_drive_mirror(QDict *args, QObject **ret, Error **errp); ++void qmp_block_dirty_bitmap_add(const char *node, const char *name, bool has_granularity, uint32_t granularity, bool has_persistent, bool persistent, bool has_disabled, bool disabled, Error **errp); ++void qmp_marshal_block_dirty_bitmap_add(QDict *args, QObject **ret, Error **errp); ++void qmp_block_dirty_bitmap_remove(const char *node, const char *name, Error **errp); ++void qmp_marshal_block_dirty_bitmap_remove(QDict *args, QObject **ret, Error **errp); ++void qmp_block_dirty_bitmap_clear(const char *node, const char *name, Error **errp); ++void qmp_marshal_block_dirty_bitmap_clear(QDict *args, QObject **ret, Error **errp); ++void qmp_block_dirty_bitmap_enable(const char *node, const char *name, Error **errp); ++void qmp_marshal_block_dirty_bitmap_enable(QDict *args, QObject **ret, Error **errp); ++void qmp_block_dirty_bitmap_disable(const char *node, const char *name, Error **errp); ++void qmp_marshal_block_dirty_bitmap_disable(QDict *args, QObject **ret, Error **errp); ++void qmp_block_dirty_bitmap_merge(const char *node, const char *target, BlockDirtyBitmapOrStrList *bitmaps, Error **errp); ++void qmp_marshal_block_dirty_bitmap_merge(QDict *args, QObject **ret, Error **errp); ++BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, const char *name, Error **errp); ++void qmp_marshal_x_debug_block_dirty_bitmap_sha256(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_mirror(const char *job_id, const char *device, const char *target, const char *replaces, MirrorSyncMode sync, bool has_speed, int64_t speed, bool has_granularity, uint32_t granularity, bool has_buf_size, int64_t buf_size, bool has_on_source_error, BlockdevOnError on_source_error, bool has_on_target_error, BlockdevOnError on_target_error, const char *filter_node_name, bool has_copy_mode, MirrorCopyMode copy_mode, bool has_auto_finalize, bool auto_finalize, bool has_auto_dismiss, bool auto_dismiss, Error **errp); ++void qmp_marshal_blockdev_mirror(QDict *args, QObject **ret, Error **errp); ++void qmp_block_stream(const char *job_id, const char *device, const char *base, const char *base_node, const char *backing_file, bool has_backing_mask_protocol, bool backing_mask_protocol, const char *bottom, bool has_speed, int64_t speed, bool has_on_error, BlockdevOnError on_error, const char *filter_node_name, bool has_auto_finalize, bool auto_finalize, bool has_auto_dismiss, bool auto_dismiss, Error **errp); ++void qmp_marshal_block_stream(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp); ++void qmp_marshal_block_job_set_speed(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_cancel(const char *device, bool has_force, bool force, Error **errp); ++void qmp_marshal_block_job_cancel(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_pause(const char *device, Error **errp); ++void qmp_marshal_block_job_pause(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_resume(const char *device, Error **errp); ++void qmp_marshal_block_job_resume(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_complete(const char *device, Error **errp); ++void qmp_marshal_block_job_complete(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_dismiss(const char *id, Error **errp); ++void qmp_marshal_block_job_dismiss(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_finalize(const char *id, Error **errp); ++void qmp_marshal_block_job_finalize(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_change(BlockJobChangeOptions *arg, Error **errp); ++void qmp_marshal_block_job_change(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_add(BlockdevOptions *arg, Error **errp); ++void qmp_marshal_blockdev_add(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_reopen(BlockdevOptionsList *options, Error **errp); ++void qmp_marshal_blockdev_reopen(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_del(const char *node_name, Error **errp); ++void qmp_marshal_blockdev_del(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, Error **errp); ++void qmp_marshal_blockdev_create(QDict *args, QObject **ret, Error **errp); ++void qmp_x_blockdev_amend(const char *job_id, const char *node_name, BlockdevAmendOptions *options, bool has_force, bool force, Error **errp); ++void qmp_marshal_x_blockdev_amend(QDict *args, QObject **ret, Error **errp); ++void qmp_block_set_write_threshold(const char *node_name, uint64_t write_threshold, Error **errp); ++void qmp_marshal_block_set_write_threshold(QDict *args, QObject **ret, Error **errp); ++void qmp_x_blockdev_change(const char *parent, const char *child, const char *node, Error **errp); ++void qmp_marshal_x_blockdev_change(QDict *args, QObject **ret, Error **errp); ++void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, bool has_force, bool force, Error **errp); ++void qmp_marshal_x_blockdev_set_iothread(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_snapshot_internal_sync(const char *device, const char *name, Error **errp); ++void qmp_marshal_blockdev_snapshot_internal_sync(QDict *args, QObject **ret, Error **errp); ++SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, const char *id, const char *name, Error **errp); ++void qmp_marshal_blockdev_snapshot_delete_internal_sync(QDict *args, QObject **ret, Error **errp); ++ ++#endif /* QAPI_COMMANDS_BLOCK_CORE_H */ +diff --git a/include/qapi/qapi-commands-block.h b/include/qapi/qapi-commands-block.h +new file mode 100644 +index 00000000..2c1adc60 +--- /dev/null ++++ b/include/qapi/qapi-commands-block.h +@@ -0,0 +1,38 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP commands ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_COMMANDS_BLOCK_H ++#define QAPI_COMMANDS_BLOCK_H ++ ++#include "qapi-commands-block-core.h" ++#include "qapi-types-block.h" ++ ++PRManagerInfoList *qmp_query_pr_managers(Error **errp); ++void qmp_marshal_query_pr_managers(QDict *args, QObject **ret, Error **errp); ++void qmp_eject(const char *device, const char *id, bool has_force, bool force, Error **errp); ++void qmp_marshal_eject(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_open_tray(const char *device, const char *id, bool has_force, bool force, Error **errp); ++void qmp_marshal_blockdev_open_tray(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_close_tray(const char *device, const char *id, Error **errp); ++void qmp_marshal_blockdev_close_tray(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_remove_medium(const char *id, Error **errp); ++void qmp_marshal_blockdev_remove_medium(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_insert_medium(const char *id, const char *node_name, Error **errp); ++void qmp_marshal_blockdev_insert_medium(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_change_medium(const char *device, const char *id, const char *filename, const char *format, bool has_force, bool force, bool has_read_only_mode, BlockdevChangeReadOnlyMode read_only_mode, Error **errp); ++void qmp_marshal_blockdev_change_medium(QDict *args, QObject **ret, Error **errp); ++void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp); ++void qmp_marshal_block_set_io_throttle(QDict *args, QObject **ret, Error **errp); ++void qmp_block_latency_histogram_set(const char *id, bool has_boundaries, uint64List *boundaries, bool has_boundaries_read, uint64List *boundaries_read, bool has_boundaries_write, uint64List *boundaries_write, bool has_boundaries_zap, uint64List *boundaries_zap, bool has_boundaries_flush, uint64List *boundaries_flush, Error **errp); ++void qmp_marshal_block_latency_histogram_set(QDict *args, QObject **ret, Error **errp); ++ ++#endif /* QAPI_COMMANDS_BLOCK_H */ +diff --git a/include/qapi/qapi-commands-common.h b/include/qapi/qapi-commands-common.h +new file mode 100644 +index 00000000..23b5cb75 +--- /dev/null ++++ b/include/qapi/qapi-commands-common.h +@@ -0,0 +1,19 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP commands ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_COMMANDS_COMMON_H ++#define QAPI_COMMANDS_COMMON_H ++ ++#include "qapi-types-common.h" ++ ++ ++#endif /* QAPI_COMMANDS_COMMON_H */ +diff --git a/include/qapi/qapi-commands-job.h b/include/qapi/qapi-commands-job.h +new file mode 100644 +index 00000000..9783650f +--- /dev/null ++++ b/include/qapi/qapi-commands-job.h +@@ -0,0 +1,33 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP commands ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_COMMANDS_JOB_H ++#define QAPI_COMMANDS_JOB_H ++ ++#include "qapi-types-job.h" ++ ++void qmp_job_pause(const char *id, Error **errp); ++void qmp_marshal_job_pause(QDict *args, QObject **ret, Error **errp); ++void qmp_job_resume(const char *id, Error **errp); ++void qmp_marshal_job_resume(QDict *args, QObject **ret, Error **errp); ++void qmp_job_cancel(const char *id, Error **errp); ++void qmp_marshal_job_cancel(QDict *args, QObject **ret, Error **errp); ++void qmp_job_complete(const char *id, Error **errp); ++void qmp_marshal_job_complete(QDict *args, QObject **ret, Error **errp); ++void qmp_job_dismiss(const char *id, Error **errp); ++void qmp_marshal_job_dismiss(QDict *args, QObject **ret, Error **errp); ++void qmp_job_finalize(const char *id, Error **errp); ++void qmp_marshal_job_finalize(QDict *args, QObject **ret, Error **errp); ++JobInfoList *qmp_query_jobs(Error **errp); ++void qmp_marshal_query_jobs(QDict *args, QObject **ret, Error **errp); ++ ++#endif /* QAPI_COMMANDS_JOB_H */ +diff --git a/include/qapi/qapi-events-block-core.h b/include/qapi/qapi-events-block-core.h +new file mode 100644 +index 00000000..7f12beff +--- /dev/null ++++ b/include/qapi/qapi-events-block-core.h +@@ -0,0 +1,43 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP events ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * Copyright (c) 2015-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_EVENTS_BLOCK_CORE_H ++#define QAPI_EVENTS_BLOCK_CORE_H ++ ++#include "qapi-events-common.h" ++#include "qapi-events-crypto.h" ++#include "qapi-events-job.h" ++#include "qapi-events-sockets.h" ++#include "qapi/util.h" ++#include "qapi-types-block-core.h" ++ ++void qapi_event_send_block_image_corrupted(const char *device, const char *node_name, const char *msg, bool has_offset, int64_t offset, bool has_size, int64_t size, bool fatal); ++ ++void qapi_event_send_block_io_error(const char *device, const char *node_name, IoOperationType operation, BlockErrorAction action, bool has_nospace, bool nospace, const char *reason); ++ ++void qapi_event_send_block_job_completed(JobType type, const char *device, int64_t len, int64_t offset, int64_t speed, const char *error); ++ ++void qapi_event_send_block_job_cancelled(JobType type, const char *device, int64_t len, int64_t offset, int64_t speed); ++ ++void qapi_event_send_block_job_error(const char *device, IoOperationType operation, BlockErrorAction action); ++ ++void qapi_event_send_block_job_ready(JobType type, const char *device, int64_t len, int64_t offset, int64_t speed); ++ ++void qapi_event_send_block_job_pending(JobType type, const char *id); ++ ++void qapi_event_send_block_write_threshold(const char *node_name, uint64_t amount_exceeded, uint64_t write_threshold); ++ ++void qapi_event_send_quorum_failure(const char *reference, int64_t sector_num, int64_t sectors_count); ++ ++void qapi_event_send_quorum_report_bad(QuorumOpType type, const char *error, const char *node_name, int64_t sector_num, int64_t sectors_count); ++ ++#endif /* QAPI_EVENTS_BLOCK_CORE_H */ +diff --git a/include/qapi/qapi-events-common.h b/include/qapi/qapi-events-common.h +new file mode 100644 +index 00000000..4c3e4790 +--- /dev/null ++++ b/include/qapi/qapi-events-common.h +@@ -0,0 +1,19 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP events ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * Copyright (c) 2015-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_EVENTS_COMMON_H ++#define QAPI_EVENTS_COMMON_H ++ ++#include "qapi/util.h" ++#include "qapi-types-common.h" ++ ++#endif /* QAPI_EVENTS_COMMON_H */ +diff --git a/include/qapi/qapi-events-job.h b/include/qapi/qapi-events-job.h +new file mode 100644 +index 00000000..09a6dc06 +--- /dev/null ++++ b/include/qapi/qapi-events-job.h +@@ -0,0 +1,21 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP events ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * Copyright (c) 2015-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_EVENTS_JOB_H ++#define QAPI_EVENTS_JOB_H ++ ++#include "qapi/util.h" ++#include "qapi-types-job.h" ++ ++void qapi_event_send_job_status_change(const char *id, JobStatus status); ++ ++#endif /* QAPI_EVENTS_JOB_H */ +diff --git a/include/qapi/qapi-types-block-core.h b/include/qapi/qapi-types-block-core.h +new file mode 100644 +index 00000000..26585893 +--- /dev/null ++++ b/include/qapi/qapi-types-block-core.h +@@ -0,0 +1,3626 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_BLOCK_CORE_H ++#define QAPI_TYPES_BLOCK_CORE_H ++ ++#include "qapi/qapi-builtin-types.h" ++#include "qapi-types-common.h" ++#include "qapi-types-crypto.h" ++#include "qapi-types-job.h" ++#include "qapi-types-sockets.h" ++ ++typedef struct SnapshotInfo SnapshotInfo; ++ ++typedef struct ImageInfoSpecificQCow2EncryptionBase ImageInfoSpecificQCow2EncryptionBase; ++ ++typedef struct ImageInfoSpecificQCow2Encryption ImageInfoSpecificQCow2Encryption; ++ ++typedef struct Qcow2BitmapInfoList Qcow2BitmapInfoList; ++ ++typedef struct ImageInfoSpecificQCow2 ImageInfoSpecificQCow2; ++ ++typedef struct VmdkExtentInfoList VmdkExtentInfoList; ++ ++typedef struct ImageInfoSpecificVmdk ImageInfoSpecificVmdk; ++ ++typedef struct VmdkExtentInfo VmdkExtentInfo; ++ ++typedef struct ImageInfoSpecificRbd ImageInfoSpecificRbd; ++ ++typedef struct ImageInfoSpecificFile ImageInfoSpecificFile; ++ ++typedef enum ImageInfoSpecificKind { ++ IMAGE_INFO_SPECIFIC_KIND_QCOW2, ++ IMAGE_INFO_SPECIFIC_KIND_VMDK, ++ IMAGE_INFO_SPECIFIC_KIND_LUKS, ++ IMAGE_INFO_SPECIFIC_KIND_RBD, ++ IMAGE_INFO_SPECIFIC_KIND_FILE, ++ IMAGE_INFO_SPECIFIC_KIND__MAX, ++} ImageInfoSpecificKind; ++ ++#define ImageInfoSpecificKind_str(val) \ ++ qapi_enum_lookup(&ImageInfoSpecificKind_lookup, (val)) ++ ++extern const QEnumLookup ImageInfoSpecificKind_lookup; ++ ++typedef struct ImageInfoSpecificQCow2Wrapper ImageInfoSpecificQCow2Wrapper; ++ ++typedef struct ImageInfoSpecificVmdkWrapper ImageInfoSpecificVmdkWrapper; ++ ++typedef struct ImageInfoSpecificLUKSWrapper ImageInfoSpecificLUKSWrapper; ++ ++typedef struct ImageInfoSpecificRbdWrapper ImageInfoSpecificRbdWrapper; ++ ++typedef struct ImageInfoSpecificFileWrapper ImageInfoSpecificFileWrapper; ++ ++typedef struct q_obj_ImageInfoSpecific_base q_obj_ImageInfoSpecific_base; ++ ++typedef struct ImageInfoSpecific ImageInfoSpecific; ++ ++typedef struct SnapshotInfoList SnapshotInfoList; ++ ++typedef struct BlockNodeInfo BlockNodeInfo; ++ ++typedef struct ImageInfo ImageInfo; ++ ++typedef struct BlockChildInfo BlockChildInfo; ++ ++typedef struct BlockChildInfoList BlockChildInfoList; ++ ++typedef struct BlockGraphInfo BlockGraphInfo; ++ ++typedef struct ImageCheck ImageCheck; ++ ++typedef struct MapEntry MapEntry; ++ ++typedef struct BlockdevCacheInfo BlockdevCacheInfo; ++ ++typedef struct BlockDirtyInfoList BlockDirtyInfoList; ++ ++typedef struct BlockDeviceInfo BlockDeviceInfo; ++ ++typedef enum BlockDeviceIoStatus { ++ BLOCK_DEVICE_IO_STATUS_OK, ++ BLOCK_DEVICE_IO_STATUS_FAILED, ++ BLOCK_DEVICE_IO_STATUS_NOSPACE, ++ BLOCK_DEVICE_IO_STATUS__MAX, ++} BlockDeviceIoStatus; ++ ++#define BlockDeviceIoStatus_str(val) \ ++ qapi_enum_lookup(&BlockDeviceIoStatus_lookup, (val)) ++ ++extern const QEnumLookup BlockDeviceIoStatus_lookup; ++ ++typedef struct BlockDirtyInfo BlockDirtyInfo; ++ ++typedef enum Qcow2BitmapInfoFlags { ++ QCOW2_BITMAP_INFO_FLAGS_IN_USE, ++ QCOW2_BITMAP_INFO_FLAGS_AUTO, ++ QCOW2_BITMAP_INFO_FLAGS__MAX, ++} Qcow2BitmapInfoFlags; ++ ++#define Qcow2BitmapInfoFlags_str(val) \ ++ qapi_enum_lookup(&Qcow2BitmapInfoFlags_lookup, (val)) ++ ++extern const QEnumLookup Qcow2BitmapInfoFlags_lookup; ++ ++typedef struct Qcow2BitmapInfoFlagsList Qcow2BitmapInfoFlagsList; ++ ++typedef struct Qcow2BitmapInfo Qcow2BitmapInfo; ++ ++typedef struct BlockLatencyHistogramInfo BlockLatencyHistogramInfo; ++ ++typedef struct BlockInfo BlockInfo; ++ ++typedef struct BlockMeasureInfo BlockMeasureInfo; ++ ++typedef struct BlockInfoList BlockInfoList; ++ ++typedef struct BlockDeviceTimedStats BlockDeviceTimedStats; ++ ++typedef struct BlockDeviceTimedStatsList BlockDeviceTimedStatsList; ++ ++typedef struct BlockDeviceStats BlockDeviceStats; ++ ++typedef struct BlockStatsSpecificFile BlockStatsSpecificFile; ++ ++typedef struct BlockStatsSpecificNvme BlockStatsSpecificNvme; ++ ++typedef struct q_obj_BlockStatsSpecific_base q_obj_BlockStatsSpecific_base; ++ ++typedef struct BlockStatsSpecific BlockStatsSpecific; ++ ++typedef struct BlockStats BlockStats; ++ ++typedef struct q_obj_query_blockstats_arg q_obj_query_blockstats_arg; ++ ++typedef struct BlockStatsList BlockStatsList; ++ ++typedef enum BlockdevOnError { ++ BLOCKDEV_ON_ERROR_REPORT, ++ BLOCKDEV_ON_ERROR_IGNORE, ++ BLOCKDEV_ON_ERROR_ENOSPC, ++ BLOCKDEV_ON_ERROR_STOP, ++ BLOCKDEV_ON_ERROR_AUTO, ++ BLOCKDEV_ON_ERROR__MAX, ++} BlockdevOnError; ++ ++#define BlockdevOnError_str(val) \ ++ qapi_enum_lookup(&BlockdevOnError_lookup, (val)) ++ ++extern const QEnumLookup BlockdevOnError_lookup; ++ ++typedef enum MirrorSyncMode { ++ MIRROR_SYNC_MODE_TOP, ++ MIRROR_SYNC_MODE_FULL, ++ MIRROR_SYNC_MODE_NONE, ++ MIRROR_SYNC_MODE_INCREMENTAL, ++ MIRROR_SYNC_MODE_BITMAP, ++ MIRROR_SYNC_MODE__MAX, ++} MirrorSyncMode; ++ ++#define MirrorSyncMode_str(val) \ ++ qapi_enum_lookup(&MirrorSyncMode_lookup, (val)) ++ ++extern const QEnumLookup MirrorSyncMode_lookup; ++ ++typedef enum BitmapSyncMode { ++ BITMAP_SYNC_MODE_ON_SUCCESS, ++ BITMAP_SYNC_MODE_NEVER, ++ BITMAP_SYNC_MODE_ALWAYS, ++ BITMAP_SYNC_MODE__MAX, ++} BitmapSyncMode; ++ ++#define BitmapSyncMode_str(val) \ ++ qapi_enum_lookup(&BitmapSyncMode_lookup, (val)) ++ ++extern const QEnumLookup BitmapSyncMode_lookup; ++ ++typedef enum MirrorCopyMode { ++ MIRROR_COPY_MODE_BACKGROUND, ++ MIRROR_COPY_MODE_WRITE_BLOCKING, ++ MIRROR_COPY_MODE__MAX, ++} MirrorCopyMode; ++ ++#define MirrorCopyMode_str(val) \ ++ qapi_enum_lookup(&MirrorCopyMode_lookup, (val)) ++ ++extern const QEnumLookup MirrorCopyMode_lookup; ++ ++typedef struct BlockJobInfoMirror BlockJobInfoMirror; ++ ++typedef struct q_obj_BlockJobInfo_base q_obj_BlockJobInfo_base; ++ ++typedef struct BlockJobInfo BlockJobInfo; ++ ++typedef struct BlockJobInfoList BlockJobInfoList; ++ ++typedef struct q_obj_block_resize_arg q_obj_block_resize_arg; ++ ++typedef enum NewImageMode { ++ NEW_IMAGE_MODE_EXISTING, ++ NEW_IMAGE_MODE_ABSOLUTE_PATHS, ++ NEW_IMAGE_MODE__MAX, ++} NewImageMode; ++ ++#define NewImageMode_str(val) \ ++ qapi_enum_lookup(&NewImageMode_lookup, (val)) ++ ++extern const QEnumLookup NewImageMode_lookup; ++ ++typedef struct BlockdevSnapshotSync BlockdevSnapshotSync; ++ ++typedef struct BlockdevSnapshot BlockdevSnapshot; ++ ++typedef struct BackupPerf BackupPerf; ++ ++typedef struct BackupCommon BackupCommon; ++ ++typedef struct DriveBackup DriveBackup; ++ ++typedef struct BlockdevBackup BlockdevBackup; ++ ++typedef struct q_obj_change_backing_file_arg q_obj_change_backing_file_arg; ++ ++typedef struct q_obj_block_commit_arg q_obj_block_commit_arg; ++ ++typedef struct q_obj_query_named_block_nodes_arg q_obj_query_named_block_nodes_arg; ++ ++typedef struct BlockDeviceInfoList BlockDeviceInfoList; ++ ++typedef enum XDbgBlockGraphNodeType { ++ X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_BACKEND, ++ X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB, ++ X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_DRIVER, ++ X_DBG_BLOCK_GRAPH_NODE_TYPE__MAX, ++} XDbgBlockGraphNodeType; ++ ++#define XDbgBlockGraphNodeType_str(val) \ ++ qapi_enum_lookup(&XDbgBlockGraphNodeType_lookup, (val)) ++ ++extern const QEnumLookup XDbgBlockGraphNodeType_lookup; ++ ++typedef struct XDbgBlockGraphNode XDbgBlockGraphNode; ++ ++typedef enum BlockPermission { ++ BLOCK_PERMISSION_CONSISTENT_READ, ++ BLOCK_PERMISSION_WRITE, ++ BLOCK_PERMISSION_WRITE_UNCHANGED, ++ BLOCK_PERMISSION_RESIZE, ++ BLOCK_PERMISSION__MAX, ++} BlockPermission; ++ ++#define BlockPermission_str(val) \ ++ qapi_enum_lookup(&BlockPermission_lookup, (val)) ++ ++extern const QEnumLookup BlockPermission_lookup; ++ ++typedef struct BlockPermissionList BlockPermissionList; ++ ++typedef struct XDbgBlockGraphEdge XDbgBlockGraphEdge; ++ ++typedef struct XDbgBlockGraphNodeList XDbgBlockGraphNodeList; ++ ++typedef struct XDbgBlockGraphEdgeList XDbgBlockGraphEdgeList; ++ ++typedef struct XDbgBlockGraph XDbgBlockGraph; ++ ++typedef struct DriveMirror DriveMirror; ++ ++typedef struct BlockDirtyBitmap BlockDirtyBitmap; ++ ++typedef struct BlockDirtyBitmapAdd BlockDirtyBitmapAdd; ++ ++typedef struct BlockDirtyBitmapOrStr BlockDirtyBitmapOrStr; ++ ++typedef struct BlockDirtyBitmapOrStrList BlockDirtyBitmapOrStrList; ++ ++typedef struct BlockDirtyBitmapMerge BlockDirtyBitmapMerge; ++ ++typedef struct BlockDirtyBitmapSha256 BlockDirtyBitmapSha256; ++ ++typedef struct q_obj_blockdev_mirror_arg q_obj_blockdev_mirror_arg; ++ ++typedef struct BlockIOThrottle BlockIOThrottle; ++ ++typedef struct ThrottleLimits ThrottleLimits; ++ ++typedef struct ThrottleGroupProperties ThrottleGroupProperties; ++ ++typedef struct q_obj_block_stream_arg q_obj_block_stream_arg; ++ ++typedef struct q_obj_block_job_set_speed_arg q_obj_block_job_set_speed_arg; ++ ++typedef struct q_obj_block_job_cancel_arg q_obj_block_job_cancel_arg; ++ ++typedef struct q_obj_block_job_pause_arg q_obj_block_job_pause_arg; ++ ++typedef struct q_obj_block_job_resume_arg q_obj_block_job_resume_arg; ++ ++typedef struct q_obj_block_job_complete_arg q_obj_block_job_complete_arg; ++ ++typedef struct q_obj_block_job_dismiss_arg q_obj_block_job_dismiss_arg; ++ ++typedef struct q_obj_block_job_finalize_arg q_obj_block_job_finalize_arg; ++ ++typedef struct BlockJobChangeOptionsMirror BlockJobChangeOptionsMirror; ++ ++typedef struct q_obj_BlockJobChangeOptions_base q_obj_BlockJobChangeOptions_base; ++ ++typedef struct BlockJobChangeOptions BlockJobChangeOptions; ++ ++typedef enum BlockdevDiscardOptions { ++ BLOCKDEV_DISCARD_OPTIONS_IGNORE, ++ BLOCKDEV_DISCARD_OPTIONS_UNMAP, ++ BLOCKDEV_DISCARD_OPTIONS__MAX, ++} BlockdevDiscardOptions; ++ ++#define BlockdevDiscardOptions_str(val) \ ++ qapi_enum_lookup(&BlockdevDiscardOptions_lookup, (val)) ++ ++extern const QEnumLookup BlockdevDiscardOptions_lookup; ++ ++typedef enum BlockdevDetectZeroesOptions { ++ BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, ++ BLOCKDEV_DETECT_ZEROES_OPTIONS_ON, ++ BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP, ++ BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX, ++} BlockdevDetectZeroesOptions; ++ ++#define BlockdevDetectZeroesOptions_str(val) \ ++ qapi_enum_lookup(&BlockdevDetectZeroesOptions_lookup, (val)) ++ ++extern const QEnumLookup BlockdevDetectZeroesOptions_lookup; ++ ++typedef enum BlockdevAioOptions { ++ BLOCKDEV_AIO_OPTIONS_THREADS, ++ BLOCKDEV_AIO_OPTIONS_NATIVE, ++#if defined(CONFIG_LINUX_IO_URING) ++ BLOCKDEV_AIO_OPTIONS_IO_URING, ++#endif /* defined(CONFIG_LINUX_IO_URING) */ ++ BLOCKDEV_AIO_OPTIONS__MAX, ++} BlockdevAioOptions; ++ ++#define BlockdevAioOptions_str(val) \ ++ qapi_enum_lookup(&BlockdevAioOptions_lookup, (val)) ++ ++extern const QEnumLookup BlockdevAioOptions_lookup; ++ ++typedef struct BlockdevCacheOptions BlockdevCacheOptions; ++ ++typedef enum BlockdevDriver { ++ BLOCKDEV_DRIVER_BLKDEBUG, ++ BLOCKDEV_DRIVER_BLKLOGWRITES, ++ BLOCKDEV_DRIVER_BLKREPLAY, ++ BLOCKDEV_DRIVER_BLKVERIFY, ++ BLOCKDEV_DRIVER_BOCHS, ++ BLOCKDEV_DRIVER_CLOOP, ++ BLOCKDEV_DRIVER_COMPRESS, ++ BLOCKDEV_DRIVER_COPY_BEFORE_WRITE, ++ BLOCKDEV_DRIVER_COPY_ON_READ, ++ BLOCKDEV_DRIVER_DMG, ++ BLOCKDEV_DRIVER_FILE, ++ BLOCKDEV_DRIVER_SNAPSHOT_ACCESS, ++ BLOCKDEV_DRIVER_FTP, ++ BLOCKDEV_DRIVER_FTPS, ++ BLOCKDEV_DRIVER_GLUSTER, ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ BLOCKDEV_DRIVER_HOST_CDROM, ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ BLOCKDEV_DRIVER_HOST_DEVICE, ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ BLOCKDEV_DRIVER_HTTP, ++ BLOCKDEV_DRIVER_HTTPS, ++#if defined(CONFIG_BLKIO) ++ BLOCKDEV_DRIVER_IO_URING, ++#endif /* defined(CONFIG_BLKIO) */ ++ BLOCKDEV_DRIVER_ISCSI, ++ BLOCKDEV_DRIVER_LUKS, ++ BLOCKDEV_DRIVER_NBD, ++ BLOCKDEV_DRIVER_NFS, ++ BLOCKDEV_DRIVER_NULL_AIO, ++ BLOCKDEV_DRIVER_NULL_CO, ++ BLOCKDEV_DRIVER_NVME, ++#if defined(CONFIG_BLKIO) ++ BLOCKDEV_DRIVER_NVME_IO_URING, ++#endif /* defined(CONFIG_BLKIO) */ ++ BLOCKDEV_DRIVER_PARALLELS, ++ BLOCKDEV_DRIVER_PREALLOCATE, ++ BLOCKDEV_DRIVER_QCOW, ++ BLOCKDEV_DRIVER_QCOW2, ++ BLOCKDEV_DRIVER_QED, ++ BLOCKDEV_DRIVER_QUORUM, ++ BLOCKDEV_DRIVER_RAW, ++ BLOCKDEV_DRIVER_RBD, ++#if defined(CONFIG_REPLICATION) ++ BLOCKDEV_DRIVER_REPLICATION, ++#endif /* defined(CONFIG_REPLICATION) */ ++ BLOCKDEV_DRIVER_SSH, ++ BLOCKDEV_DRIVER_THROTTLE, ++ BLOCKDEV_DRIVER_VDI, ++ BLOCKDEV_DRIVER_VHDX, ++#if defined(CONFIG_BLKIO) ++ BLOCKDEV_DRIVER_VIRTIO_BLK_VFIO_PCI, ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_USER, ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_VDPA, ++#endif /* defined(CONFIG_BLKIO) */ ++ BLOCKDEV_DRIVER_VMDK, ++ BLOCKDEV_DRIVER_VPC, ++ BLOCKDEV_DRIVER_VVFAT, ++ BLOCKDEV_DRIVER__MAX, ++} BlockdevDriver; ++ ++#define BlockdevDriver_str(val) \ ++ qapi_enum_lookup(&BlockdevDriver_lookup, (val)) ++ ++extern const QEnumLookup BlockdevDriver_lookup; ++ ++typedef struct BlockdevOptionsFile BlockdevOptionsFile; ++ ++typedef struct BlockdevOptionsNull BlockdevOptionsNull; ++ ++typedef struct BlockdevOptionsNVMe BlockdevOptionsNVMe; ++ ++typedef struct BlockdevOptionsVVFAT BlockdevOptionsVVFAT; ++ ++typedef struct BlockdevOptionsGenericFormat BlockdevOptionsGenericFormat; ++ ++typedef struct BlockdevOptionsLUKS BlockdevOptionsLUKS; ++ ++typedef struct BlockdevOptionsGenericCOWFormat BlockdevOptionsGenericCOWFormat; ++ ++typedef enum Qcow2OverlapCheckMode { ++ QCOW2_OVERLAP_CHECK_MODE_NONE, ++ QCOW2_OVERLAP_CHECK_MODE_CONSTANT, ++ QCOW2_OVERLAP_CHECK_MODE_CACHED, ++ QCOW2_OVERLAP_CHECK_MODE_ALL, ++ QCOW2_OVERLAP_CHECK_MODE__MAX, ++} Qcow2OverlapCheckMode; ++ ++#define Qcow2OverlapCheckMode_str(val) \ ++ qapi_enum_lookup(&Qcow2OverlapCheckMode_lookup, (val)) ++ ++extern const QEnumLookup Qcow2OverlapCheckMode_lookup; ++ ++typedef struct Qcow2OverlapCheckFlags Qcow2OverlapCheckFlags; ++ ++typedef struct Qcow2OverlapChecks Qcow2OverlapChecks; ++ ++typedef enum BlockdevQcowEncryptionFormat { ++ BLOCKDEV_QCOW_ENCRYPTION_FORMAT_AES, ++ BLOCKDEV_QCOW_ENCRYPTION_FORMAT__MAX, ++} BlockdevQcowEncryptionFormat; ++ ++#define BlockdevQcowEncryptionFormat_str(val) \ ++ qapi_enum_lookup(&BlockdevQcowEncryptionFormat_lookup, (val)) ++ ++extern const QEnumLookup BlockdevQcowEncryptionFormat_lookup; ++ ++typedef struct q_obj_BlockdevQcowEncryption_base q_obj_BlockdevQcowEncryption_base; ++ ++typedef struct BlockdevQcowEncryption BlockdevQcowEncryption; ++ ++typedef struct BlockdevOptionsQcow BlockdevOptionsQcow; ++ ++typedef enum BlockdevQcow2EncryptionFormat { ++ BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES, ++ BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS, ++ BLOCKDEV_QCOW2_ENCRYPTION_FORMAT__MAX, ++} BlockdevQcow2EncryptionFormat; ++ ++#define BlockdevQcow2EncryptionFormat_str(val) \ ++ qapi_enum_lookup(&BlockdevQcow2EncryptionFormat_lookup, (val)) ++ ++extern const QEnumLookup BlockdevQcow2EncryptionFormat_lookup; ++ ++typedef struct q_obj_BlockdevQcow2Encryption_base q_obj_BlockdevQcow2Encryption_base; ++ ++typedef struct BlockdevQcow2Encryption BlockdevQcow2Encryption; ++ ++typedef struct BlockdevOptionsPreallocate BlockdevOptionsPreallocate; ++ ++typedef struct BlockdevOptionsQcow2 BlockdevOptionsQcow2; ++ ++typedef enum SshHostKeyCheckMode { ++ SSH_HOST_KEY_CHECK_MODE_NONE, ++ SSH_HOST_KEY_CHECK_MODE_HASH, ++ SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS, ++ SSH_HOST_KEY_CHECK_MODE__MAX, ++} SshHostKeyCheckMode; ++ ++#define SshHostKeyCheckMode_str(val) \ ++ qapi_enum_lookup(&SshHostKeyCheckMode_lookup, (val)) ++ ++extern const QEnumLookup SshHostKeyCheckMode_lookup; ++ ++typedef enum SshHostKeyCheckHashType { ++ SSH_HOST_KEY_CHECK_HASH_TYPE_MD5, ++ SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1, ++ SSH_HOST_KEY_CHECK_HASH_TYPE_SHA256, ++ SSH_HOST_KEY_CHECK_HASH_TYPE__MAX, ++} SshHostKeyCheckHashType; ++ ++#define SshHostKeyCheckHashType_str(val) \ ++ qapi_enum_lookup(&SshHostKeyCheckHashType_lookup, (val)) ++ ++extern const QEnumLookup SshHostKeyCheckHashType_lookup; ++ ++typedef struct SshHostKeyHash SshHostKeyHash; ++ ++typedef struct q_obj_SshHostKeyCheck_base q_obj_SshHostKeyCheck_base; ++ ++typedef struct SshHostKeyCheck SshHostKeyCheck; ++ ++typedef struct BlockdevOptionsSsh BlockdevOptionsSsh; ++ ++typedef enum BlkdebugEvent { ++ BLKDBG_L1_UPDATE, ++ BLKDBG_L1_GROW_ALLOC_TABLE, ++ BLKDBG_L1_GROW_WRITE_TABLE, ++ BLKDBG_L1_GROW_ACTIVATE_TABLE, ++ BLKDBG_L2_LOAD, ++ BLKDBG_L2_UPDATE, ++ BLKDBG_L2_UPDATE_COMPRESSED, ++ BLKDBG_L2_ALLOC_COW_READ, ++ BLKDBG_L2_ALLOC_WRITE, ++ BLKDBG_READ_AIO, ++ BLKDBG_READ_BACKING_AIO, ++ BLKDBG_READ_COMPRESSED, ++ BLKDBG_WRITE_AIO, ++ BLKDBG_WRITE_COMPRESSED, ++ BLKDBG_VMSTATE_LOAD, ++ BLKDBG_VMSTATE_SAVE, ++ BLKDBG_COW_READ, ++ BLKDBG_COW_WRITE, ++ BLKDBG_REFTABLE_LOAD, ++ BLKDBG_REFTABLE_GROW, ++ BLKDBG_REFTABLE_UPDATE, ++ BLKDBG_REFBLOCK_LOAD, ++ BLKDBG_REFBLOCK_UPDATE, ++ BLKDBG_REFBLOCK_UPDATE_PART, ++ BLKDBG_REFBLOCK_ALLOC, ++ BLKDBG_REFBLOCK_ALLOC_HOOKUP, ++ BLKDBG_REFBLOCK_ALLOC_WRITE, ++ BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS, ++ BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE, ++ BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE, ++ BLKDBG_CLUSTER_ALLOC, ++ BLKDBG_CLUSTER_ALLOC_BYTES, ++ BLKDBG_CLUSTER_FREE, ++ BLKDBG_FLUSH_TO_OS, ++ BLKDBG_FLUSH_TO_DISK, ++ BLKDBG_PWRITEV_RMW_HEAD, ++ BLKDBG_PWRITEV_RMW_AFTER_HEAD, ++ BLKDBG_PWRITEV_RMW_TAIL, ++ BLKDBG_PWRITEV_RMW_AFTER_TAIL, ++ BLKDBG_PWRITEV, ++ BLKDBG_PWRITEV_ZERO, ++ BLKDBG_PWRITEV_DONE, ++ BLKDBG_EMPTY_IMAGE_PREPARE, ++ BLKDBG_L1_SHRINK_WRITE_TABLE, ++ BLKDBG_L1_SHRINK_FREE_L2_CLUSTERS, ++ BLKDBG_COR_WRITE, ++ BLKDBG_CLUSTER_ALLOC_SPACE, ++ BLKDBG_NONE, ++ BLKDBG__MAX, ++} BlkdebugEvent; ++ ++#define BlkdebugEvent_str(val) \ ++ qapi_enum_lookup(&BlkdebugEvent_lookup, (val)) ++ ++extern const QEnumLookup BlkdebugEvent_lookup; ++ ++typedef enum BlkdebugIOType { ++ BLKDEBUG_IO_TYPE_READ, ++ BLKDEBUG_IO_TYPE_WRITE, ++ BLKDEBUG_IO_TYPE_WRITE_ZEROES, ++ BLKDEBUG_IO_TYPE_DISCARD, ++ BLKDEBUG_IO_TYPE_FLUSH, ++ BLKDEBUG_IO_TYPE_BLOCK_STATUS, ++ BLKDEBUG_IO_TYPE__MAX, ++} BlkdebugIOType; ++ ++#define BlkdebugIOType_str(val) \ ++ qapi_enum_lookup(&BlkdebugIOType_lookup, (val)) ++ ++extern const QEnumLookup BlkdebugIOType_lookup; ++ ++typedef struct BlkdebugInjectErrorOptions BlkdebugInjectErrorOptions; ++ ++typedef struct BlkdebugSetStateOptions BlkdebugSetStateOptions; ++ ++typedef struct BlkdebugInjectErrorOptionsList BlkdebugInjectErrorOptionsList; ++ ++typedef struct BlkdebugSetStateOptionsList BlkdebugSetStateOptionsList; ++ ++typedef struct BlockdevOptionsBlkdebug BlockdevOptionsBlkdebug; ++ ++typedef struct BlockdevOptionsBlklogwrites BlockdevOptionsBlklogwrites; ++ ++typedef struct BlockdevOptionsBlkverify BlockdevOptionsBlkverify; ++ ++typedef struct BlockdevOptionsBlkreplay BlockdevOptionsBlkreplay; ++ ++typedef enum QuorumReadPattern { ++ QUORUM_READ_PATTERN_QUORUM, ++ QUORUM_READ_PATTERN_FIFO, ++ QUORUM_READ_PATTERN__MAX, ++} QuorumReadPattern; ++ ++#define QuorumReadPattern_str(val) \ ++ qapi_enum_lookup(&QuorumReadPattern_lookup, (val)) ++ ++extern const QEnumLookup QuorumReadPattern_lookup; ++ ++typedef struct BlockdevRefList BlockdevRefList; ++ ++typedef struct BlockdevOptionsQuorum BlockdevOptionsQuorum; ++ ++typedef struct BlockdevOptionsGluster BlockdevOptionsGluster; ++ ++#if defined(CONFIG_BLKIO) ++typedef struct BlockdevOptionsIoUring BlockdevOptionsIoUring; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++typedef struct BlockdevOptionsNvmeIoUring BlockdevOptionsNvmeIoUring; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++typedef struct BlockdevOptionsVirtioBlkVfioPci BlockdevOptionsVirtioBlkVfioPci; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++typedef struct BlockdevOptionsVirtioBlkVhostUser BlockdevOptionsVirtioBlkVhostUser; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++typedef struct BlockdevOptionsVirtioBlkVhostVdpa BlockdevOptionsVirtioBlkVhostVdpa; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++typedef enum IscsiTransport { ++ ISCSI_TRANSPORT_TCP, ++ ISCSI_TRANSPORT_ISER, ++ ISCSI_TRANSPORT__MAX, ++} IscsiTransport; ++ ++#define IscsiTransport_str(val) \ ++ qapi_enum_lookup(&IscsiTransport_lookup, (val)) ++ ++extern const QEnumLookup IscsiTransport_lookup; ++ ++typedef enum IscsiHeaderDigest { ++ QAPI_ISCSI_HEADER_DIGEST_CRC32C, ++ QAPI_ISCSI_HEADER_DIGEST_NONE, ++ QAPI_ISCSI_HEADER_DIGEST_CRC32C_NONE, ++ QAPI_ISCSI_HEADER_DIGEST_NONE_CRC32C, ++ QAPI_ISCSI_HEADER_DIGEST__MAX, ++} IscsiHeaderDigest; ++ ++#define IscsiHeaderDigest_str(val) \ ++ qapi_enum_lookup(&IscsiHeaderDigest_lookup, (val)) ++ ++extern const QEnumLookup IscsiHeaderDigest_lookup; ++ ++typedef struct BlockdevOptionsIscsi BlockdevOptionsIscsi; ++ ++typedef enum RbdAuthMode { ++ RBD_AUTH_MODE_CEPHX, ++ RBD_AUTH_MODE_NONE, ++ RBD_AUTH_MODE__MAX, ++} RbdAuthMode; ++ ++#define RbdAuthMode_str(val) \ ++ qapi_enum_lookup(&RbdAuthMode_lookup, (val)) ++ ++extern const QEnumLookup RbdAuthMode_lookup; ++ ++typedef enum RbdImageEncryptionFormat { ++ RBD_IMAGE_ENCRYPTION_FORMAT_LUKS, ++ RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2, ++ RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY, ++ RBD_IMAGE_ENCRYPTION_FORMAT__MAX, ++} RbdImageEncryptionFormat; ++ ++#define RbdImageEncryptionFormat_str(val) \ ++ qapi_enum_lookup(&RbdImageEncryptionFormat_lookup, (val)) ++ ++extern const QEnumLookup RbdImageEncryptionFormat_lookup; ++ ++typedef struct RbdEncryptionOptionsLUKSBase RbdEncryptionOptionsLUKSBase; ++ ++typedef struct RbdEncryptionCreateOptionsLUKSBase RbdEncryptionCreateOptionsLUKSBase; ++ ++typedef struct RbdEncryptionOptionsLUKS RbdEncryptionOptionsLUKS; ++ ++typedef struct RbdEncryptionOptionsLUKS2 RbdEncryptionOptionsLUKS2; ++ ++typedef struct RbdEncryptionOptionsLUKSAny RbdEncryptionOptionsLUKSAny; ++ ++typedef struct RbdEncryptionCreateOptionsLUKS RbdEncryptionCreateOptionsLUKS; ++ ++typedef struct RbdEncryptionCreateOptionsLUKS2 RbdEncryptionCreateOptionsLUKS2; ++ ++typedef struct q_obj_RbdEncryptionOptions_base q_obj_RbdEncryptionOptions_base; ++ ++typedef struct RbdEncryptionOptions RbdEncryptionOptions; ++ ++typedef struct q_obj_RbdEncryptionCreateOptions_base q_obj_RbdEncryptionCreateOptions_base; ++ ++typedef struct RbdEncryptionCreateOptions RbdEncryptionCreateOptions; ++ ++typedef struct RbdAuthModeList RbdAuthModeList; ++ ++typedef struct BlockdevOptionsRbd BlockdevOptionsRbd; ++ ++#if defined(CONFIG_REPLICATION) ++typedef enum ReplicationMode { ++ REPLICATION_MODE_PRIMARY, ++ REPLICATION_MODE_SECONDARY, ++ REPLICATION_MODE__MAX, ++} ReplicationMode; ++ ++#define ReplicationMode_str(val) \ ++ qapi_enum_lookup(&ReplicationMode_lookup, (val)) ++ ++extern const QEnumLookup ReplicationMode_lookup; ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++#if defined(CONFIG_REPLICATION) ++typedef struct BlockdevOptionsReplication BlockdevOptionsReplication; ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++typedef enum NFSTransport { ++ NFS_TRANSPORT_INET, ++ NFS_TRANSPORT__MAX, ++} NFSTransport; ++ ++#define NFSTransport_str(val) \ ++ qapi_enum_lookup(&NFSTransport_lookup, (val)) ++ ++extern const QEnumLookup NFSTransport_lookup; ++ ++typedef struct NFSServer NFSServer; ++ ++typedef struct BlockdevOptionsNfs BlockdevOptionsNfs; ++ ++typedef struct BlockdevOptionsCurlBase BlockdevOptionsCurlBase; ++ ++typedef struct BlockdevOptionsCurlHttp BlockdevOptionsCurlHttp; ++ ++typedef struct BlockdevOptionsCurlHttps BlockdevOptionsCurlHttps; ++ ++typedef struct BlockdevOptionsCurlFtp BlockdevOptionsCurlFtp; ++ ++typedef struct BlockdevOptionsCurlFtps BlockdevOptionsCurlFtps; ++ ++typedef struct BlockdevOptionsNbd BlockdevOptionsNbd; ++ ++typedef struct BlockdevOptionsRaw BlockdevOptionsRaw; ++ ++typedef struct BlockdevOptionsThrottle BlockdevOptionsThrottle; ++ ++typedef struct BlockdevOptionsCor BlockdevOptionsCor; ++ ++typedef enum OnCbwError { ++ ON_CBW_ERROR_BREAK_GUEST_WRITE, ++ ON_CBW_ERROR_BREAK_SNAPSHOT, ++ ON_CBW_ERROR__MAX, ++} OnCbwError; ++ ++#define OnCbwError_str(val) \ ++ qapi_enum_lookup(&OnCbwError_lookup, (val)) ++ ++extern const QEnumLookup OnCbwError_lookup; ++ ++typedef struct BlockdevOptionsCbw BlockdevOptionsCbw; ++ ++typedef struct q_obj_BlockdevOptions_base q_obj_BlockdevOptions_base; ++ ++typedef struct BlockdevOptions BlockdevOptions; ++ ++typedef struct BlockdevRef BlockdevRef; ++ ++typedef struct BlockdevRefOrNull BlockdevRefOrNull; ++ ++typedef struct BlockdevOptionsList BlockdevOptionsList; ++ ++typedef struct q_obj_blockdev_reopen_arg q_obj_blockdev_reopen_arg; ++ ++typedef struct q_obj_blockdev_del_arg q_obj_blockdev_del_arg; ++ ++typedef struct BlockdevCreateOptionsFile BlockdevCreateOptionsFile; ++ ++typedef struct BlockdevCreateOptionsGluster BlockdevCreateOptionsGluster; ++ ++typedef struct BlockdevCreateOptionsLUKS BlockdevCreateOptionsLUKS; ++ ++typedef struct BlockdevCreateOptionsNfs BlockdevCreateOptionsNfs; ++ ++typedef struct BlockdevCreateOptionsParallels BlockdevCreateOptionsParallels; ++ ++typedef struct BlockdevCreateOptionsQcow BlockdevCreateOptionsQcow; ++ ++typedef enum BlockdevQcow2Version { ++ BLOCKDEV_QCOW2_VERSION_V2, ++ BLOCKDEV_QCOW2_VERSION_V3, ++ BLOCKDEV_QCOW2_VERSION__MAX, ++} BlockdevQcow2Version; ++ ++#define BlockdevQcow2Version_str(val) \ ++ qapi_enum_lookup(&BlockdevQcow2Version_lookup, (val)) ++ ++extern const QEnumLookup BlockdevQcow2Version_lookup; ++ ++typedef enum Qcow2CompressionType { ++ QCOW2_COMPRESSION_TYPE_ZLIB, ++#if defined(CONFIG_ZSTD) ++ QCOW2_COMPRESSION_TYPE_ZSTD, ++#endif /* defined(CONFIG_ZSTD) */ ++ QCOW2_COMPRESSION_TYPE__MAX, ++} Qcow2CompressionType; ++ ++#define Qcow2CompressionType_str(val) \ ++ qapi_enum_lookup(&Qcow2CompressionType_lookup, (val)) ++ ++extern const QEnumLookup Qcow2CompressionType_lookup; ++ ++typedef struct BlockdevCreateOptionsQcow2 BlockdevCreateOptionsQcow2; ++ ++typedef struct BlockdevCreateOptionsQed BlockdevCreateOptionsQed; ++ ++typedef struct BlockdevCreateOptionsRbd BlockdevCreateOptionsRbd; ++ ++typedef enum BlockdevVmdkSubformat { ++ BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICSPARSE, ++ BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICFLAT, ++ BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTSPARSE, ++ BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT, ++ BLOCKDEV_VMDK_SUBFORMAT_STREAMOPTIMIZED, ++ BLOCKDEV_VMDK_SUBFORMAT__MAX, ++} BlockdevVmdkSubformat; ++ ++#define BlockdevVmdkSubformat_str(val) \ ++ qapi_enum_lookup(&BlockdevVmdkSubformat_lookup, (val)) ++ ++extern const QEnumLookup BlockdevVmdkSubformat_lookup; ++ ++typedef enum BlockdevVmdkAdapterType { ++ BLOCKDEV_VMDK_ADAPTER_TYPE_IDE, ++ BLOCKDEV_VMDK_ADAPTER_TYPE_BUSLOGIC, ++ BLOCKDEV_VMDK_ADAPTER_TYPE_LSILOGIC, ++ BLOCKDEV_VMDK_ADAPTER_TYPE_LEGACYESX, ++ BLOCKDEV_VMDK_ADAPTER_TYPE__MAX, ++} BlockdevVmdkAdapterType; ++ ++#define BlockdevVmdkAdapterType_str(val) \ ++ qapi_enum_lookup(&BlockdevVmdkAdapterType_lookup, (val)) ++ ++extern const QEnumLookup BlockdevVmdkAdapterType_lookup; ++ ++typedef struct BlockdevCreateOptionsVmdk BlockdevCreateOptionsVmdk; ++ ++typedef struct BlockdevCreateOptionsSsh BlockdevCreateOptionsSsh; ++ ++typedef struct BlockdevCreateOptionsVdi BlockdevCreateOptionsVdi; ++ ++typedef enum BlockdevVhdxSubformat { ++ BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC, ++ BLOCKDEV_VHDX_SUBFORMAT_FIXED, ++ BLOCKDEV_VHDX_SUBFORMAT__MAX, ++} BlockdevVhdxSubformat; ++ ++#define BlockdevVhdxSubformat_str(val) \ ++ qapi_enum_lookup(&BlockdevVhdxSubformat_lookup, (val)) ++ ++extern const QEnumLookup BlockdevVhdxSubformat_lookup; ++ ++typedef struct BlockdevCreateOptionsVhdx BlockdevCreateOptionsVhdx; ++ ++typedef enum BlockdevVpcSubformat { ++ BLOCKDEV_VPC_SUBFORMAT_DYNAMIC, ++ BLOCKDEV_VPC_SUBFORMAT_FIXED, ++ BLOCKDEV_VPC_SUBFORMAT__MAX, ++} BlockdevVpcSubformat; ++ ++#define BlockdevVpcSubformat_str(val) \ ++ qapi_enum_lookup(&BlockdevVpcSubformat_lookup, (val)) ++ ++extern const QEnumLookup BlockdevVpcSubformat_lookup; ++ ++typedef struct BlockdevCreateOptionsVpc BlockdevCreateOptionsVpc; ++ ++typedef struct q_obj_BlockdevCreateOptions_base q_obj_BlockdevCreateOptions_base; ++ ++typedef struct BlockdevCreateOptions BlockdevCreateOptions; ++ ++typedef struct q_obj_blockdev_create_arg q_obj_blockdev_create_arg; ++ ++typedef struct BlockdevAmendOptionsLUKS BlockdevAmendOptionsLUKS; ++ ++typedef struct BlockdevAmendOptionsQcow2 BlockdevAmendOptionsQcow2; ++ ++typedef struct q_obj_BlockdevAmendOptions_base q_obj_BlockdevAmendOptions_base; ++ ++typedef struct BlockdevAmendOptions BlockdevAmendOptions; ++ ++typedef struct q_obj_x_blockdev_amend_arg q_obj_x_blockdev_amend_arg; ++ ++typedef enum BlockErrorAction { ++ BLOCK_ERROR_ACTION_IGNORE, ++ BLOCK_ERROR_ACTION_REPORT, ++ BLOCK_ERROR_ACTION_STOP, ++ BLOCK_ERROR_ACTION__MAX, ++} BlockErrorAction; ++ ++#define BlockErrorAction_str(val) \ ++ qapi_enum_lookup(&BlockErrorAction_lookup, (val)) ++ ++extern const QEnumLookup BlockErrorAction_lookup; ++ ++typedef struct q_obj_BLOCK_IMAGE_CORRUPTED_arg q_obj_BLOCK_IMAGE_CORRUPTED_arg; ++ ++typedef struct q_obj_BLOCK_IO_ERROR_arg q_obj_BLOCK_IO_ERROR_arg; ++ ++typedef struct q_obj_BLOCK_JOB_COMPLETED_arg q_obj_BLOCK_JOB_COMPLETED_arg; ++ ++typedef struct q_obj_BLOCK_JOB_CANCELLED_arg q_obj_BLOCK_JOB_CANCELLED_arg; ++ ++typedef struct q_obj_BLOCK_JOB_ERROR_arg q_obj_BLOCK_JOB_ERROR_arg; ++ ++typedef struct q_obj_BLOCK_JOB_READY_arg q_obj_BLOCK_JOB_READY_arg; ++ ++typedef struct q_obj_BLOCK_JOB_PENDING_arg q_obj_BLOCK_JOB_PENDING_arg; ++ ++typedef enum PreallocMode { ++ PREALLOC_MODE_OFF, ++ PREALLOC_MODE_METADATA, ++ PREALLOC_MODE_FALLOC, ++ PREALLOC_MODE_FULL, ++ PREALLOC_MODE__MAX, ++} PreallocMode; ++ ++#define PreallocMode_str(val) \ ++ qapi_enum_lookup(&PreallocMode_lookup, (val)) ++ ++extern const QEnumLookup PreallocMode_lookup; ++ ++typedef struct q_obj_BLOCK_WRITE_THRESHOLD_arg q_obj_BLOCK_WRITE_THRESHOLD_arg; ++ ++typedef struct q_obj_block_set_write_threshold_arg q_obj_block_set_write_threshold_arg; ++ ++typedef struct q_obj_x_blockdev_change_arg q_obj_x_blockdev_change_arg; ++ ++typedef struct q_obj_x_blockdev_set_iothread_arg q_obj_x_blockdev_set_iothread_arg; ++ ++typedef enum QuorumOpType { ++ QUORUM_OP_TYPE_READ, ++ QUORUM_OP_TYPE_WRITE, ++ QUORUM_OP_TYPE_FLUSH, ++ QUORUM_OP_TYPE__MAX, ++} QuorumOpType; ++ ++#define QuorumOpType_str(val) \ ++ qapi_enum_lookup(&QuorumOpType_lookup, (val)) ++ ++extern const QEnumLookup QuorumOpType_lookup; ++ ++typedef struct q_obj_QUORUM_FAILURE_arg q_obj_QUORUM_FAILURE_arg; ++ ++typedef struct q_obj_QUORUM_REPORT_BAD_arg q_obj_QUORUM_REPORT_BAD_arg; ++ ++typedef struct BlockdevSnapshotInternal BlockdevSnapshotInternal; ++ ++typedef struct q_obj_blockdev_snapshot_delete_internal_sync_arg q_obj_blockdev_snapshot_delete_internal_sync_arg; ++ ++typedef struct BlockGraphInfoList BlockGraphInfoList; ++ ++typedef struct DummyBlockCoreForceArrays DummyBlockCoreForceArrays; ++ ++struct SnapshotInfo { ++ char *id; ++ char *name; ++ int64_t vm_state_size; ++ int64_t date_sec; ++ int64_t date_nsec; ++ int64_t vm_clock_sec; ++ int64_t vm_clock_nsec; ++ bool has_icount; ++ int64_t icount; ++}; ++ ++void qapi_free_SnapshotInfo(SnapshotInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SnapshotInfo, qapi_free_SnapshotInfo) ++ ++struct ImageInfoSpecificQCow2EncryptionBase { ++ BlockdevQcow2EncryptionFormat format; ++}; ++ ++void qapi_free_ImageInfoSpecificQCow2EncryptionBase(ImageInfoSpecificQCow2EncryptionBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2EncryptionBase, qapi_free_ImageInfoSpecificQCow2EncryptionBase) ++ ++struct ImageInfoSpecificQCow2Encryption { ++ /* Members inherited from ImageInfoSpecificQCow2EncryptionBase: */ ++ BlockdevQcow2EncryptionFormat format; ++ /* Own members: */ ++ union { /* union tag is @format */ ++ QCryptoBlockInfoLUKS luks; ++ } u; ++}; ++ ++static inline ImageInfoSpecificQCow2EncryptionBase *qapi_ImageInfoSpecificQCow2Encryption_base(const ImageInfoSpecificQCow2Encryption *obj) ++{ ++ return (ImageInfoSpecificQCow2EncryptionBase *)obj; ++} ++ ++void qapi_free_ImageInfoSpecificQCow2Encryption(ImageInfoSpecificQCow2Encryption *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2Encryption, qapi_free_ImageInfoSpecificQCow2Encryption) ++ ++struct Qcow2BitmapInfoList { ++ Qcow2BitmapInfoList *next; ++ Qcow2BitmapInfo *value; ++}; ++ ++void qapi_free_Qcow2BitmapInfoList(Qcow2BitmapInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(Qcow2BitmapInfoList, qapi_free_Qcow2BitmapInfoList) ++ ++struct ImageInfoSpecificQCow2 { ++ char *compat; ++ char *data_file; ++ bool has_data_file_raw; ++ bool data_file_raw; ++ bool has_extended_l2; ++ bool extended_l2; ++ bool has_lazy_refcounts; ++ bool lazy_refcounts; ++ bool has_corrupt; ++ bool corrupt; ++ int64_t refcount_bits; ++ ImageInfoSpecificQCow2Encryption *encrypt; ++ bool has_bitmaps; ++ Qcow2BitmapInfoList *bitmaps; ++ Qcow2CompressionType compression_type; ++}; ++ ++void qapi_free_ImageInfoSpecificQCow2(ImageInfoSpecificQCow2 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2, qapi_free_ImageInfoSpecificQCow2) ++ ++struct VmdkExtentInfoList { ++ VmdkExtentInfoList *next; ++ VmdkExtentInfo *value; ++}; ++ ++void qapi_free_VmdkExtentInfoList(VmdkExtentInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(VmdkExtentInfoList, qapi_free_VmdkExtentInfoList) ++ ++struct ImageInfoSpecificVmdk { ++ char *create_type; ++ int64_t cid; ++ int64_t parent_cid; ++ VmdkExtentInfoList *extents; ++}; ++ ++void qapi_free_ImageInfoSpecificVmdk(ImageInfoSpecificVmdk *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificVmdk, qapi_free_ImageInfoSpecificVmdk) ++ ++struct VmdkExtentInfo { ++ char *filename; ++ char *format; ++ int64_t virtual_size; ++ bool has_cluster_size; ++ int64_t cluster_size; ++ bool has_compressed; ++ bool compressed; ++}; ++ ++void qapi_free_VmdkExtentInfo(VmdkExtentInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(VmdkExtentInfo, qapi_free_VmdkExtentInfo) ++ ++struct ImageInfoSpecificRbd { ++ bool has_encryption_format; ++ RbdImageEncryptionFormat encryption_format; ++}; ++ ++void qapi_free_ImageInfoSpecificRbd(ImageInfoSpecificRbd *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificRbd, qapi_free_ImageInfoSpecificRbd) ++ ++struct ImageInfoSpecificFile { ++ bool has_extent_size_hint; ++ uint64_t extent_size_hint; ++}; ++ ++void qapi_free_ImageInfoSpecificFile(ImageInfoSpecificFile *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificFile, qapi_free_ImageInfoSpecificFile) ++ ++struct ImageInfoSpecificQCow2Wrapper { ++ ImageInfoSpecificQCow2 *data; ++}; ++ ++void qapi_free_ImageInfoSpecificQCow2Wrapper(ImageInfoSpecificQCow2Wrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2Wrapper, qapi_free_ImageInfoSpecificQCow2Wrapper) ++ ++struct ImageInfoSpecificVmdkWrapper { ++ ImageInfoSpecificVmdk *data; ++}; ++ ++void qapi_free_ImageInfoSpecificVmdkWrapper(ImageInfoSpecificVmdkWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificVmdkWrapper, qapi_free_ImageInfoSpecificVmdkWrapper) ++ ++struct ImageInfoSpecificLUKSWrapper { ++ QCryptoBlockInfoLUKS *data; ++}; ++ ++void qapi_free_ImageInfoSpecificLUKSWrapper(ImageInfoSpecificLUKSWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificLUKSWrapper, qapi_free_ImageInfoSpecificLUKSWrapper) ++ ++struct ImageInfoSpecificRbdWrapper { ++ ImageInfoSpecificRbd *data; ++}; ++ ++void qapi_free_ImageInfoSpecificRbdWrapper(ImageInfoSpecificRbdWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificRbdWrapper, qapi_free_ImageInfoSpecificRbdWrapper) ++ ++struct ImageInfoSpecificFileWrapper { ++ ImageInfoSpecificFile *data; ++}; ++ ++void qapi_free_ImageInfoSpecificFileWrapper(ImageInfoSpecificFileWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificFileWrapper, qapi_free_ImageInfoSpecificFileWrapper) ++ ++struct q_obj_ImageInfoSpecific_base { ++ ImageInfoSpecificKind type; ++}; ++ ++struct ImageInfoSpecific { ++ ImageInfoSpecificKind type; ++ union { /* union tag is @type */ ++ ImageInfoSpecificQCow2Wrapper qcow2; ++ ImageInfoSpecificVmdkWrapper vmdk; ++ ImageInfoSpecificLUKSWrapper luks; ++ ImageInfoSpecificRbdWrapper rbd; ++ ImageInfoSpecificFileWrapper file; ++ } u; ++}; ++ ++void qapi_free_ImageInfoSpecific(ImageInfoSpecific *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecific, qapi_free_ImageInfoSpecific) ++ ++struct SnapshotInfoList { ++ SnapshotInfoList *next; ++ SnapshotInfo *value; ++}; ++ ++void qapi_free_SnapshotInfoList(SnapshotInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SnapshotInfoList, qapi_free_SnapshotInfoList) ++ ++struct BlockNodeInfo { ++ char *filename; ++ char *format; ++ bool has_dirty_flag; ++ bool dirty_flag; ++ bool has_actual_size; ++ int64_t actual_size; ++ int64_t virtual_size; ++ bool has_cluster_size; ++ int64_t cluster_size; ++ bool has_encrypted; ++ bool encrypted; ++ bool has_compressed; ++ bool compressed; ++ char *backing_filename; ++ char *full_backing_filename; ++ char *backing_filename_format; ++ bool has_snapshots; ++ SnapshotInfoList *snapshots; ++ ImageInfoSpecific *format_specific; ++}; ++ ++void qapi_free_BlockNodeInfo(BlockNodeInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockNodeInfo, qapi_free_BlockNodeInfo) ++ ++struct ImageInfo { ++ /* Members inherited from BlockNodeInfo: */ ++ char *filename; ++ char *format; ++ bool has_dirty_flag; ++ bool dirty_flag; ++ bool has_actual_size; ++ int64_t actual_size; ++ int64_t virtual_size; ++ bool has_cluster_size; ++ int64_t cluster_size; ++ bool has_encrypted; ++ bool encrypted; ++ bool has_compressed; ++ bool compressed; ++ char *backing_filename; ++ char *full_backing_filename; ++ char *backing_filename_format; ++ bool has_snapshots; ++ SnapshotInfoList *snapshots; ++ ImageInfoSpecific *format_specific; ++ /* Own members: */ ++ ImageInfo *backing_image; ++}; ++ ++static inline BlockNodeInfo *qapi_ImageInfo_base(const ImageInfo *obj) ++{ ++ return (BlockNodeInfo *)obj; ++} ++ ++void qapi_free_ImageInfo(ImageInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfo, qapi_free_ImageInfo) ++ ++struct BlockChildInfo { ++ char *name; ++ BlockGraphInfo *info; ++}; ++ ++void qapi_free_BlockChildInfo(BlockChildInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockChildInfo, qapi_free_BlockChildInfo) ++ ++struct BlockChildInfoList { ++ BlockChildInfoList *next; ++ BlockChildInfo *value; ++}; ++ ++void qapi_free_BlockChildInfoList(BlockChildInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockChildInfoList, qapi_free_BlockChildInfoList) ++ ++struct BlockGraphInfo { ++ /* Members inherited from BlockNodeInfo: */ ++ char *filename; ++ char *format; ++ bool has_dirty_flag; ++ bool dirty_flag; ++ bool has_actual_size; ++ int64_t actual_size; ++ int64_t virtual_size; ++ bool has_cluster_size; ++ int64_t cluster_size; ++ bool has_encrypted; ++ bool encrypted; ++ bool has_compressed; ++ bool compressed; ++ char *backing_filename; ++ char *full_backing_filename; ++ char *backing_filename_format; ++ bool has_snapshots; ++ SnapshotInfoList *snapshots; ++ ImageInfoSpecific *format_specific; ++ /* Own members: */ ++ BlockChildInfoList *children; ++}; ++ ++static inline BlockNodeInfo *qapi_BlockGraphInfo_base(const BlockGraphInfo *obj) ++{ ++ return (BlockNodeInfo *)obj; ++} ++ ++void qapi_free_BlockGraphInfo(BlockGraphInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockGraphInfo, qapi_free_BlockGraphInfo) ++ ++struct ImageCheck { ++ char *filename; ++ char *format; ++ int64_t check_errors; ++ bool has_image_end_offset; ++ int64_t image_end_offset; ++ bool has_corruptions; ++ int64_t corruptions; ++ bool has_leaks; ++ int64_t leaks; ++ bool has_corruptions_fixed; ++ int64_t corruptions_fixed; ++ bool has_leaks_fixed; ++ int64_t leaks_fixed; ++ bool has_total_clusters; ++ int64_t total_clusters; ++ bool has_allocated_clusters; ++ int64_t allocated_clusters; ++ bool has_fragmented_clusters; ++ int64_t fragmented_clusters; ++ bool has_compressed_clusters; ++ int64_t compressed_clusters; ++}; ++ ++void qapi_free_ImageCheck(ImageCheck *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageCheck, qapi_free_ImageCheck) ++ ++struct MapEntry { ++ int64_t start; ++ int64_t length; ++ bool data; ++ bool zero; ++ bool compressed; ++ int64_t depth; ++ bool present; ++ bool has_offset; ++ int64_t offset; ++ char *filename; ++}; ++ ++void qapi_free_MapEntry(MapEntry *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(MapEntry, qapi_free_MapEntry) ++ ++struct BlockdevCacheInfo { ++ bool writeback; ++ bool direct; ++ bool no_flush; ++}; ++ ++void qapi_free_BlockdevCacheInfo(BlockdevCacheInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCacheInfo, qapi_free_BlockdevCacheInfo) ++ ++struct BlockDirtyInfoList { ++ BlockDirtyInfoList *next; ++ BlockDirtyInfo *value; ++}; ++ ++void qapi_free_BlockDirtyInfoList(BlockDirtyInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyInfoList, qapi_free_BlockDirtyInfoList) ++ ++struct BlockDeviceInfo { ++ char *file; ++ char *node_name; ++ bool ro; ++ char *drv; ++ char *backing_file; ++ int64_t backing_file_depth; ++ bool encrypted; ++ BlockdevDetectZeroesOptions detect_zeroes; ++ int64_t bps; ++ int64_t bps_rd; ++ int64_t bps_wr; ++ int64_t iops; ++ int64_t iops_rd; ++ int64_t iops_wr; ++ ImageInfo *image; ++ bool has_bps_max; ++ int64_t bps_max; ++ bool has_bps_rd_max; ++ int64_t bps_rd_max; ++ bool has_bps_wr_max; ++ int64_t bps_wr_max; ++ bool has_iops_max; ++ int64_t iops_max; ++ bool has_iops_rd_max; ++ int64_t iops_rd_max; ++ bool has_iops_wr_max; ++ int64_t iops_wr_max; ++ bool has_bps_max_length; ++ int64_t bps_max_length; ++ bool has_bps_rd_max_length; ++ int64_t bps_rd_max_length; ++ bool has_bps_wr_max_length; ++ int64_t bps_wr_max_length; ++ bool has_iops_max_length; ++ int64_t iops_max_length; ++ bool has_iops_rd_max_length; ++ int64_t iops_rd_max_length; ++ bool has_iops_wr_max_length; ++ int64_t iops_wr_max_length; ++ bool has_iops_size; ++ int64_t iops_size; ++ char *group; ++ BlockdevCacheInfo *cache; ++ int64_t write_threshold; ++ bool has_dirty_bitmaps; ++ BlockDirtyInfoList *dirty_bitmaps; ++}; ++ ++void qapi_free_BlockDeviceInfo(BlockDeviceInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDeviceInfo, qapi_free_BlockDeviceInfo) ++ ++struct BlockDirtyInfo { ++ char *name; ++ int64_t count; ++ uint32_t granularity; ++ bool recording; ++ bool busy; ++ bool persistent; ++ bool has_inconsistent; ++ bool inconsistent; ++}; ++ ++void qapi_free_BlockDirtyInfo(BlockDirtyInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyInfo, qapi_free_BlockDirtyInfo) ++ ++struct Qcow2BitmapInfoFlagsList { ++ Qcow2BitmapInfoFlagsList *next; ++ Qcow2BitmapInfoFlags value; ++}; ++ ++void qapi_free_Qcow2BitmapInfoFlagsList(Qcow2BitmapInfoFlagsList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(Qcow2BitmapInfoFlagsList, qapi_free_Qcow2BitmapInfoFlagsList) ++ ++struct Qcow2BitmapInfo { ++ char *name; ++ uint32_t granularity; ++ Qcow2BitmapInfoFlagsList *flags; ++}; ++ ++void qapi_free_Qcow2BitmapInfo(Qcow2BitmapInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(Qcow2BitmapInfo, qapi_free_Qcow2BitmapInfo) ++ ++struct BlockLatencyHistogramInfo { ++ uint64List *boundaries; ++ uint64List *bins; ++}; ++ ++void qapi_free_BlockLatencyHistogramInfo(BlockLatencyHistogramInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockLatencyHistogramInfo, qapi_free_BlockLatencyHistogramInfo) ++ ++struct BlockInfo { ++ char *device; ++ char *qdev; ++ char *type; ++ bool removable; ++ bool locked; ++ BlockDeviceInfo *inserted; ++ bool has_tray_open; ++ bool tray_open; ++ bool has_io_status; ++ BlockDeviceIoStatus io_status; ++}; ++ ++void qapi_free_BlockInfo(BlockInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockInfo, qapi_free_BlockInfo) ++ ++struct BlockMeasureInfo { ++ int64_t required; ++ int64_t fully_allocated; ++ bool has_bitmaps; ++ int64_t bitmaps; ++}; ++ ++void qapi_free_BlockMeasureInfo(BlockMeasureInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockMeasureInfo, qapi_free_BlockMeasureInfo) ++ ++struct BlockInfoList { ++ BlockInfoList *next; ++ BlockInfo *value; ++}; ++ ++void qapi_free_BlockInfoList(BlockInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockInfoList, qapi_free_BlockInfoList) ++ ++struct BlockDeviceTimedStats { ++ int64_t interval_length; ++ int64_t min_rd_latency_ns; ++ int64_t max_rd_latency_ns; ++ int64_t avg_rd_latency_ns; ++ int64_t min_wr_latency_ns; ++ int64_t max_wr_latency_ns; ++ int64_t avg_wr_latency_ns; ++ int64_t min_zone_append_latency_ns; ++ int64_t max_zone_append_latency_ns; ++ int64_t avg_zone_append_latency_ns; ++ int64_t min_flush_latency_ns; ++ int64_t max_flush_latency_ns; ++ int64_t avg_flush_latency_ns; ++ double avg_rd_queue_depth; ++ double avg_wr_queue_depth; ++ double avg_zone_append_queue_depth; ++}; ++ ++void qapi_free_BlockDeviceTimedStats(BlockDeviceTimedStats *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDeviceTimedStats, qapi_free_BlockDeviceTimedStats) ++ ++struct BlockDeviceTimedStatsList { ++ BlockDeviceTimedStatsList *next; ++ BlockDeviceTimedStats *value; ++}; ++ ++void qapi_free_BlockDeviceTimedStatsList(BlockDeviceTimedStatsList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDeviceTimedStatsList, qapi_free_BlockDeviceTimedStatsList) ++ ++struct BlockDeviceStats { ++ int64_t rd_bytes; ++ int64_t wr_bytes; ++ int64_t zone_append_bytes; ++ int64_t unmap_bytes; ++ int64_t rd_operations; ++ int64_t wr_operations; ++ int64_t zone_append_operations; ++ int64_t flush_operations; ++ int64_t unmap_operations; ++ int64_t rd_total_time_ns; ++ int64_t wr_total_time_ns; ++ int64_t zone_append_total_time_ns; ++ int64_t flush_total_time_ns; ++ int64_t unmap_total_time_ns; ++ int64_t wr_highest_offset; ++ int64_t rd_merged; ++ int64_t wr_merged; ++ int64_t zone_append_merged; ++ int64_t unmap_merged; ++ bool has_idle_time_ns; ++ int64_t idle_time_ns; ++ int64_t failed_rd_operations; ++ int64_t failed_wr_operations; ++ int64_t failed_zone_append_operations; ++ int64_t failed_flush_operations; ++ int64_t failed_unmap_operations; ++ int64_t invalid_rd_operations; ++ int64_t invalid_wr_operations; ++ int64_t invalid_zone_append_operations; ++ int64_t invalid_flush_operations; ++ int64_t invalid_unmap_operations; ++ bool account_invalid; ++ bool account_failed; ++ BlockDeviceTimedStatsList *timed_stats; ++ BlockLatencyHistogramInfo *rd_latency_histogram; ++ BlockLatencyHistogramInfo *wr_latency_histogram; ++ BlockLatencyHistogramInfo *zone_append_latency_histogram; ++ BlockLatencyHistogramInfo *flush_latency_histogram; ++}; ++ ++void qapi_free_BlockDeviceStats(BlockDeviceStats *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDeviceStats, qapi_free_BlockDeviceStats) ++ ++struct BlockStatsSpecificFile { ++ uint64_t discard_nb_ok; ++ uint64_t discard_nb_failed; ++ uint64_t discard_bytes_ok; ++}; ++ ++void qapi_free_BlockStatsSpecificFile(BlockStatsSpecificFile *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockStatsSpecificFile, qapi_free_BlockStatsSpecificFile) ++ ++struct BlockStatsSpecificNvme { ++ uint64_t completion_errors; ++ uint64_t aligned_accesses; ++ uint64_t unaligned_accesses; ++}; ++ ++void qapi_free_BlockStatsSpecificNvme(BlockStatsSpecificNvme *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockStatsSpecificNvme, qapi_free_BlockStatsSpecificNvme) ++ ++struct q_obj_BlockStatsSpecific_base { ++ BlockdevDriver driver; ++}; ++ ++struct BlockStatsSpecific { ++ BlockdevDriver driver; ++ union { /* union tag is @driver */ ++ BlockStatsSpecificFile file; ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ BlockStatsSpecificFile host_device; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ BlockStatsSpecificNvme nvme; ++ } u; ++}; ++ ++void qapi_free_BlockStatsSpecific(BlockStatsSpecific *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockStatsSpecific, qapi_free_BlockStatsSpecific) ++ ++struct BlockStats { ++ char *device; ++ char *qdev; ++ char *node_name; ++ BlockDeviceStats *stats; ++ BlockStatsSpecific *driver_specific; ++ BlockStats *parent; ++ BlockStats *backing; ++}; ++ ++void qapi_free_BlockStats(BlockStats *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockStats, qapi_free_BlockStats) ++ ++struct q_obj_query_blockstats_arg { ++ bool has_query_nodes; ++ bool query_nodes; ++}; ++ ++struct BlockStatsList { ++ BlockStatsList *next; ++ BlockStats *value; ++}; ++ ++void qapi_free_BlockStatsList(BlockStatsList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockStatsList, qapi_free_BlockStatsList) ++ ++struct BlockJobInfoMirror { ++ bool actively_synced; ++}; ++ ++void qapi_free_BlockJobInfoMirror(BlockJobInfoMirror *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobInfoMirror, qapi_free_BlockJobInfoMirror) ++ ++struct q_obj_BlockJobInfo_base { ++ JobType type; ++ char *device; ++ int64_t len; ++ int64_t offset; ++ bool busy; ++ bool paused; ++ int64_t speed; ++ BlockDeviceIoStatus io_status; ++ bool ready; ++ JobStatus status; ++ bool auto_finalize; ++ bool auto_dismiss; ++ char *error; ++}; ++ ++struct BlockJobInfo { ++ JobType type; ++ char *device; ++ int64_t len; ++ int64_t offset; ++ bool busy; ++ bool paused; ++ int64_t speed; ++ BlockDeviceIoStatus io_status; ++ bool ready; ++ JobStatus status; ++ bool auto_finalize; ++ bool auto_dismiss; ++ char *error; ++ union { /* union tag is @type */ ++ BlockJobInfoMirror mirror; ++ } u; ++}; ++ ++void qapi_free_BlockJobInfo(BlockJobInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobInfo, qapi_free_BlockJobInfo) ++ ++struct BlockJobInfoList { ++ BlockJobInfoList *next; ++ BlockJobInfo *value; ++}; ++ ++void qapi_free_BlockJobInfoList(BlockJobInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobInfoList, qapi_free_BlockJobInfoList) ++ ++struct q_obj_block_resize_arg { ++ char *device; ++ char *node_name; ++ int64_t size; ++}; ++ ++struct BlockdevSnapshotSync { ++ char *device; ++ char *node_name; ++ char *snapshot_file; ++ char *snapshot_node_name; ++ char *format; ++ bool has_mode; ++ NewImageMode mode; ++}; ++ ++void qapi_free_BlockdevSnapshotSync(BlockdevSnapshotSync *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevSnapshotSync, qapi_free_BlockdevSnapshotSync) ++ ++struct BlockdevSnapshot { ++ char *node; ++ char *overlay; ++}; ++ ++void qapi_free_BlockdevSnapshot(BlockdevSnapshot *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevSnapshot, qapi_free_BlockdevSnapshot) ++ ++struct BackupPerf { ++ bool has_use_copy_range; ++ bool use_copy_range; ++ bool has_max_workers; ++ int64_t max_workers; ++ bool has_max_chunk; ++ int64_t max_chunk; ++}; ++ ++void qapi_free_BackupPerf(BackupPerf *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BackupPerf, qapi_free_BackupPerf) ++ ++struct BackupCommon { ++ char *job_id; ++ char *device; ++ MirrorSyncMode sync; ++ bool has_speed; ++ int64_t speed; ++ char *bitmap; ++ bool has_bitmap_mode; ++ BitmapSyncMode bitmap_mode; ++ bool has_compress; ++ bool compress; ++ bool has_on_source_error; ++ BlockdevOnError on_source_error; ++ bool has_on_target_error; ++ BlockdevOnError on_target_error; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++ char *filter_node_name; ++ bool has_discard_source; ++ bool discard_source; ++ BackupPerf *x_perf; ++}; ++ ++void qapi_free_BackupCommon(BackupCommon *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BackupCommon, qapi_free_BackupCommon) ++ ++struct DriveBackup { ++ /* Members inherited from BackupCommon: */ ++ char *job_id; ++ char *device; ++ MirrorSyncMode sync; ++ bool has_speed; ++ int64_t speed; ++ char *bitmap; ++ bool has_bitmap_mode; ++ BitmapSyncMode bitmap_mode; ++ bool has_compress; ++ bool compress; ++ bool has_on_source_error; ++ BlockdevOnError on_source_error; ++ bool has_on_target_error; ++ BlockdevOnError on_target_error; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++ char *filter_node_name; ++ bool has_discard_source; ++ bool discard_source; ++ BackupPerf *x_perf; ++ /* Own members: */ ++ char *target; ++ char *format; ++ bool has_mode; ++ NewImageMode mode; ++}; ++ ++static inline BackupCommon *qapi_DriveBackup_base(const DriveBackup *obj) ++{ ++ return (BackupCommon *)obj; ++} ++ ++void qapi_free_DriveBackup(DriveBackup *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(DriveBackup, qapi_free_DriveBackup) ++ ++struct BlockdevBackup { ++ /* Members inherited from BackupCommon: */ ++ char *job_id; ++ char *device; ++ MirrorSyncMode sync; ++ bool has_speed; ++ int64_t speed; ++ char *bitmap; ++ bool has_bitmap_mode; ++ BitmapSyncMode bitmap_mode; ++ bool has_compress; ++ bool compress; ++ bool has_on_source_error; ++ BlockdevOnError on_source_error; ++ bool has_on_target_error; ++ BlockdevOnError on_target_error; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++ char *filter_node_name; ++ bool has_discard_source; ++ bool discard_source; ++ BackupPerf *x_perf; ++ /* Own members: */ ++ char *target; ++}; ++ ++static inline BackupCommon *qapi_BlockdevBackup_base(const BlockdevBackup *obj) ++{ ++ return (BackupCommon *)obj; ++} ++ ++void qapi_free_BlockdevBackup(BlockdevBackup *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevBackup, qapi_free_BlockdevBackup) ++ ++struct q_obj_change_backing_file_arg { ++ char *device; ++ char *image_node_name; ++ char *backing_file; ++}; ++ ++struct q_obj_block_commit_arg { ++ char *job_id; ++ char *device; ++ char *base_node; ++ char *base; ++ char *top_node; ++ char *top; ++ char *backing_file; ++ bool has_backing_mask_protocol; ++ bool backing_mask_protocol; ++ bool has_speed; ++ int64_t speed; ++ bool has_on_error; ++ BlockdevOnError on_error; ++ char *filter_node_name; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++}; ++ ++struct q_obj_query_named_block_nodes_arg { ++ bool has_flat; ++ bool flat; ++}; ++ ++struct BlockDeviceInfoList { ++ BlockDeviceInfoList *next; ++ BlockDeviceInfo *value; ++}; ++ ++void qapi_free_BlockDeviceInfoList(BlockDeviceInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDeviceInfoList, qapi_free_BlockDeviceInfoList) ++ ++struct XDbgBlockGraphNode { ++ uint64_t id; ++ XDbgBlockGraphNodeType type; ++ char *name; ++}; ++ ++void qapi_free_XDbgBlockGraphNode(XDbgBlockGraphNode *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(XDbgBlockGraphNode, qapi_free_XDbgBlockGraphNode) ++ ++struct BlockPermissionList { ++ BlockPermissionList *next; ++ BlockPermission value; ++}; ++ ++void qapi_free_BlockPermissionList(BlockPermissionList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockPermissionList, qapi_free_BlockPermissionList) ++ ++struct XDbgBlockGraphEdge { ++ uint64_t parent; ++ uint64_t child; ++ char *name; ++ BlockPermissionList *perm; ++ BlockPermissionList *shared_perm; ++}; ++ ++void qapi_free_XDbgBlockGraphEdge(XDbgBlockGraphEdge *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(XDbgBlockGraphEdge, qapi_free_XDbgBlockGraphEdge) ++ ++struct XDbgBlockGraphNodeList { ++ XDbgBlockGraphNodeList *next; ++ XDbgBlockGraphNode *value; ++}; ++ ++void qapi_free_XDbgBlockGraphNodeList(XDbgBlockGraphNodeList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(XDbgBlockGraphNodeList, qapi_free_XDbgBlockGraphNodeList) ++ ++struct XDbgBlockGraphEdgeList { ++ XDbgBlockGraphEdgeList *next; ++ XDbgBlockGraphEdge *value; ++}; ++ ++void qapi_free_XDbgBlockGraphEdgeList(XDbgBlockGraphEdgeList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(XDbgBlockGraphEdgeList, qapi_free_XDbgBlockGraphEdgeList) ++ ++struct XDbgBlockGraph { ++ XDbgBlockGraphNodeList *nodes; ++ XDbgBlockGraphEdgeList *edges; ++}; ++ ++void qapi_free_XDbgBlockGraph(XDbgBlockGraph *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(XDbgBlockGraph, qapi_free_XDbgBlockGraph) ++ ++struct DriveMirror { ++ char *job_id; ++ char *device; ++ char *target; ++ char *format; ++ char *node_name; ++ char *replaces; ++ MirrorSyncMode sync; ++ bool has_mode; ++ NewImageMode mode; ++ bool has_speed; ++ int64_t speed; ++ bool has_granularity; ++ uint32_t granularity; ++ bool has_buf_size; ++ int64_t buf_size; ++ bool has_on_source_error; ++ BlockdevOnError on_source_error; ++ bool has_on_target_error; ++ BlockdevOnError on_target_error; ++ bool has_unmap; ++ bool unmap; ++ bool has_copy_mode; ++ MirrorCopyMode copy_mode; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++}; ++ ++void qapi_free_DriveMirror(DriveMirror *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(DriveMirror, qapi_free_DriveMirror) ++ ++struct BlockDirtyBitmap { ++ char *node; ++ char *name; ++}; ++ ++void qapi_free_BlockDirtyBitmap(BlockDirtyBitmap *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyBitmap, qapi_free_BlockDirtyBitmap) ++ ++struct BlockDirtyBitmapAdd { ++ char *node; ++ char *name; ++ bool has_granularity; ++ uint32_t granularity; ++ bool has_persistent; ++ bool persistent; ++ bool has_disabled; ++ bool disabled; ++}; ++ ++void qapi_free_BlockDirtyBitmapAdd(BlockDirtyBitmapAdd *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyBitmapAdd, qapi_free_BlockDirtyBitmapAdd) ++ ++struct BlockDirtyBitmapOrStr { ++ QType type; ++ union { /* union tag is @type */ ++ char *local; ++ BlockDirtyBitmap external; ++ } u; ++}; ++ ++void qapi_free_BlockDirtyBitmapOrStr(BlockDirtyBitmapOrStr *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyBitmapOrStr, qapi_free_BlockDirtyBitmapOrStr) ++ ++struct BlockDirtyBitmapOrStrList { ++ BlockDirtyBitmapOrStrList *next; ++ BlockDirtyBitmapOrStr *value; ++}; ++ ++void qapi_free_BlockDirtyBitmapOrStrList(BlockDirtyBitmapOrStrList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyBitmapOrStrList, qapi_free_BlockDirtyBitmapOrStrList) ++ ++struct BlockDirtyBitmapMerge { ++ char *node; ++ char *target; ++ BlockDirtyBitmapOrStrList *bitmaps; ++}; ++ ++void qapi_free_BlockDirtyBitmapMerge(BlockDirtyBitmapMerge *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyBitmapMerge, qapi_free_BlockDirtyBitmapMerge) ++ ++struct BlockDirtyBitmapSha256 { ++ char *sha256; ++}; ++ ++void qapi_free_BlockDirtyBitmapSha256(BlockDirtyBitmapSha256 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyBitmapSha256, qapi_free_BlockDirtyBitmapSha256) ++ ++struct q_obj_blockdev_mirror_arg { ++ char *job_id; ++ char *device; ++ char *target; ++ char *replaces; ++ MirrorSyncMode sync; ++ bool has_speed; ++ int64_t speed; ++ bool has_granularity; ++ uint32_t granularity; ++ bool has_buf_size; ++ int64_t buf_size; ++ bool has_on_source_error; ++ BlockdevOnError on_source_error; ++ bool has_on_target_error; ++ BlockdevOnError on_target_error; ++ char *filter_node_name; ++ bool has_copy_mode; ++ MirrorCopyMode copy_mode; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++}; ++ ++struct BlockIOThrottle { ++ char *device; ++ char *id; ++ int64_t bps; ++ int64_t bps_rd; ++ int64_t bps_wr; ++ int64_t iops; ++ int64_t iops_rd; ++ int64_t iops_wr; ++ bool has_bps_max; ++ int64_t bps_max; ++ bool has_bps_rd_max; ++ int64_t bps_rd_max; ++ bool has_bps_wr_max; ++ int64_t bps_wr_max; ++ bool has_iops_max; ++ int64_t iops_max; ++ bool has_iops_rd_max; ++ int64_t iops_rd_max; ++ bool has_iops_wr_max; ++ int64_t iops_wr_max; ++ bool has_bps_max_length; ++ int64_t bps_max_length; ++ bool has_bps_rd_max_length; ++ int64_t bps_rd_max_length; ++ bool has_bps_wr_max_length; ++ int64_t bps_wr_max_length; ++ bool has_iops_max_length; ++ int64_t iops_max_length; ++ bool has_iops_rd_max_length; ++ int64_t iops_rd_max_length; ++ bool has_iops_wr_max_length; ++ int64_t iops_wr_max_length; ++ bool has_iops_size; ++ int64_t iops_size; ++ char *group; ++}; ++ ++void qapi_free_BlockIOThrottle(BlockIOThrottle *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockIOThrottle, qapi_free_BlockIOThrottle) ++ ++struct ThrottleLimits { ++ bool has_iops_total; ++ int64_t iops_total; ++ bool has_iops_total_max; ++ int64_t iops_total_max; ++ bool has_iops_total_max_length; ++ int64_t iops_total_max_length; ++ bool has_iops_read; ++ int64_t iops_read; ++ bool has_iops_read_max; ++ int64_t iops_read_max; ++ bool has_iops_read_max_length; ++ int64_t iops_read_max_length; ++ bool has_iops_write; ++ int64_t iops_write; ++ bool has_iops_write_max; ++ int64_t iops_write_max; ++ bool has_iops_write_max_length; ++ int64_t iops_write_max_length; ++ bool has_bps_total; ++ int64_t bps_total; ++ bool has_bps_total_max; ++ int64_t bps_total_max; ++ bool has_bps_total_max_length; ++ int64_t bps_total_max_length; ++ bool has_bps_read; ++ int64_t bps_read; ++ bool has_bps_read_max; ++ int64_t bps_read_max; ++ bool has_bps_read_max_length; ++ int64_t bps_read_max_length; ++ bool has_bps_write; ++ int64_t bps_write; ++ bool has_bps_write_max; ++ int64_t bps_write_max; ++ bool has_bps_write_max_length; ++ int64_t bps_write_max_length; ++ bool has_iops_size; ++ int64_t iops_size; ++}; ++ ++void qapi_free_ThrottleLimits(ThrottleLimits *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ThrottleLimits, qapi_free_ThrottleLimits) ++ ++struct ThrottleGroupProperties { ++ ThrottleLimits *limits; ++ bool has_x_iops_total; ++ int64_t x_iops_total; ++ bool has_x_iops_total_max; ++ int64_t x_iops_total_max; ++ bool has_x_iops_total_max_length; ++ int64_t x_iops_total_max_length; ++ bool has_x_iops_read; ++ int64_t x_iops_read; ++ bool has_x_iops_read_max; ++ int64_t x_iops_read_max; ++ bool has_x_iops_read_max_length; ++ int64_t x_iops_read_max_length; ++ bool has_x_iops_write; ++ int64_t x_iops_write; ++ bool has_x_iops_write_max; ++ int64_t x_iops_write_max; ++ bool has_x_iops_write_max_length; ++ int64_t x_iops_write_max_length; ++ bool has_x_bps_total; ++ int64_t x_bps_total; ++ bool has_x_bps_total_max; ++ int64_t x_bps_total_max; ++ bool has_x_bps_total_max_length; ++ int64_t x_bps_total_max_length; ++ bool has_x_bps_read; ++ int64_t x_bps_read; ++ bool has_x_bps_read_max; ++ int64_t x_bps_read_max; ++ bool has_x_bps_read_max_length; ++ int64_t x_bps_read_max_length; ++ bool has_x_bps_write; ++ int64_t x_bps_write; ++ bool has_x_bps_write_max; ++ int64_t x_bps_write_max; ++ bool has_x_bps_write_max_length; ++ int64_t x_bps_write_max_length; ++ bool has_x_iops_size; ++ int64_t x_iops_size; ++}; ++ ++void qapi_free_ThrottleGroupProperties(ThrottleGroupProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ThrottleGroupProperties, qapi_free_ThrottleGroupProperties) ++ ++struct q_obj_block_stream_arg { ++ char *job_id; ++ char *device; ++ char *base; ++ char *base_node; ++ char *backing_file; ++ bool has_backing_mask_protocol; ++ bool backing_mask_protocol; ++ char *bottom; ++ bool has_speed; ++ int64_t speed; ++ bool has_on_error; ++ BlockdevOnError on_error; ++ char *filter_node_name; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++}; ++ ++struct q_obj_block_job_set_speed_arg { ++ char *device; ++ int64_t speed; ++}; ++ ++struct q_obj_block_job_cancel_arg { ++ char *device; ++ bool has_force; ++ bool force; ++}; ++ ++struct q_obj_block_job_pause_arg { ++ char *device; ++}; ++ ++struct q_obj_block_job_resume_arg { ++ char *device; ++}; ++ ++struct q_obj_block_job_complete_arg { ++ char *device; ++}; ++ ++struct q_obj_block_job_dismiss_arg { ++ char *id; ++}; ++ ++struct q_obj_block_job_finalize_arg { ++ char *id; ++}; ++ ++struct BlockJobChangeOptionsMirror { ++ MirrorCopyMode copy_mode; ++}; ++ ++void qapi_free_BlockJobChangeOptionsMirror(BlockJobChangeOptionsMirror *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobChangeOptionsMirror, qapi_free_BlockJobChangeOptionsMirror) ++ ++struct q_obj_BlockJobChangeOptions_base { ++ char *id; ++ JobType type; ++}; ++ ++struct BlockJobChangeOptions { ++ char *id; ++ JobType type; ++ union { /* union tag is @type */ ++ BlockJobChangeOptionsMirror mirror; ++ } u; ++}; ++ ++void qapi_free_BlockJobChangeOptions(BlockJobChangeOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobChangeOptions, qapi_free_BlockJobChangeOptions) ++ ++struct BlockdevCacheOptions { ++ bool has_direct; ++ bool direct; ++ bool has_no_flush; ++ bool no_flush; ++}; ++ ++void qapi_free_BlockdevCacheOptions(BlockdevCacheOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCacheOptions, qapi_free_BlockdevCacheOptions) ++ ++struct BlockdevOptionsFile { ++ char *filename; ++ char *pr_manager; ++ bool has_locking; ++ OnOffAuto locking; ++ bool has_aio; ++ BlockdevAioOptions aio; ++ bool has_aio_max_batch; ++ int64_t aio_max_batch; ++#if defined(CONFIG_LINUX) ++ bool has_drop_cache; ++ bool drop_cache; ++#endif /* defined(CONFIG_LINUX) */ ++ bool has_x_check_cache_dropped; ++ bool x_check_cache_dropped; ++}; ++ ++void qapi_free_BlockdevOptionsFile(BlockdevOptionsFile *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsFile, qapi_free_BlockdevOptionsFile) ++ ++struct BlockdevOptionsNull { ++ bool has_size; ++ int64_t size; ++ bool has_latency_ns; ++ uint64_t latency_ns; ++ bool has_read_zeroes; ++ bool read_zeroes; ++}; ++ ++void qapi_free_BlockdevOptionsNull(BlockdevOptionsNull *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsNull, qapi_free_BlockdevOptionsNull) ++ ++struct BlockdevOptionsNVMe { ++ char *device; ++ int64_t q_namespace; ++}; ++ ++void qapi_free_BlockdevOptionsNVMe(BlockdevOptionsNVMe *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsNVMe, qapi_free_BlockdevOptionsNVMe) ++ ++struct BlockdevOptionsVVFAT { ++ char *dir; ++ bool has_fat_type; ++ int64_t fat_type; ++ bool has_floppy; ++ bool floppy; ++ char *label; ++ bool has_rw; ++ bool rw; ++}; ++ ++void qapi_free_BlockdevOptionsVVFAT(BlockdevOptionsVVFAT *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsVVFAT, qapi_free_BlockdevOptionsVVFAT) ++ ++struct BlockdevOptionsGenericFormat { ++ BlockdevRef *file; ++}; ++ ++void qapi_free_BlockdevOptionsGenericFormat(BlockdevOptionsGenericFormat *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsGenericFormat, qapi_free_BlockdevOptionsGenericFormat) ++ ++struct BlockdevOptionsLUKS { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ char *key_secret; ++ BlockdevRef *header; ++}; ++ ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsLUKS_base(const BlockdevOptionsLUKS *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsLUKS(BlockdevOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsLUKS, qapi_free_BlockdevOptionsLUKS) ++ ++struct BlockdevOptionsGenericCOWFormat { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ BlockdevRefOrNull *backing; ++}; ++ ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsGenericCOWFormat_base(const BlockdevOptionsGenericCOWFormat *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsGenericCOWFormat(BlockdevOptionsGenericCOWFormat *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsGenericCOWFormat, qapi_free_BlockdevOptionsGenericCOWFormat) ++ ++struct Qcow2OverlapCheckFlags { ++ bool has_q_template; ++ Qcow2OverlapCheckMode q_template; ++ bool has_main_header; ++ bool main_header; ++ bool has_active_l1; ++ bool active_l1; ++ bool has_active_l2; ++ bool active_l2; ++ bool has_refcount_table; ++ bool refcount_table; ++ bool has_refcount_block; ++ bool refcount_block; ++ bool has_snapshot_table; ++ bool snapshot_table; ++ bool has_inactive_l1; ++ bool inactive_l1; ++ bool has_inactive_l2; ++ bool inactive_l2; ++ bool has_bitmap_directory; ++ bool bitmap_directory; ++}; ++ ++void qapi_free_Qcow2OverlapCheckFlags(Qcow2OverlapCheckFlags *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(Qcow2OverlapCheckFlags, qapi_free_Qcow2OverlapCheckFlags) ++ ++struct Qcow2OverlapChecks { ++ QType type; ++ union { /* union tag is @type */ ++ Qcow2OverlapCheckFlags flags; ++ Qcow2OverlapCheckMode mode; ++ } u; ++}; ++ ++void qapi_free_Qcow2OverlapChecks(Qcow2OverlapChecks *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(Qcow2OverlapChecks, qapi_free_Qcow2OverlapChecks) ++ ++struct q_obj_BlockdevQcowEncryption_base { ++ BlockdevQcowEncryptionFormat format; ++}; ++ ++struct BlockdevQcowEncryption { ++ BlockdevQcowEncryptionFormat format; ++ union { /* union tag is @format */ ++ QCryptoBlockOptionsQCow aes; ++ } u; ++}; ++ ++void qapi_free_BlockdevQcowEncryption(BlockdevQcowEncryption *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevQcowEncryption, qapi_free_BlockdevQcowEncryption) ++ ++struct BlockdevOptionsQcow { ++ /* Members inherited from BlockdevOptionsGenericCOWFormat: */ ++ BlockdevRef *file; ++ BlockdevRefOrNull *backing; ++ /* Own members: */ ++ BlockdevQcowEncryption *encrypt; ++}; ++ ++static inline BlockdevOptionsGenericCOWFormat *qapi_BlockdevOptionsQcow_base(const BlockdevOptionsQcow *obj) ++{ ++ return (BlockdevOptionsGenericCOWFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsQcow(BlockdevOptionsQcow *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsQcow, qapi_free_BlockdevOptionsQcow) ++ ++struct q_obj_BlockdevQcow2Encryption_base { ++ BlockdevQcow2EncryptionFormat format; ++}; ++ ++struct BlockdevQcow2Encryption { ++ BlockdevQcow2EncryptionFormat format; ++ union { /* union tag is @format */ ++ QCryptoBlockOptionsQCow aes; ++ QCryptoBlockOptionsLUKS luks; ++ } u; ++}; ++ ++void qapi_free_BlockdevQcow2Encryption(BlockdevQcow2Encryption *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevQcow2Encryption, qapi_free_BlockdevQcow2Encryption) ++ ++struct BlockdevOptionsPreallocate { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ bool has_prealloc_align; ++ int64_t prealloc_align; ++ bool has_prealloc_size; ++ int64_t prealloc_size; ++}; ++ ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsPreallocate_base(const BlockdevOptionsPreallocate *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsPreallocate(BlockdevOptionsPreallocate *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsPreallocate, qapi_free_BlockdevOptionsPreallocate) ++ ++struct BlockdevOptionsQcow2 { ++ /* Members inherited from BlockdevOptionsGenericCOWFormat: */ ++ BlockdevRef *file; ++ BlockdevRefOrNull *backing; ++ /* Own members: */ ++ bool has_lazy_refcounts; ++ bool lazy_refcounts; ++ bool has_pass_discard_request; ++ bool pass_discard_request; ++ bool has_pass_discard_snapshot; ++ bool pass_discard_snapshot; ++ bool has_pass_discard_other; ++ bool pass_discard_other; ++ bool has_discard_no_unref; ++ bool discard_no_unref; ++ Qcow2OverlapChecks *overlap_check; ++ bool has_cache_size; ++ int64_t cache_size; ++ bool has_l2_cache_size; ++ int64_t l2_cache_size; ++ bool has_l2_cache_entry_size; ++ int64_t l2_cache_entry_size; ++ bool has_refcount_cache_size; ++ int64_t refcount_cache_size; ++ bool has_cache_clean_interval; ++ int64_t cache_clean_interval; ++ BlockdevQcow2Encryption *encrypt; ++ BlockdevRef *data_file; ++}; ++ ++static inline BlockdevOptionsGenericCOWFormat *qapi_BlockdevOptionsQcow2_base(const BlockdevOptionsQcow2 *obj) ++{ ++ return (BlockdevOptionsGenericCOWFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsQcow2(BlockdevOptionsQcow2 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsQcow2, qapi_free_BlockdevOptionsQcow2) ++ ++struct SshHostKeyHash { ++ SshHostKeyCheckHashType type; ++ char *hash; ++}; ++ ++void qapi_free_SshHostKeyHash(SshHostKeyHash *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SshHostKeyHash, qapi_free_SshHostKeyHash) ++ ++struct q_obj_SshHostKeyCheck_base { ++ SshHostKeyCheckMode mode; ++}; ++ ++struct SshHostKeyCheck { ++ SshHostKeyCheckMode mode; ++ union { /* union tag is @mode */ ++ SshHostKeyHash hash; ++ } u; ++}; ++ ++void qapi_free_SshHostKeyCheck(SshHostKeyCheck *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SshHostKeyCheck, qapi_free_SshHostKeyCheck) ++ ++struct BlockdevOptionsSsh { ++ InetSocketAddress *server; ++ char *path; ++ char *user; ++ SshHostKeyCheck *host_key_check; ++}; ++ ++void qapi_free_BlockdevOptionsSsh(BlockdevOptionsSsh *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsSsh, qapi_free_BlockdevOptionsSsh) ++ ++struct BlkdebugInjectErrorOptions { ++ BlkdebugEvent event; ++ bool has_state; ++ int64_t state; ++ bool has_iotype; ++ BlkdebugIOType iotype; ++ bool has_q_errno; ++ int64_t q_errno; ++ bool has_sector; ++ int64_t sector; ++ bool has_once; ++ bool once; ++ bool has_immediately; ++ bool immediately; ++}; ++ ++void qapi_free_BlkdebugInjectErrorOptions(BlkdebugInjectErrorOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlkdebugInjectErrorOptions, qapi_free_BlkdebugInjectErrorOptions) ++ ++struct BlkdebugSetStateOptions { ++ BlkdebugEvent event; ++ bool has_state; ++ int64_t state; ++ int64_t new_state; ++}; ++ ++void qapi_free_BlkdebugSetStateOptions(BlkdebugSetStateOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlkdebugSetStateOptions, qapi_free_BlkdebugSetStateOptions) ++ ++struct BlkdebugInjectErrorOptionsList { ++ BlkdebugInjectErrorOptionsList *next; ++ BlkdebugInjectErrorOptions *value; ++}; ++ ++void qapi_free_BlkdebugInjectErrorOptionsList(BlkdebugInjectErrorOptionsList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlkdebugInjectErrorOptionsList, qapi_free_BlkdebugInjectErrorOptionsList) ++ ++struct BlkdebugSetStateOptionsList { ++ BlkdebugSetStateOptionsList *next; ++ BlkdebugSetStateOptions *value; ++}; ++ ++void qapi_free_BlkdebugSetStateOptionsList(BlkdebugSetStateOptionsList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlkdebugSetStateOptionsList, qapi_free_BlkdebugSetStateOptionsList) ++ ++struct BlockdevOptionsBlkdebug { ++ BlockdevRef *image; ++ char *config; ++ bool has_align; ++ int64_t align; ++ bool has_max_transfer; ++ int32_t max_transfer; ++ bool has_opt_write_zero; ++ int32_t opt_write_zero; ++ bool has_max_write_zero; ++ int32_t max_write_zero; ++ bool has_opt_discard; ++ int32_t opt_discard; ++ bool has_max_discard; ++ int32_t max_discard; ++ bool has_inject_error; ++ BlkdebugInjectErrorOptionsList *inject_error; ++ bool has_set_state; ++ BlkdebugSetStateOptionsList *set_state; ++ bool has_take_child_perms; ++ BlockPermissionList *take_child_perms; ++ bool has_unshare_child_perms; ++ BlockPermissionList *unshare_child_perms; ++}; ++ ++void qapi_free_BlockdevOptionsBlkdebug(BlockdevOptionsBlkdebug *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsBlkdebug, qapi_free_BlockdevOptionsBlkdebug) ++ ++struct BlockdevOptionsBlklogwrites { ++ BlockdevRef *file; ++ BlockdevRef *log; ++ bool has_log_sector_size; ++ uint32_t log_sector_size; ++ bool has_log_append; ++ bool log_append; ++ bool has_log_super_update_interval; ++ uint64_t log_super_update_interval; ++}; ++ ++void qapi_free_BlockdevOptionsBlklogwrites(BlockdevOptionsBlklogwrites *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsBlklogwrites, qapi_free_BlockdevOptionsBlklogwrites) ++ ++struct BlockdevOptionsBlkverify { ++ BlockdevRef *test; ++ BlockdevRef *raw; ++}; ++ ++void qapi_free_BlockdevOptionsBlkverify(BlockdevOptionsBlkverify *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsBlkverify, qapi_free_BlockdevOptionsBlkverify) ++ ++struct BlockdevOptionsBlkreplay { ++ BlockdevRef *image; ++}; ++ ++void qapi_free_BlockdevOptionsBlkreplay(BlockdevOptionsBlkreplay *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsBlkreplay, qapi_free_BlockdevOptionsBlkreplay) ++ ++struct BlockdevRefList { ++ BlockdevRefList *next; ++ BlockdevRef *value; ++}; ++ ++void qapi_free_BlockdevRefList(BlockdevRefList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevRefList, qapi_free_BlockdevRefList) ++ ++struct BlockdevOptionsQuorum { ++ bool has_blkverify; ++ bool blkverify; ++ BlockdevRefList *children; ++ int64_t vote_threshold; ++ bool has_rewrite_corrupted; ++ bool rewrite_corrupted; ++ bool has_read_pattern; ++ QuorumReadPattern read_pattern; ++}; ++ ++void qapi_free_BlockdevOptionsQuorum(BlockdevOptionsQuorum *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsQuorum, qapi_free_BlockdevOptionsQuorum) ++ ++struct BlockdevOptionsGluster { ++ char *volume; ++ char *path; ++ SocketAddressList *server; ++ bool has_debug; ++ int64_t debug; ++ char *logfile; ++}; ++ ++void qapi_free_BlockdevOptionsGluster(BlockdevOptionsGluster *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsGluster, qapi_free_BlockdevOptionsGluster) ++ ++#if defined(CONFIG_BLKIO) ++struct BlockdevOptionsIoUring { ++ char *filename; ++}; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsIoUring(BlockdevOptionsIoUring *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsIoUring, qapi_free_BlockdevOptionsIoUring) ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++struct BlockdevOptionsNvmeIoUring { ++ char *path; ++}; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsNvmeIoUring(BlockdevOptionsNvmeIoUring *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsNvmeIoUring, qapi_free_BlockdevOptionsNvmeIoUring) ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++struct BlockdevOptionsVirtioBlkVfioPci { ++ char *path; ++}; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsVirtioBlkVfioPci(BlockdevOptionsVirtioBlkVfioPci *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsVirtioBlkVfioPci, qapi_free_BlockdevOptionsVirtioBlkVfioPci) ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++struct BlockdevOptionsVirtioBlkVhostUser { ++ char *path; ++}; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsVirtioBlkVhostUser(BlockdevOptionsVirtioBlkVhostUser *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsVirtioBlkVhostUser, qapi_free_BlockdevOptionsVirtioBlkVhostUser) ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++struct BlockdevOptionsVirtioBlkVhostVdpa { ++ char *path; ++}; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsVirtioBlkVhostVdpa(BlockdevOptionsVirtioBlkVhostVdpa *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsVirtioBlkVhostVdpa, qapi_free_BlockdevOptionsVirtioBlkVhostVdpa) ++#endif /* defined(CONFIG_BLKIO) */ ++ ++struct BlockdevOptionsIscsi { ++ IscsiTransport transport; ++ char *portal; ++ char *target; ++ bool has_lun; ++ int64_t lun; ++ char *user; ++ char *password_secret; ++ char *initiator_name; ++ bool has_header_digest; ++ IscsiHeaderDigest header_digest; ++ bool has_timeout; ++ int64_t timeout; ++}; ++ ++void qapi_free_BlockdevOptionsIscsi(BlockdevOptionsIscsi *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsIscsi, qapi_free_BlockdevOptionsIscsi) ++ ++struct RbdEncryptionOptionsLUKSBase { ++ char *key_secret; ++}; ++ ++void qapi_free_RbdEncryptionOptionsLUKSBase(RbdEncryptionOptionsLUKSBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionOptionsLUKSBase, qapi_free_RbdEncryptionOptionsLUKSBase) ++ ++struct RbdEncryptionCreateOptionsLUKSBase { ++ /* Members inherited from RbdEncryptionOptionsLUKSBase: */ ++ char *key_secret; ++ /* Own members: */ ++ bool has_cipher_alg; ++ QCryptoCipherAlgorithm cipher_alg; ++}; ++ ++static inline RbdEncryptionOptionsLUKSBase *qapi_RbdEncryptionCreateOptionsLUKSBase_base(const RbdEncryptionCreateOptionsLUKSBase *obj) ++{ ++ return (RbdEncryptionOptionsLUKSBase *)obj; ++} ++ ++void qapi_free_RbdEncryptionCreateOptionsLUKSBase(RbdEncryptionCreateOptionsLUKSBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionCreateOptionsLUKSBase, qapi_free_RbdEncryptionCreateOptionsLUKSBase) ++ ++struct RbdEncryptionOptionsLUKS { ++ /* Members inherited from RbdEncryptionOptionsLUKSBase: */ ++ char *key_secret; ++ /* Own members: */ ++}; ++ ++static inline RbdEncryptionOptionsLUKSBase *qapi_RbdEncryptionOptionsLUKS_base(const RbdEncryptionOptionsLUKS *obj) ++{ ++ return (RbdEncryptionOptionsLUKSBase *)obj; ++} ++ ++void qapi_free_RbdEncryptionOptionsLUKS(RbdEncryptionOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionOptionsLUKS, qapi_free_RbdEncryptionOptionsLUKS) ++ ++struct RbdEncryptionOptionsLUKS2 { ++ /* Members inherited from RbdEncryptionOptionsLUKSBase: */ ++ char *key_secret; ++ /* Own members: */ ++}; ++ ++static inline RbdEncryptionOptionsLUKSBase *qapi_RbdEncryptionOptionsLUKS2_base(const RbdEncryptionOptionsLUKS2 *obj) ++{ ++ return (RbdEncryptionOptionsLUKSBase *)obj; ++} ++ ++void qapi_free_RbdEncryptionOptionsLUKS2(RbdEncryptionOptionsLUKS2 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionOptionsLUKS2, qapi_free_RbdEncryptionOptionsLUKS2) ++ ++struct RbdEncryptionOptionsLUKSAny { ++ /* Members inherited from RbdEncryptionOptionsLUKSBase: */ ++ char *key_secret; ++ /* Own members: */ ++}; ++ ++static inline RbdEncryptionOptionsLUKSBase *qapi_RbdEncryptionOptionsLUKSAny_base(const RbdEncryptionOptionsLUKSAny *obj) ++{ ++ return (RbdEncryptionOptionsLUKSBase *)obj; ++} ++ ++void qapi_free_RbdEncryptionOptionsLUKSAny(RbdEncryptionOptionsLUKSAny *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionOptionsLUKSAny, qapi_free_RbdEncryptionOptionsLUKSAny) ++ ++struct RbdEncryptionCreateOptionsLUKS { ++ /* Members inherited from RbdEncryptionCreateOptionsLUKSBase: */ ++ char *key_secret; ++ bool has_cipher_alg; ++ QCryptoCipherAlgorithm cipher_alg; ++ /* Own members: */ ++}; ++ ++static inline RbdEncryptionCreateOptionsLUKSBase *qapi_RbdEncryptionCreateOptionsLUKS_base(const RbdEncryptionCreateOptionsLUKS *obj) ++{ ++ return (RbdEncryptionCreateOptionsLUKSBase *)obj; ++} ++ ++void qapi_free_RbdEncryptionCreateOptionsLUKS(RbdEncryptionCreateOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionCreateOptionsLUKS, qapi_free_RbdEncryptionCreateOptionsLUKS) ++ ++struct RbdEncryptionCreateOptionsLUKS2 { ++ /* Members inherited from RbdEncryptionCreateOptionsLUKSBase: */ ++ char *key_secret; ++ bool has_cipher_alg; ++ QCryptoCipherAlgorithm cipher_alg; ++ /* Own members: */ ++}; ++ ++static inline RbdEncryptionCreateOptionsLUKSBase *qapi_RbdEncryptionCreateOptionsLUKS2_base(const RbdEncryptionCreateOptionsLUKS2 *obj) ++{ ++ return (RbdEncryptionCreateOptionsLUKSBase *)obj; ++} ++ ++void qapi_free_RbdEncryptionCreateOptionsLUKS2(RbdEncryptionCreateOptionsLUKS2 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionCreateOptionsLUKS2, qapi_free_RbdEncryptionCreateOptionsLUKS2) ++ ++struct q_obj_RbdEncryptionOptions_base { ++ RbdImageEncryptionFormat format; ++ RbdEncryptionOptions *parent; ++}; ++ ++struct RbdEncryptionOptions { ++ RbdImageEncryptionFormat format; ++ RbdEncryptionOptions *parent; ++ union { /* union tag is @format */ ++ RbdEncryptionOptionsLUKS luks; ++ RbdEncryptionOptionsLUKS2 luks2; ++ RbdEncryptionOptionsLUKSAny luks_any; ++ } u; ++}; ++ ++void qapi_free_RbdEncryptionOptions(RbdEncryptionOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionOptions, qapi_free_RbdEncryptionOptions) ++ ++struct q_obj_RbdEncryptionCreateOptions_base { ++ RbdImageEncryptionFormat format; ++}; ++ ++struct RbdEncryptionCreateOptions { ++ RbdImageEncryptionFormat format; ++ union { /* union tag is @format */ ++ RbdEncryptionCreateOptionsLUKS luks; ++ RbdEncryptionCreateOptionsLUKS2 luks2; ++ } u; ++}; ++ ++void qapi_free_RbdEncryptionCreateOptions(RbdEncryptionCreateOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionCreateOptions, qapi_free_RbdEncryptionCreateOptions) ++ ++struct RbdAuthModeList { ++ RbdAuthModeList *next; ++ RbdAuthMode value; ++}; ++ ++void qapi_free_RbdAuthModeList(RbdAuthModeList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdAuthModeList, qapi_free_RbdAuthModeList) ++ ++struct BlockdevOptionsRbd { ++ char *pool; ++ char *q_namespace; ++ char *image; ++ char *conf; ++ char *snapshot; ++ RbdEncryptionOptions *encrypt; ++ char *user; ++ bool has_auth_client_required; ++ RbdAuthModeList *auth_client_required; ++ char *key_secret; ++ bool has_server; ++ InetSocketAddressBaseList *server; ++}; ++ ++void qapi_free_BlockdevOptionsRbd(BlockdevOptionsRbd *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsRbd, qapi_free_BlockdevOptionsRbd) ++ ++#if defined(CONFIG_REPLICATION) ++struct BlockdevOptionsReplication { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ ReplicationMode mode; ++ char *top_id; ++}; ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++#if defined(CONFIG_REPLICATION) ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsReplication_base(const BlockdevOptionsReplication *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsReplication(BlockdevOptionsReplication *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsReplication, qapi_free_BlockdevOptionsReplication) ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++struct NFSServer { ++ NFSTransport type; ++ char *host; ++}; ++ ++void qapi_free_NFSServer(NFSServer *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(NFSServer, qapi_free_NFSServer) ++ ++struct BlockdevOptionsNfs { ++ NFSServer *server; ++ char *path; ++ bool has_user; ++ int64_t user; ++ bool has_group; ++ int64_t group; ++ bool has_tcp_syn_count; ++ int64_t tcp_syn_count; ++ bool has_readahead_size; ++ int64_t readahead_size; ++ bool has_page_cache_size; ++ int64_t page_cache_size; ++ bool has_debug; ++ int64_t debug; ++}; ++ ++void qapi_free_BlockdevOptionsNfs(BlockdevOptionsNfs *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsNfs, qapi_free_BlockdevOptionsNfs) ++ ++struct BlockdevOptionsCurlBase { ++ char *url; ++ bool has_readahead; ++ int64_t readahead; ++ bool has_timeout; ++ int64_t timeout; ++ char *username; ++ char *password_secret; ++ char *proxy_username; ++ char *proxy_password_secret; ++}; ++ ++void qapi_free_BlockdevOptionsCurlBase(BlockdevOptionsCurlBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCurlBase, qapi_free_BlockdevOptionsCurlBase) ++ ++struct BlockdevOptionsCurlHttp { ++ /* Members inherited from BlockdevOptionsCurlBase: */ ++ char *url; ++ bool has_readahead; ++ int64_t readahead; ++ bool has_timeout; ++ int64_t timeout; ++ char *username; ++ char *password_secret; ++ char *proxy_username; ++ char *proxy_password_secret; ++ /* Own members: */ ++ char *cookie; ++ char *cookie_secret; ++}; ++ ++static inline BlockdevOptionsCurlBase *qapi_BlockdevOptionsCurlHttp_base(const BlockdevOptionsCurlHttp *obj) ++{ ++ return (BlockdevOptionsCurlBase *)obj; ++} ++ ++void qapi_free_BlockdevOptionsCurlHttp(BlockdevOptionsCurlHttp *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCurlHttp, qapi_free_BlockdevOptionsCurlHttp) ++ ++struct BlockdevOptionsCurlHttps { ++ /* Members inherited from BlockdevOptionsCurlBase: */ ++ char *url; ++ bool has_readahead; ++ int64_t readahead; ++ bool has_timeout; ++ int64_t timeout; ++ char *username; ++ char *password_secret; ++ char *proxy_username; ++ char *proxy_password_secret; ++ /* Own members: */ ++ char *cookie; ++ bool has_sslverify; ++ bool sslverify; ++ char *cookie_secret; ++}; ++ ++static inline BlockdevOptionsCurlBase *qapi_BlockdevOptionsCurlHttps_base(const BlockdevOptionsCurlHttps *obj) ++{ ++ return (BlockdevOptionsCurlBase *)obj; ++} ++ ++void qapi_free_BlockdevOptionsCurlHttps(BlockdevOptionsCurlHttps *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCurlHttps, qapi_free_BlockdevOptionsCurlHttps) ++ ++struct BlockdevOptionsCurlFtp { ++ /* Members inherited from BlockdevOptionsCurlBase: */ ++ char *url; ++ bool has_readahead; ++ int64_t readahead; ++ bool has_timeout; ++ int64_t timeout; ++ char *username; ++ char *password_secret; ++ char *proxy_username; ++ char *proxy_password_secret; ++ /* Own members: */ ++}; ++ ++static inline BlockdevOptionsCurlBase *qapi_BlockdevOptionsCurlFtp_base(const BlockdevOptionsCurlFtp *obj) ++{ ++ return (BlockdevOptionsCurlBase *)obj; ++} ++ ++void qapi_free_BlockdevOptionsCurlFtp(BlockdevOptionsCurlFtp *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCurlFtp, qapi_free_BlockdevOptionsCurlFtp) ++ ++struct BlockdevOptionsCurlFtps { ++ /* Members inherited from BlockdevOptionsCurlBase: */ ++ char *url; ++ bool has_readahead; ++ int64_t readahead; ++ bool has_timeout; ++ int64_t timeout; ++ char *username; ++ char *password_secret; ++ char *proxy_username; ++ char *proxy_password_secret; ++ /* Own members: */ ++ bool has_sslverify; ++ bool sslverify; ++}; ++ ++static inline BlockdevOptionsCurlBase *qapi_BlockdevOptionsCurlFtps_base(const BlockdevOptionsCurlFtps *obj) ++{ ++ return (BlockdevOptionsCurlBase *)obj; ++} ++ ++void qapi_free_BlockdevOptionsCurlFtps(BlockdevOptionsCurlFtps *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCurlFtps, qapi_free_BlockdevOptionsCurlFtps) ++ ++struct BlockdevOptionsNbd { ++ SocketAddress *server; ++ char *export; ++ char *tls_creds; ++ char *tls_hostname; ++ char *x_dirty_bitmap; ++ bool has_reconnect_delay; ++ uint32_t reconnect_delay; ++ bool has_open_timeout; ++ uint32_t open_timeout; ++}; ++ ++void qapi_free_BlockdevOptionsNbd(BlockdevOptionsNbd *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsNbd, qapi_free_BlockdevOptionsNbd) ++ ++struct BlockdevOptionsRaw { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ bool has_offset; ++ int64_t offset; ++ bool has_size; ++ int64_t size; ++}; ++ ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsRaw_base(const BlockdevOptionsRaw *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsRaw(BlockdevOptionsRaw *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsRaw, qapi_free_BlockdevOptionsRaw) ++ ++struct BlockdevOptionsThrottle { ++ char *throttle_group; ++ BlockdevRef *file; ++}; ++ ++void qapi_free_BlockdevOptionsThrottle(BlockdevOptionsThrottle *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsThrottle, qapi_free_BlockdevOptionsThrottle) ++ ++struct BlockdevOptionsCor { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ char *bottom; ++}; ++ ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsCor_base(const BlockdevOptionsCor *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsCor(BlockdevOptionsCor *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCor, qapi_free_BlockdevOptionsCor) ++ ++struct BlockdevOptionsCbw { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ BlockdevRef *target; ++ BlockDirtyBitmap *bitmap; ++ bool has_on_cbw_error; ++ OnCbwError on_cbw_error; ++ bool has_cbw_timeout; ++ uint32_t cbw_timeout; ++}; ++ ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsCbw_base(const BlockdevOptionsCbw *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsCbw(BlockdevOptionsCbw *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCbw, qapi_free_BlockdevOptionsCbw) ++ ++struct q_obj_BlockdevOptions_base { ++ BlockdevDriver driver; ++ char *node_name; ++ bool has_discard; ++ BlockdevDiscardOptions discard; ++ BlockdevCacheOptions *cache; ++ bool has_read_only; ++ bool read_only; ++ bool has_auto_read_only; ++ bool auto_read_only; ++ bool has_force_share; ++ bool force_share; ++ bool has_detect_zeroes; ++ BlockdevDetectZeroesOptions detect_zeroes; ++}; ++ ++struct BlockdevOptions { ++ BlockdevDriver driver; ++ char *node_name; ++ bool has_discard; ++ BlockdevDiscardOptions discard; ++ BlockdevCacheOptions *cache; ++ bool has_read_only; ++ bool read_only; ++ bool has_auto_read_only; ++ bool auto_read_only; ++ bool has_force_share; ++ bool force_share; ++ bool has_detect_zeroes; ++ BlockdevDetectZeroesOptions detect_zeroes; ++ union { /* union tag is @driver */ ++ BlockdevOptionsBlkdebug blkdebug; ++ BlockdevOptionsBlklogwrites blklogwrites; ++ BlockdevOptionsBlkverify blkverify; ++ BlockdevOptionsBlkreplay blkreplay; ++ BlockdevOptionsGenericFormat bochs; ++ BlockdevOptionsGenericFormat cloop; ++ BlockdevOptionsGenericFormat compress; ++ BlockdevOptionsCbw copy_before_write; ++ BlockdevOptionsCor copy_on_read; ++ BlockdevOptionsGenericFormat dmg; ++ BlockdevOptionsFile file; ++ BlockdevOptionsCurlFtp ftp; ++ BlockdevOptionsCurlFtps ftps; ++ BlockdevOptionsGluster gluster; ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ BlockdevOptionsFile host_cdrom; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ BlockdevOptionsFile host_device; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ BlockdevOptionsCurlHttp http; ++ BlockdevOptionsCurlHttps https; ++#if defined(CONFIG_BLKIO) ++ BlockdevOptionsIoUring io_uring; ++#endif /* defined(CONFIG_BLKIO) */ ++ BlockdevOptionsIscsi iscsi; ++ BlockdevOptionsLUKS luks; ++ BlockdevOptionsNbd nbd; ++ BlockdevOptionsNfs nfs; ++ BlockdevOptionsNull null_aio; ++ BlockdevOptionsNull null_co; ++ BlockdevOptionsNVMe nvme; ++#if defined(CONFIG_BLKIO) ++ BlockdevOptionsNvmeIoUring nvme_io_uring; ++#endif /* defined(CONFIG_BLKIO) */ ++ BlockdevOptionsGenericFormat parallels; ++ BlockdevOptionsPreallocate preallocate; ++ BlockdevOptionsQcow2 qcow2; ++ BlockdevOptionsQcow qcow; ++ BlockdevOptionsGenericCOWFormat qed; ++ BlockdevOptionsQuorum quorum; ++ BlockdevOptionsRaw raw; ++ BlockdevOptionsRbd rbd; ++#if defined(CONFIG_REPLICATION) ++ BlockdevOptionsReplication replication; ++#endif /* defined(CONFIG_REPLICATION) */ ++ BlockdevOptionsGenericFormat snapshot_access; ++ BlockdevOptionsSsh ssh; ++ BlockdevOptionsThrottle throttle; ++ BlockdevOptionsGenericFormat vdi; ++ BlockdevOptionsGenericFormat vhdx; ++#if defined(CONFIG_BLKIO) ++ BlockdevOptionsVirtioBlkVfioPci virtio_blk_vfio_pci; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ BlockdevOptionsVirtioBlkVhostUser virtio_blk_vhost_user; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ BlockdevOptionsVirtioBlkVhostVdpa virtio_blk_vhost_vdpa; ++#endif /* defined(CONFIG_BLKIO) */ ++ BlockdevOptionsGenericCOWFormat vmdk; ++ BlockdevOptionsGenericFormat vpc; ++ BlockdevOptionsVVFAT vvfat; ++ } u; ++}; ++ ++void qapi_free_BlockdevOptions(BlockdevOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptions, qapi_free_BlockdevOptions) ++ ++struct BlockdevRef { ++ QType type; ++ union { /* union tag is @type */ ++ BlockdevOptions definition; ++ char *reference; ++ } u; ++}; ++ ++void qapi_free_BlockdevRef(BlockdevRef *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevRef, qapi_free_BlockdevRef) ++ ++struct BlockdevRefOrNull { ++ QType type; ++ union { /* union tag is @type */ ++ BlockdevOptions definition; ++ char *reference; ++ QNull *null; ++ } u; ++}; ++ ++void qapi_free_BlockdevRefOrNull(BlockdevRefOrNull *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevRefOrNull, qapi_free_BlockdevRefOrNull) ++ ++struct BlockdevOptionsList { ++ BlockdevOptionsList *next; ++ BlockdevOptions *value; ++}; ++ ++void qapi_free_BlockdevOptionsList(BlockdevOptionsList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsList, qapi_free_BlockdevOptionsList) ++ ++struct q_obj_blockdev_reopen_arg { ++ BlockdevOptionsList *options; ++}; ++ ++struct q_obj_blockdev_del_arg { ++ char *node_name; ++}; ++ ++struct BlockdevCreateOptionsFile { ++ char *filename; ++ uint64_t size; ++ bool has_preallocation; ++ PreallocMode preallocation; ++ bool has_nocow; ++ bool nocow; ++ bool has_extent_size_hint; ++ uint64_t extent_size_hint; ++}; ++ ++void qapi_free_BlockdevCreateOptionsFile(BlockdevCreateOptionsFile *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsFile, qapi_free_BlockdevCreateOptionsFile) ++ ++struct BlockdevCreateOptionsGluster { ++ BlockdevOptionsGluster *location; ++ uint64_t size; ++ bool has_preallocation; ++ PreallocMode preallocation; ++}; ++ ++void qapi_free_BlockdevCreateOptionsGluster(BlockdevCreateOptionsGluster *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsGluster, qapi_free_BlockdevCreateOptionsGluster) ++ ++struct BlockdevCreateOptionsLUKS { ++ /* Members inherited from QCryptoBlockCreateOptionsLUKS: */ ++ char *key_secret; ++ bool has_cipher_alg; ++ QCryptoCipherAlgorithm cipher_alg; ++ bool has_cipher_mode; ++ QCryptoCipherMode cipher_mode; ++ bool has_ivgen_alg; ++ QCryptoIVGenAlgorithm ivgen_alg; ++ bool has_ivgen_hash_alg; ++ QCryptoHashAlgorithm ivgen_hash_alg; ++ bool has_hash_alg; ++ QCryptoHashAlgorithm hash_alg; ++ bool has_iter_time; ++ int64_t iter_time; ++ /* Own members: */ ++ BlockdevRef *file; ++ BlockdevRef *header; ++ uint64_t size; ++ bool has_preallocation; ++ PreallocMode preallocation; ++}; ++ ++static inline QCryptoBlockCreateOptionsLUKS *qapi_BlockdevCreateOptionsLUKS_base(const BlockdevCreateOptionsLUKS *obj) ++{ ++ return (QCryptoBlockCreateOptionsLUKS *)obj; ++} ++ ++void qapi_free_BlockdevCreateOptionsLUKS(BlockdevCreateOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsLUKS, qapi_free_BlockdevCreateOptionsLUKS) ++ ++struct BlockdevCreateOptionsNfs { ++ BlockdevOptionsNfs *location; ++ uint64_t size; ++}; ++ ++void qapi_free_BlockdevCreateOptionsNfs(BlockdevCreateOptionsNfs *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsNfs, qapi_free_BlockdevCreateOptionsNfs) ++ ++struct BlockdevCreateOptionsParallels { ++ BlockdevRef *file; ++ uint64_t size; ++ bool has_cluster_size; ++ uint64_t cluster_size; ++}; ++ ++void qapi_free_BlockdevCreateOptionsParallels(BlockdevCreateOptionsParallels *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsParallels, qapi_free_BlockdevCreateOptionsParallels) ++ ++struct BlockdevCreateOptionsQcow { ++ BlockdevRef *file; ++ uint64_t size; ++ char *backing_file; ++ QCryptoBlockCreateOptions *encrypt; ++}; ++ ++void qapi_free_BlockdevCreateOptionsQcow(BlockdevCreateOptionsQcow *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsQcow, qapi_free_BlockdevCreateOptionsQcow) ++ ++struct BlockdevCreateOptionsQcow2 { ++ BlockdevRef *file; ++ BlockdevRef *data_file; ++ bool has_data_file_raw; ++ bool data_file_raw; ++ bool has_extended_l2; ++ bool extended_l2; ++ uint64_t size; ++ bool has_version; ++ BlockdevQcow2Version version; ++ char *backing_file; ++ bool has_backing_fmt; ++ BlockdevDriver backing_fmt; ++ QCryptoBlockCreateOptions *encrypt; ++ bool has_cluster_size; ++ uint64_t cluster_size; ++ bool has_preallocation; ++ PreallocMode preallocation; ++ bool has_lazy_refcounts; ++ bool lazy_refcounts; ++ bool has_refcount_bits; ++ int64_t refcount_bits; ++ bool has_compression_type; ++ Qcow2CompressionType compression_type; ++}; ++ ++void qapi_free_BlockdevCreateOptionsQcow2(BlockdevCreateOptionsQcow2 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsQcow2, qapi_free_BlockdevCreateOptionsQcow2) ++ ++struct BlockdevCreateOptionsQed { ++ BlockdevRef *file; ++ uint64_t size; ++ char *backing_file; ++ bool has_backing_fmt; ++ BlockdevDriver backing_fmt; ++ bool has_cluster_size; ++ uint64_t cluster_size; ++ bool has_table_size; ++ int64_t table_size; ++}; ++ ++void qapi_free_BlockdevCreateOptionsQed(BlockdevCreateOptionsQed *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsQed, qapi_free_BlockdevCreateOptionsQed) ++ ++struct BlockdevCreateOptionsRbd { ++ BlockdevOptionsRbd *location; ++ uint64_t size; ++ bool has_cluster_size; ++ uint64_t cluster_size; ++ RbdEncryptionCreateOptions *encrypt; ++}; ++ ++void qapi_free_BlockdevCreateOptionsRbd(BlockdevCreateOptionsRbd *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsRbd, qapi_free_BlockdevCreateOptionsRbd) ++ ++struct BlockdevCreateOptionsVmdk { ++ BlockdevRef *file; ++ uint64_t size; ++ bool has_extents; ++ BlockdevRefList *extents; ++ bool has_subformat; ++ BlockdevVmdkSubformat subformat; ++ char *backing_file; ++ bool has_adapter_type; ++ BlockdevVmdkAdapterType adapter_type; ++ char *hwversion; ++ char *toolsversion; ++ bool has_zeroed_grain; ++ bool zeroed_grain; ++}; ++ ++void qapi_free_BlockdevCreateOptionsVmdk(BlockdevCreateOptionsVmdk *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsVmdk, qapi_free_BlockdevCreateOptionsVmdk) ++ ++struct BlockdevCreateOptionsSsh { ++ BlockdevOptionsSsh *location; ++ uint64_t size; ++}; ++ ++void qapi_free_BlockdevCreateOptionsSsh(BlockdevCreateOptionsSsh *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsSsh, qapi_free_BlockdevCreateOptionsSsh) ++ ++struct BlockdevCreateOptionsVdi { ++ BlockdevRef *file; ++ uint64_t size; ++ bool has_preallocation; ++ PreallocMode preallocation; ++}; ++ ++void qapi_free_BlockdevCreateOptionsVdi(BlockdevCreateOptionsVdi *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsVdi, qapi_free_BlockdevCreateOptionsVdi) ++ ++struct BlockdevCreateOptionsVhdx { ++ BlockdevRef *file; ++ uint64_t size; ++ bool has_log_size; ++ uint64_t log_size; ++ bool has_block_size; ++ uint64_t block_size; ++ bool has_subformat; ++ BlockdevVhdxSubformat subformat; ++ bool has_block_state_zero; ++ bool block_state_zero; ++}; ++ ++void qapi_free_BlockdevCreateOptionsVhdx(BlockdevCreateOptionsVhdx *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsVhdx, qapi_free_BlockdevCreateOptionsVhdx) ++ ++struct BlockdevCreateOptionsVpc { ++ BlockdevRef *file; ++ uint64_t size; ++ bool has_subformat; ++ BlockdevVpcSubformat subformat; ++ bool has_force_size; ++ bool force_size; ++}; ++ ++void qapi_free_BlockdevCreateOptionsVpc(BlockdevCreateOptionsVpc *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsVpc, qapi_free_BlockdevCreateOptionsVpc) ++ ++struct q_obj_BlockdevCreateOptions_base { ++ BlockdevDriver driver; ++}; ++ ++struct BlockdevCreateOptions { ++ BlockdevDriver driver; ++ union { /* union tag is @driver */ ++ BlockdevCreateOptionsFile file; ++ BlockdevCreateOptionsGluster gluster; ++ BlockdevCreateOptionsLUKS luks; ++ BlockdevCreateOptionsNfs nfs; ++ BlockdevCreateOptionsParallels parallels; ++ BlockdevCreateOptionsQcow qcow; ++ BlockdevCreateOptionsQcow2 qcow2; ++ BlockdevCreateOptionsQed qed; ++ BlockdevCreateOptionsRbd rbd; ++ BlockdevCreateOptionsSsh ssh; ++ BlockdevCreateOptionsVdi vdi; ++ BlockdevCreateOptionsVhdx vhdx; ++ BlockdevCreateOptionsVmdk vmdk; ++ BlockdevCreateOptionsVpc vpc; ++ } u; ++}; ++ ++void qapi_free_BlockdevCreateOptions(BlockdevCreateOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptions, qapi_free_BlockdevCreateOptions) ++ ++struct q_obj_blockdev_create_arg { ++ char *job_id; ++ BlockdevCreateOptions *options; ++}; ++ ++struct BlockdevAmendOptionsLUKS { ++ /* Members inherited from QCryptoBlockAmendOptionsLUKS: */ ++ QCryptoBlockLUKSKeyslotState state; ++ char *new_secret; ++ char *old_secret; ++ bool has_keyslot; ++ int64_t keyslot; ++ bool has_iter_time; ++ int64_t iter_time; ++ char *secret; ++ /* Own members: */ ++}; ++ ++static inline QCryptoBlockAmendOptionsLUKS *qapi_BlockdevAmendOptionsLUKS_base(const BlockdevAmendOptionsLUKS *obj) ++{ ++ return (QCryptoBlockAmendOptionsLUKS *)obj; ++} ++ ++void qapi_free_BlockdevAmendOptionsLUKS(BlockdevAmendOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevAmendOptionsLUKS, qapi_free_BlockdevAmendOptionsLUKS) ++ ++struct BlockdevAmendOptionsQcow2 { ++ QCryptoBlockAmendOptions *encrypt; ++}; ++ ++void qapi_free_BlockdevAmendOptionsQcow2(BlockdevAmendOptionsQcow2 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevAmendOptionsQcow2, qapi_free_BlockdevAmendOptionsQcow2) ++ ++struct q_obj_BlockdevAmendOptions_base { ++ BlockdevDriver driver; ++}; ++ ++struct BlockdevAmendOptions { ++ BlockdevDriver driver; ++ union { /* union tag is @driver */ ++ BlockdevAmendOptionsLUKS luks; ++ BlockdevAmendOptionsQcow2 qcow2; ++ } u; ++}; ++ ++void qapi_free_BlockdevAmendOptions(BlockdevAmendOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevAmendOptions, qapi_free_BlockdevAmendOptions) ++ ++struct q_obj_x_blockdev_amend_arg { ++ char *job_id; ++ char *node_name; ++ BlockdevAmendOptions *options; ++ bool has_force; ++ bool force; ++}; ++ ++struct q_obj_BLOCK_IMAGE_CORRUPTED_arg { ++ char *device; ++ char *node_name; ++ char *msg; ++ bool has_offset; ++ int64_t offset; ++ bool has_size; ++ int64_t size; ++ bool fatal; ++}; ++ ++struct q_obj_BLOCK_IO_ERROR_arg { ++ char *device; ++ char *node_name; ++ IoOperationType operation; ++ BlockErrorAction action; ++ bool has_nospace; ++ bool nospace; ++ char *reason; ++}; ++ ++struct q_obj_BLOCK_JOB_COMPLETED_arg { ++ JobType type; ++ char *device; ++ int64_t len; ++ int64_t offset; ++ int64_t speed; ++ char *error; ++}; ++ ++struct q_obj_BLOCK_JOB_CANCELLED_arg { ++ JobType type; ++ char *device; ++ int64_t len; ++ int64_t offset; ++ int64_t speed; ++}; ++ ++struct q_obj_BLOCK_JOB_ERROR_arg { ++ char *device; ++ IoOperationType operation; ++ BlockErrorAction action; ++}; ++ ++struct q_obj_BLOCK_JOB_READY_arg { ++ JobType type; ++ char *device; ++ int64_t len; ++ int64_t offset; ++ int64_t speed; ++}; ++ ++struct q_obj_BLOCK_JOB_PENDING_arg { ++ JobType type; ++ char *id; ++}; ++ ++struct q_obj_BLOCK_WRITE_THRESHOLD_arg { ++ char *node_name; ++ uint64_t amount_exceeded; ++ uint64_t write_threshold; ++}; ++ ++struct q_obj_block_set_write_threshold_arg { ++ char *node_name; ++ uint64_t write_threshold; ++}; ++ ++struct q_obj_x_blockdev_change_arg { ++ char *parent; ++ char *child; ++ char *node; ++}; ++ ++struct q_obj_x_blockdev_set_iothread_arg { ++ char *node_name; ++ StrOrNull *iothread; ++ bool has_force; ++ bool force; ++}; ++ ++struct q_obj_QUORUM_FAILURE_arg { ++ char *reference; ++ int64_t sector_num; ++ int64_t sectors_count; ++}; ++ ++struct q_obj_QUORUM_REPORT_BAD_arg { ++ QuorumOpType type; ++ char *error; ++ char *node_name; ++ int64_t sector_num; ++ int64_t sectors_count; ++}; ++ ++struct BlockdevSnapshotInternal { ++ char *device; ++ char *name; ++}; ++ ++void qapi_free_BlockdevSnapshotInternal(BlockdevSnapshotInternal *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevSnapshotInternal, qapi_free_BlockdevSnapshotInternal) ++ ++struct q_obj_blockdev_snapshot_delete_internal_sync_arg { ++ char *device; ++ char *id; ++ char *name; ++}; ++ ++struct BlockGraphInfoList { ++ BlockGraphInfoList *next; ++ BlockGraphInfo *value; ++}; ++ ++void qapi_free_BlockGraphInfoList(BlockGraphInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockGraphInfoList, qapi_free_BlockGraphInfoList) ++ ++struct DummyBlockCoreForceArrays { ++ BlockGraphInfoList *unused_block_graph_info; ++}; ++ ++void qapi_free_DummyBlockCoreForceArrays(DummyBlockCoreForceArrays *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(DummyBlockCoreForceArrays, qapi_free_DummyBlockCoreForceArrays) ++ ++#endif /* QAPI_TYPES_BLOCK_CORE_H */ +diff --git a/include/qapi/qapi-types-block-export.h b/include/qapi/qapi-types-block-export.h +new file mode 100644 +index 00000000..62e568e8 +--- /dev/null ++++ b/include/qapi/qapi-types-block-export.h +@@ -0,0 +1,274 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_BLOCK_EXPORT_H ++#define QAPI_TYPES_BLOCK_EXPORT_H ++ ++#include "qapi/qapi-builtin-types.h" ++#include "qapi-types-sockets.h" ++#include "qapi-types-block-core.h" ++ ++typedef struct NbdServerOptions NbdServerOptions; ++ ++typedef struct q_obj_nbd_server_start_arg q_obj_nbd_server_start_arg; ++ ++typedef struct BlockExportOptionsNbdBase BlockExportOptionsNbdBase; ++ ++typedef struct BlockExportOptionsNbd BlockExportOptionsNbd; ++ ++typedef struct BlockExportOptionsVhostUserBlk BlockExportOptionsVhostUserBlk; ++ ++typedef enum FuseExportAllowOther { ++ FUSE_EXPORT_ALLOW_OTHER_OFF, ++ FUSE_EXPORT_ALLOW_OTHER_ON, ++ FUSE_EXPORT_ALLOW_OTHER_AUTO, ++ FUSE_EXPORT_ALLOW_OTHER__MAX, ++} FuseExportAllowOther; ++ ++#define FuseExportAllowOther_str(val) \ ++ qapi_enum_lookup(&FuseExportAllowOther_lookup, (val)) ++ ++extern const QEnumLookup FuseExportAllowOther_lookup; ++ ++#if defined(CONFIG_FUSE) ++typedef struct BlockExportOptionsFuse BlockExportOptionsFuse; ++#endif /* defined(CONFIG_FUSE) */ ++ ++typedef struct BlockExportOptionsVduseBlk BlockExportOptionsVduseBlk; ++ ++typedef struct NbdServerAddOptions NbdServerAddOptions; ++ ++typedef enum BlockExportRemoveMode { ++ BLOCK_EXPORT_REMOVE_MODE_SAFE, ++ BLOCK_EXPORT_REMOVE_MODE_HARD, ++ BLOCK_EXPORT_REMOVE_MODE__MAX, ++} BlockExportRemoveMode; ++ ++#define BlockExportRemoveMode_str(val) \ ++ qapi_enum_lookup(&BlockExportRemoveMode_lookup, (val)) ++ ++extern const QEnumLookup BlockExportRemoveMode_lookup; ++ ++typedef struct q_obj_nbd_server_remove_arg q_obj_nbd_server_remove_arg; ++ ++typedef enum BlockExportType { ++ BLOCK_EXPORT_TYPE_NBD, ++#if defined(CONFIG_VHOST_USER_BLK_SERVER) ++ BLOCK_EXPORT_TYPE_VHOST_USER_BLK, ++#endif /* defined(CONFIG_VHOST_USER_BLK_SERVER) */ ++#if defined(CONFIG_FUSE) ++ BLOCK_EXPORT_TYPE_FUSE, ++#endif /* defined(CONFIG_FUSE) */ ++#if defined(CONFIG_VDUSE_BLK_EXPORT) ++ BLOCK_EXPORT_TYPE_VDUSE_BLK, ++#endif /* defined(CONFIG_VDUSE_BLK_EXPORT) */ ++ BLOCK_EXPORT_TYPE__MAX, ++} BlockExportType; ++ ++#define BlockExportType_str(val) \ ++ qapi_enum_lookup(&BlockExportType_lookup, (val)) ++ ++extern const QEnumLookup BlockExportType_lookup; ++ ++typedef struct q_obj_BlockExportOptions_base q_obj_BlockExportOptions_base; ++ ++typedef struct BlockExportOptions BlockExportOptions; ++ ++typedef struct q_obj_block_export_del_arg q_obj_block_export_del_arg; ++ ++typedef struct q_obj_BLOCK_EXPORT_DELETED_arg q_obj_BLOCK_EXPORT_DELETED_arg; ++ ++typedef struct BlockExportInfo BlockExportInfo; ++ ++typedef struct BlockExportInfoList BlockExportInfoList; ++ ++struct NbdServerOptions { ++ SocketAddress *addr; ++ char *tls_creds; ++ char *tls_authz; ++ bool has_max_connections; ++ uint32_t max_connections; ++}; ++ ++void qapi_free_NbdServerOptions(NbdServerOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(NbdServerOptions, qapi_free_NbdServerOptions) ++ ++struct q_obj_nbd_server_start_arg { ++ SocketAddressLegacy *addr; ++ char *tls_creds; ++ char *tls_authz; ++ bool has_max_connections; ++ uint32_t max_connections; ++}; ++ ++struct BlockExportOptionsNbdBase { ++ char *name; ++ char *description; ++}; ++ ++void qapi_free_BlockExportOptionsNbdBase(BlockExportOptionsNbdBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportOptionsNbdBase, qapi_free_BlockExportOptionsNbdBase) ++ ++struct BlockExportOptionsNbd { ++ /* Members inherited from BlockExportOptionsNbdBase: */ ++ char *name; ++ char *description; ++ /* Own members: */ ++ bool has_bitmaps; ++ BlockDirtyBitmapOrStrList *bitmaps; ++ bool has_allocation_depth; ++ bool allocation_depth; ++}; ++ ++static inline BlockExportOptionsNbdBase *qapi_BlockExportOptionsNbd_base(const BlockExportOptionsNbd *obj) ++{ ++ return (BlockExportOptionsNbdBase *)obj; ++} ++ ++void qapi_free_BlockExportOptionsNbd(BlockExportOptionsNbd *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportOptionsNbd, qapi_free_BlockExportOptionsNbd) ++ ++struct BlockExportOptionsVhostUserBlk { ++ SocketAddress *addr; ++ bool has_logical_block_size; ++ uint64_t logical_block_size; ++ bool has_num_queues; ++ uint16_t num_queues; ++}; ++ ++void qapi_free_BlockExportOptionsVhostUserBlk(BlockExportOptionsVhostUserBlk *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportOptionsVhostUserBlk, qapi_free_BlockExportOptionsVhostUserBlk) ++ ++#if defined(CONFIG_FUSE) ++struct BlockExportOptionsFuse { ++ char *mountpoint; ++ bool has_growable; ++ bool growable; ++ bool has_allow_other; ++ FuseExportAllowOther allow_other; ++}; ++#endif /* defined(CONFIG_FUSE) */ ++ ++#if defined(CONFIG_FUSE) ++void qapi_free_BlockExportOptionsFuse(BlockExportOptionsFuse *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportOptionsFuse, qapi_free_BlockExportOptionsFuse) ++#endif /* defined(CONFIG_FUSE) */ ++ ++struct BlockExportOptionsVduseBlk { ++ char *name; ++ bool has_num_queues; ++ uint16_t num_queues; ++ bool has_queue_size; ++ uint16_t queue_size; ++ bool has_logical_block_size; ++ uint64_t logical_block_size; ++ char *serial; ++}; ++ ++void qapi_free_BlockExportOptionsVduseBlk(BlockExportOptionsVduseBlk *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportOptionsVduseBlk, qapi_free_BlockExportOptionsVduseBlk) ++ ++struct NbdServerAddOptions { ++ /* Members inherited from BlockExportOptionsNbdBase: */ ++ char *name; ++ char *description; ++ /* Own members: */ ++ char *device; ++ bool has_writable; ++ bool writable; ++ char *bitmap; ++}; ++ ++static inline BlockExportOptionsNbdBase *qapi_NbdServerAddOptions_base(const NbdServerAddOptions *obj) ++{ ++ return (BlockExportOptionsNbdBase *)obj; ++} ++ ++void qapi_free_NbdServerAddOptions(NbdServerAddOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(NbdServerAddOptions, qapi_free_NbdServerAddOptions) ++ ++struct q_obj_nbd_server_remove_arg { ++ char *name; ++ bool has_mode; ++ BlockExportRemoveMode mode; ++}; ++ ++struct q_obj_BlockExportOptions_base { ++ BlockExportType type; ++ char *id; ++ bool has_fixed_iothread; ++ bool fixed_iothread; ++ char *iothread; ++ char *node_name; ++ bool has_writable; ++ bool writable; ++ bool has_writethrough; ++ bool writethrough; ++}; ++ ++struct BlockExportOptions { ++ BlockExportType type; ++ char *id; ++ bool has_fixed_iothread; ++ bool fixed_iothread; ++ char *iothread; ++ char *node_name; ++ bool has_writable; ++ bool writable; ++ bool has_writethrough; ++ bool writethrough; ++ union { /* union tag is @type */ ++ BlockExportOptionsNbd nbd; ++#if defined(CONFIG_VHOST_USER_BLK_SERVER) ++ BlockExportOptionsVhostUserBlk vhost_user_blk; ++#endif /* defined(CONFIG_VHOST_USER_BLK_SERVER) */ ++#if defined(CONFIG_FUSE) ++ BlockExportOptionsFuse fuse; ++#endif /* defined(CONFIG_FUSE) */ ++#if defined(CONFIG_VDUSE_BLK_EXPORT) ++ BlockExportOptionsVduseBlk vduse_blk; ++#endif /* defined(CONFIG_VDUSE_BLK_EXPORT) */ ++ } u; ++}; ++ ++void qapi_free_BlockExportOptions(BlockExportOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportOptions, qapi_free_BlockExportOptions) ++ ++struct q_obj_block_export_del_arg { ++ char *id; ++ bool has_mode; ++ BlockExportRemoveMode mode; ++}; ++ ++struct q_obj_BLOCK_EXPORT_DELETED_arg { ++ char *id; ++}; ++ ++struct BlockExportInfo { ++ char *id; ++ BlockExportType type; ++ char *node_name; ++ bool shutting_down; ++}; ++ ++void qapi_free_BlockExportInfo(BlockExportInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportInfo, qapi_free_BlockExportInfo) ++ ++struct BlockExportInfoList { ++ BlockExportInfoList *next; ++ BlockExportInfo *value; ++}; ++ ++void qapi_free_BlockExportInfoList(BlockExportInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportInfoList, qapi_free_BlockExportInfoList) ++ ++#endif /* QAPI_TYPES_BLOCK_EXPORT_H */ +diff --git a/include/qapi/qapi-types-block.h b/include/qapi/qapi-types-block.h +new file mode 100644 +index 00000000..73e33b4b +--- /dev/null ++++ b/include/qapi/qapi-types-block.h +@@ -0,0 +1,161 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_BLOCK_H ++#define QAPI_TYPES_BLOCK_H ++ ++#include "qapi/qapi-builtin-types.h" ++#include "qapi-types-block-core.h" ++ ++typedef enum BiosAtaTranslation { ++ BIOS_ATA_TRANSLATION_AUTO, ++ BIOS_ATA_TRANSLATION_NONE, ++ BIOS_ATA_TRANSLATION_LBA, ++ BIOS_ATA_TRANSLATION_LARGE, ++ BIOS_ATA_TRANSLATION_RECHS, ++ BIOS_ATA_TRANSLATION__MAX, ++} BiosAtaTranslation; ++ ++#define BiosAtaTranslation_str(val) \ ++ qapi_enum_lookup(&BiosAtaTranslation_lookup, (val)) ++ ++extern const QEnumLookup BiosAtaTranslation_lookup; ++ ++typedef enum FloppyDriveType { ++ FLOPPY_DRIVE_TYPE_144, ++ FLOPPY_DRIVE_TYPE_288, ++ FLOPPY_DRIVE_TYPE_120, ++ FLOPPY_DRIVE_TYPE_NONE, ++ FLOPPY_DRIVE_TYPE_AUTO, ++ FLOPPY_DRIVE_TYPE__MAX, ++} FloppyDriveType; ++ ++#define FloppyDriveType_str(val) \ ++ qapi_enum_lookup(&FloppyDriveType_lookup, (val)) ++ ++extern const QEnumLookup FloppyDriveType_lookup; ++ ++typedef struct PRManagerInfo PRManagerInfo; ++ ++typedef struct PRManagerInfoList PRManagerInfoList; ++ ++typedef struct q_obj_eject_arg q_obj_eject_arg; ++ ++typedef struct q_obj_blockdev_open_tray_arg q_obj_blockdev_open_tray_arg; ++ ++typedef struct q_obj_blockdev_close_tray_arg q_obj_blockdev_close_tray_arg; ++ ++typedef struct q_obj_blockdev_remove_medium_arg q_obj_blockdev_remove_medium_arg; ++ ++typedef struct q_obj_blockdev_insert_medium_arg q_obj_blockdev_insert_medium_arg; ++ ++typedef enum BlockdevChangeReadOnlyMode { ++ BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN, ++ BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY, ++ BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE, ++ BLOCKDEV_CHANGE_READ_ONLY_MODE__MAX, ++} BlockdevChangeReadOnlyMode; ++ ++#define BlockdevChangeReadOnlyMode_str(val) \ ++ qapi_enum_lookup(&BlockdevChangeReadOnlyMode_lookup, (val)) ++ ++extern const QEnumLookup BlockdevChangeReadOnlyMode_lookup; ++ ++typedef struct q_obj_blockdev_change_medium_arg q_obj_blockdev_change_medium_arg; ++ ++typedef struct q_obj_DEVICE_TRAY_MOVED_arg q_obj_DEVICE_TRAY_MOVED_arg; ++ ++typedef struct q_obj_PR_MANAGER_STATUS_CHANGED_arg q_obj_PR_MANAGER_STATUS_CHANGED_arg; ++ ++typedef struct q_obj_block_latency_histogram_set_arg q_obj_block_latency_histogram_set_arg; ++ ++struct PRManagerInfo { ++ char *id; ++ bool connected; ++}; ++ ++void qapi_free_PRManagerInfo(PRManagerInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(PRManagerInfo, qapi_free_PRManagerInfo) ++ ++struct PRManagerInfoList { ++ PRManagerInfoList *next; ++ PRManagerInfo *value; ++}; ++ ++void qapi_free_PRManagerInfoList(PRManagerInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(PRManagerInfoList, qapi_free_PRManagerInfoList) ++ ++struct q_obj_eject_arg { ++ char *device; ++ char *id; ++ bool has_force; ++ bool force; ++}; ++ ++struct q_obj_blockdev_open_tray_arg { ++ char *device; ++ char *id; ++ bool has_force; ++ bool force; ++}; ++ ++struct q_obj_blockdev_close_tray_arg { ++ char *device; ++ char *id; ++}; ++ ++struct q_obj_blockdev_remove_medium_arg { ++ char *id; ++}; ++ ++struct q_obj_blockdev_insert_medium_arg { ++ char *id; ++ char *node_name; ++}; ++ ++struct q_obj_blockdev_change_medium_arg { ++ char *device; ++ char *id; ++ char *filename; ++ char *format; ++ bool has_force; ++ bool force; ++ bool has_read_only_mode; ++ BlockdevChangeReadOnlyMode read_only_mode; ++}; ++ ++struct q_obj_DEVICE_TRAY_MOVED_arg { ++ char *device; ++ char *id; ++ bool tray_open; ++}; ++ ++struct q_obj_PR_MANAGER_STATUS_CHANGED_arg { ++ char *id; ++ bool connected; ++}; ++ ++struct q_obj_block_latency_histogram_set_arg { ++ char *id; ++ bool has_boundaries; ++ uint64List *boundaries; ++ bool has_boundaries_read; ++ uint64List *boundaries_read; ++ bool has_boundaries_write; ++ uint64List *boundaries_write; ++ bool has_boundaries_zap; ++ uint64List *boundaries_zap; ++ bool has_boundaries_flush; ++ uint64List *boundaries_flush; ++}; ++ ++#endif /* QAPI_TYPES_BLOCK_H */ +diff --git a/include/qapi/qapi-types-common.h b/include/qapi/qapi-types-common.h +new file mode 100644 +index 00000000..1ce2d1c0 +--- /dev/null ++++ b/include/qapi/qapi-types-common.h +@@ -0,0 +1,163 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_COMMON_H ++#define QAPI_TYPES_COMMON_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum IoOperationType { ++ IO_OPERATION_TYPE_READ, ++ IO_OPERATION_TYPE_WRITE, ++ IO_OPERATION_TYPE__MAX, ++} IoOperationType; ++ ++#define IoOperationType_str(val) \ ++ qapi_enum_lookup(&IoOperationType_lookup, (val)) ++ ++extern const QEnumLookup IoOperationType_lookup; ++ ++typedef enum OnOffAuto { ++ ON_OFF_AUTO_AUTO, ++ ON_OFF_AUTO_ON, ++ ON_OFF_AUTO_OFF, ++ ON_OFF_AUTO__MAX, ++} OnOffAuto; ++ ++#define OnOffAuto_str(val) \ ++ qapi_enum_lookup(&OnOffAuto_lookup, (val)) ++ ++extern const QEnumLookup OnOffAuto_lookup; ++ ++typedef enum OnOffSplit { ++ ON_OFF_SPLIT_ON, ++ ON_OFF_SPLIT_OFF, ++ ON_OFF_SPLIT_SPLIT, ++ ON_OFF_SPLIT__MAX, ++} OnOffSplit; ++ ++#define OnOffSplit_str(val) \ ++ qapi_enum_lookup(&OnOffSplit_lookup, (val)) ++ ++extern const QEnumLookup OnOffSplit_lookup; ++ ++typedef struct StrOrNull StrOrNull; ++ ++typedef enum OffAutoPCIBAR { ++ OFF_AUTOPCIBAR_OFF, ++ OFF_AUTOPCIBAR_AUTO, ++ OFF_AUTOPCIBAR_BAR0, ++ OFF_AUTOPCIBAR_BAR1, ++ OFF_AUTOPCIBAR_BAR2, ++ OFF_AUTOPCIBAR_BAR3, ++ OFF_AUTOPCIBAR_BAR4, ++ OFF_AUTOPCIBAR_BAR5, ++ OFF_AUTOPCIBAR__MAX, ++} OffAutoPCIBAR; ++ ++#define OffAutoPCIBAR_str(val) \ ++ qapi_enum_lookup(&OffAutoPCIBAR_lookup, (val)) ++ ++extern const QEnumLookup OffAutoPCIBAR_lookup; ++ ++typedef enum PCIELinkSpeed { ++ PCIE_LINK_SPEED_2_5, ++ PCIE_LINK_SPEED_5, ++ PCIE_LINK_SPEED_8, ++ PCIE_LINK_SPEED_16, ++ PCIE_LINK_SPEED_32, ++ PCIE_LINK_SPEED_64, ++ PCIE_LINK_SPEED__MAX, ++} PCIELinkSpeed; ++ ++#define PCIELinkSpeed_str(val) \ ++ qapi_enum_lookup(&PCIELinkSpeed_lookup, (val)) ++ ++extern const QEnumLookup PCIELinkSpeed_lookup; ++ ++typedef enum PCIELinkWidth { ++ PCIE_LINK_WIDTH_1, ++ PCIE_LINK_WIDTH_2, ++ PCIE_LINK_WIDTH_4, ++ PCIE_LINK_WIDTH_8, ++ PCIE_LINK_WIDTH_12, ++ PCIE_LINK_WIDTH_16, ++ PCIE_LINK_WIDTH_32, ++ PCIE_LINK_WIDTH__MAX, ++} PCIELinkWidth; ++ ++#define PCIELinkWidth_str(val) \ ++ qapi_enum_lookup(&PCIELinkWidth_lookup, (val)) ++ ++extern const QEnumLookup PCIELinkWidth_lookup; ++ ++typedef enum HostMemPolicy { ++ HOST_MEM_POLICY_DEFAULT, ++ HOST_MEM_POLICY_PREFERRED, ++ HOST_MEM_POLICY_BIND, ++ HOST_MEM_POLICY_INTERLEAVE, ++ HOST_MEM_POLICY__MAX, ++} HostMemPolicy; ++ ++#define HostMemPolicy_str(val) \ ++ qapi_enum_lookup(&HostMemPolicy_lookup, (val)) ++ ++extern const QEnumLookup HostMemPolicy_lookup; ++ ++typedef enum NetFilterDirection { ++ NET_FILTER_DIRECTION_ALL, ++ NET_FILTER_DIRECTION_RX, ++ NET_FILTER_DIRECTION_TX, ++ NET_FILTER_DIRECTION__MAX, ++} NetFilterDirection; ++ ++#define NetFilterDirection_str(val) \ ++ qapi_enum_lookup(&NetFilterDirection_lookup, (val)) ++ ++extern const QEnumLookup NetFilterDirection_lookup; ++ ++typedef enum GrabToggleKeys { ++ GRAB_TOGGLE_KEYS_CTRL_CTRL, ++ GRAB_TOGGLE_KEYS_ALT_ALT, ++ GRAB_TOGGLE_KEYS_SHIFT_SHIFT, ++ GRAB_TOGGLE_KEYS_META_META, ++ GRAB_TOGGLE_KEYS_SCROLLLOCK, ++ GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK, ++ GRAB_TOGGLE_KEYS__MAX, ++} GrabToggleKeys; ++ ++#define GrabToggleKeys_str(val) \ ++ qapi_enum_lookup(&GrabToggleKeys_lookup, (val)) ++ ++extern const QEnumLookup GrabToggleKeys_lookup; ++ ++typedef struct HumanReadableText HumanReadableText; ++ ++struct StrOrNull { ++ QType type; ++ union { /* union tag is @type */ ++ char *s; ++ QNull *n; ++ } u; ++}; ++ ++void qapi_free_StrOrNull(StrOrNull *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(StrOrNull, qapi_free_StrOrNull) ++ ++struct HumanReadableText { ++ char *human_readable_text; ++}; ++ ++void qapi_free_HumanReadableText(HumanReadableText *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(HumanReadableText, qapi_free_HumanReadableText) ++ ++#endif /* QAPI_TYPES_COMMON_H */ +diff --git a/include/qapi/qapi-types-compat.h b/include/qapi/qapi-types-compat.h +new file mode 100644 +index 00000000..0b4c36ee +--- /dev/null ++++ b/include/qapi/qapi-types-compat.h +@@ -0,0 +1,57 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_COMPAT_H ++#define QAPI_TYPES_COMPAT_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum CompatPolicyInput { ++ COMPAT_POLICY_INPUT_ACCEPT, ++ COMPAT_POLICY_INPUT_REJECT, ++ COMPAT_POLICY_INPUT_CRASH, ++ COMPAT_POLICY_INPUT__MAX, ++} CompatPolicyInput; ++ ++#define CompatPolicyInput_str(val) \ ++ qapi_enum_lookup(&CompatPolicyInput_lookup, (val)) ++ ++extern const QEnumLookup CompatPolicyInput_lookup; ++ ++typedef enum CompatPolicyOutput { ++ COMPAT_POLICY_OUTPUT_ACCEPT, ++ COMPAT_POLICY_OUTPUT_HIDE, ++ COMPAT_POLICY_OUTPUT__MAX, ++} CompatPolicyOutput; ++ ++#define CompatPolicyOutput_str(val) \ ++ qapi_enum_lookup(&CompatPolicyOutput_lookup, (val)) ++ ++extern const QEnumLookup CompatPolicyOutput_lookup; ++ ++typedef struct CompatPolicy CompatPolicy; ++ ++struct CompatPolicy { ++ bool has_deprecated_input; ++ CompatPolicyInput deprecated_input; ++ bool has_deprecated_output; ++ CompatPolicyOutput deprecated_output; ++ bool has_unstable_input; ++ CompatPolicyInput unstable_input; ++ bool has_unstable_output; ++ CompatPolicyOutput unstable_output; ++}; ++ ++void qapi_free_CompatPolicy(CompatPolicy *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(CompatPolicy, qapi_free_CompatPolicy) ++ ++#endif /* QAPI_TYPES_COMPAT_H */ +diff --git a/include/qapi/qapi-types-crypto.h b/include/qapi/qapi-types-crypto.h +new file mode 100644 +index 00000000..14cf5d7c +--- /dev/null ++++ b/include/qapi/qapi-types-crypto.h +@@ -0,0 +1,538 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_CRYPTO_H ++#define QAPI_TYPES_CRYPTO_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum QCryptoTLSCredsEndpoint { ++ QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, ++ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, ++ QCRYPTO_TLS_CREDS_ENDPOINT__MAX, ++} QCryptoTLSCredsEndpoint; ++ ++#define QCryptoTLSCredsEndpoint_str(val) \ ++ qapi_enum_lookup(&QCryptoTLSCredsEndpoint_lookup, (val)) ++ ++extern const QEnumLookup QCryptoTLSCredsEndpoint_lookup; ++ ++typedef enum QCryptoSecretFormat { ++ QCRYPTO_SECRET_FORMAT_RAW, ++ QCRYPTO_SECRET_FORMAT_BASE64, ++ QCRYPTO_SECRET_FORMAT__MAX, ++} QCryptoSecretFormat; ++ ++#define QCryptoSecretFormat_str(val) \ ++ qapi_enum_lookup(&QCryptoSecretFormat_lookup, (val)) ++ ++extern const QEnumLookup QCryptoSecretFormat_lookup; ++ ++typedef enum QCryptoHashAlgorithm { ++ QCRYPTO_HASH_ALG_MD5, ++ QCRYPTO_HASH_ALG_SHA1, ++ QCRYPTO_HASH_ALG_SHA224, ++ QCRYPTO_HASH_ALG_SHA256, ++ QCRYPTO_HASH_ALG_SHA384, ++ QCRYPTO_HASH_ALG_SHA512, ++ QCRYPTO_HASH_ALG_RIPEMD160, ++ QCRYPTO_HASH_ALG__MAX, ++} QCryptoHashAlgorithm; ++ ++#define QCryptoHashAlgorithm_str(val) \ ++ qapi_enum_lookup(&QCryptoHashAlgorithm_lookup, (val)) ++ ++extern const QEnumLookup QCryptoHashAlgorithm_lookup; ++ ++typedef enum QCryptoCipherAlgorithm { ++ QCRYPTO_CIPHER_ALG_AES_128, ++ QCRYPTO_CIPHER_ALG_AES_192, ++ QCRYPTO_CIPHER_ALG_AES_256, ++ QCRYPTO_CIPHER_ALG_DES, ++ QCRYPTO_CIPHER_ALG_3DES, ++ QCRYPTO_CIPHER_ALG_CAST5_128, ++ QCRYPTO_CIPHER_ALG_SERPENT_128, ++ QCRYPTO_CIPHER_ALG_SERPENT_192, ++ QCRYPTO_CIPHER_ALG_SERPENT_256, ++ QCRYPTO_CIPHER_ALG_TWOFISH_128, ++ QCRYPTO_CIPHER_ALG_TWOFISH_192, ++ QCRYPTO_CIPHER_ALG_TWOFISH_256, ++ QCRYPTO_CIPHER_ALG_SM4, ++ QCRYPTO_CIPHER_ALG__MAX, ++} QCryptoCipherAlgorithm; ++ ++#define QCryptoCipherAlgorithm_str(val) \ ++ qapi_enum_lookup(&QCryptoCipherAlgorithm_lookup, (val)) ++ ++extern const QEnumLookup QCryptoCipherAlgorithm_lookup; ++ ++typedef enum QCryptoCipherMode { ++ QCRYPTO_CIPHER_MODE_ECB, ++ QCRYPTO_CIPHER_MODE_CBC, ++ QCRYPTO_CIPHER_MODE_XTS, ++ QCRYPTO_CIPHER_MODE_CTR, ++ QCRYPTO_CIPHER_MODE__MAX, ++} QCryptoCipherMode; ++ ++#define QCryptoCipherMode_str(val) \ ++ qapi_enum_lookup(&QCryptoCipherMode_lookup, (val)) ++ ++extern const QEnumLookup QCryptoCipherMode_lookup; ++ ++typedef enum QCryptoIVGenAlgorithm { ++ QCRYPTO_IVGEN_ALG_PLAIN, ++ QCRYPTO_IVGEN_ALG_PLAIN64, ++ QCRYPTO_IVGEN_ALG_ESSIV, ++ QCRYPTO_IVGEN_ALG__MAX, ++} QCryptoIVGenAlgorithm; ++ ++#define QCryptoIVGenAlgorithm_str(val) \ ++ qapi_enum_lookup(&QCryptoIVGenAlgorithm_lookup, (val)) ++ ++extern const QEnumLookup QCryptoIVGenAlgorithm_lookup; ++ ++typedef enum QCryptoBlockFormat { ++ Q_CRYPTO_BLOCK_FORMAT_QCOW, ++ Q_CRYPTO_BLOCK_FORMAT_LUKS, ++ Q_CRYPTO_BLOCK_FORMAT__MAX, ++} QCryptoBlockFormat; ++ ++#define QCryptoBlockFormat_str(val) \ ++ qapi_enum_lookup(&QCryptoBlockFormat_lookup, (val)) ++ ++extern const QEnumLookup QCryptoBlockFormat_lookup; ++ ++typedef struct QCryptoBlockOptionsBase QCryptoBlockOptionsBase; ++ ++typedef struct QCryptoBlockOptionsQCow QCryptoBlockOptionsQCow; ++ ++typedef struct QCryptoBlockOptionsLUKS QCryptoBlockOptionsLUKS; ++ ++typedef struct QCryptoBlockCreateOptionsLUKS QCryptoBlockCreateOptionsLUKS; ++ ++typedef struct QCryptoBlockOpenOptions QCryptoBlockOpenOptions; ++ ++typedef struct QCryptoBlockCreateOptions QCryptoBlockCreateOptions; ++ ++typedef struct QCryptoBlockInfoBase QCryptoBlockInfoBase; ++ ++typedef struct QCryptoBlockInfoLUKSSlot QCryptoBlockInfoLUKSSlot; ++ ++typedef struct QCryptoBlockInfoLUKSSlotList QCryptoBlockInfoLUKSSlotList; ++ ++typedef struct QCryptoBlockInfoLUKS QCryptoBlockInfoLUKS; ++ ++typedef struct QCryptoBlockInfo QCryptoBlockInfo; ++ ++typedef enum QCryptoBlockLUKSKeyslotState { ++ Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_ACTIVE, ++ Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_INACTIVE, ++ Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE__MAX, ++} QCryptoBlockLUKSKeyslotState; ++ ++#define QCryptoBlockLUKSKeyslotState_str(val) \ ++ qapi_enum_lookup(&QCryptoBlockLUKSKeyslotState_lookup, (val)) ++ ++extern const QEnumLookup QCryptoBlockLUKSKeyslotState_lookup; ++ ++typedef struct QCryptoBlockAmendOptionsLUKS QCryptoBlockAmendOptionsLUKS; ++ ++typedef struct QCryptoBlockAmendOptions QCryptoBlockAmendOptions; ++ ++typedef struct SecretCommonProperties SecretCommonProperties; ++ ++typedef struct SecretProperties SecretProperties; ++ ++#if defined(CONFIG_SECRET_KEYRING) ++typedef struct SecretKeyringProperties SecretKeyringProperties; ++#endif /* defined(CONFIG_SECRET_KEYRING) */ ++ ++typedef struct TlsCredsProperties TlsCredsProperties; ++ ++typedef struct TlsCredsAnonProperties TlsCredsAnonProperties; ++ ++typedef struct TlsCredsPskProperties TlsCredsPskProperties; ++ ++typedef struct TlsCredsX509Properties TlsCredsX509Properties; ++ ++typedef enum QCryptoAkCipherAlgorithm { ++ QCRYPTO_AKCIPHER_ALG_RSA, ++ QCRYPTO_AKCIPHER_ALG__MAX, ++} QCryptoAkCipherAlgorithm; ++ ++#define QCryptoAkCipherAlgorithm_str(val) \ ++ qapi_enum_lookup(&QCryptoAkCipherAlgorithm_lookup, (val)) ++ ++extern const QEnumLookup QCryptoAkCipherAlgorithm_lookup; ++ ++typedef enum QCryptoAkCipherKeyType { ++ QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, ++ QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, ++ QCRYPTO_AKCIPHER_KEY_TYPE__MAX, ++} QCryptoAkCipherKeyType; ++ ++#define QCryptoAkCipherKeyType_str(val) \ ++ qapi_enum_lookup(&QCryptoAkCipherKeyType_lookup, (val)) ++ ++extern const QEnumLookup QCryptoAkCipherKeyType_lookup; ++ ++typedef enum QCryptoRSAPaddingAlgorithm { ++ QCRYPTO_RSA_PADDING_ALG_RAW, ++ QCRYPTO_RSA_PADDING_ALG_PKCS1, ++ QCRYPTO_RSA_PADDING_ALG__MAX, ++} QCryptoRSAPaddingAlgorithm; ++ ++#define QCryptoRSAPaddingAlgorithm_str(val) \ ++ qapi_enum_lookup(&QCryptoRSAPaddingAlgorithm_lookup, (val)) ++ ++extern const QEnumLookup QCryptoRSAPaddingAlgorithm_lookup; ++ ++typedef struct QCryptoAkCipherOptionsRSA QCryptoAkCipherOptionsRSA; ++ ++typedef struct q_obj_QCryptoAkCipherOptions_base q_obj_QCryptoAkCipherOptions_base; ++ ++typedef struct QCryptoAkCipherOptions QCryptoAkCipherOptions; ++ ++struct QCryptoBlockOptionsBase { ++ QCryptoBlockFormat format; ++}; ++ ++void qapi_free_QCryptoBlockOptionsBase(QCryptoBlockOptionsBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockOptionsBase, qapi_free_QCryptoBlockOptionsBase) ++ ++struct QCryptoBlockOptionsQCow { ++ char *key_secret; ++}; ++ ++void qapi_free_QCryptoBlockOptionsQCow(QCryptoBlockOptionsQCow *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockOptionsQCow, qapi_free_QCryptoBlockOptionsQCow) ++ ++struct QCryptoBlockOptionsLUKS { ++ char *key_secret; ++}; ++ ++void qapi_free_QCryptoBlockOptionsLUKS(QCryptoBlockOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockOptionsLUKS, qapi_free_QCryptoBlockOptionsLUKS) ++ ++struct QCryptoBlockCreateOptionsLUKS { ++ /* Members inherited from QCryptoBlockOptionsLUKS: */ ++ char *key_secret; ++ /* Own members: */ ++ bool has_cipher_alg; ++ QCryptoCipherAlgorithm cipher_alg; ++ bool has_cipher_mode; ++ QCryptoCipherMode cipher_mode; ++ bool has_ivgen_alg; ++ QCryptoIVGenAlgorithm ivgen_alg; ++ bool has_ivgen_hash_alg; ++ QCryptoHashAlgorithm ivgen_hash_alg; ++ bool has_hash_alg; ++ QCryptoHashAlgorithm hash_alg; ++ bool has_iter_time; ++ int64_t iter_time; ++}; ++ ++static inline QCryptoBlockOptionsLUKS *qapi_QCryptoBlockCreateOptionsLUKS_base(const QCryptoBlockCreateOptionsLUKS *obj) ++{ ++ return (QCryptoBlockOptionsLUKS *)obj; ++} ++ ++void qapi_free_QCryptoBlockCreateOptionsLUKS(QCryptoBlockCreateOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockCreateOptionsLUKS, qapi_free_QCryptoBlockCreateOptionsLUKS) ++ ++struct QCryptoBlockOpenOptions { ++ /* Members inherited from QCryptoBlockOptionsBase: */ ++ QCryptoBlockFormat format; ++ /* Own members: */ ++ union { /* union tag is @format */ ++ QCryptoBlockOptionsQCow qcow; ++ QCryptoBlockOptionsLUKS luks; ++ } u; ++}; ++ ++static inline QCryptoBlockOptionsBase *qapi_QCryptoBlockOpenOptions_base(const QCryptoBlockOpenOptions *obj) ++{ ++ return (QCryptoBlockOptionsBase *)obj; ++} ++ ++void qapi_free_QCryptoBlockOpenOptions(QCryptoBlockOpenOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockOpenOptions, qapi_free_QCryptoBlockOpenOptions) ++ ++struct QCryptoBlockCreateOptions { ++ /* Members inherited from QCryptoBlockOptionsBase: */ ++ QCryptoBlockFormat format; ++ /* Own members: */ ++ union { /* union tag is @format */ ++ QCryptoBlockOptionsQCow qcow; ++ QCryptoBlockCreateOptionsLUKS luks; ++ } u; ++}; ++ ++static inline QCryptoBlockOptionsBase *qapi_QCryptoBlockCreateOptions_base(const QCryptoBlockCreateOptions *obj) ++{ ++ return (QCryptoBlockOptionsBase *)obj; ++} ++ ++void qapi_free_QCryptoBlockCreateOptions(QCryptoBlockCreateOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockCreateOptions, qapi_free_QCryptoBlockCreateOptions) ++ ++struct QCryptoBlockInfoBase { ++ QCryptoBlockFormat format; ++}; ++ ++void qapi_free_QCryptoBlockInfoBase(QCryptoBlockInfoBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockInfoBase, qapi_free_QCryptoBlockInfoBase) ++ ++struct QCryptoBlockInfoLUKSSlot { ++ bool active; ++ bool has_iters; ++ int64_t iters; ++ bool has_stripes; ++ int64_t stripes; ++ int64_t key_offset; ++}; ++ ++void qapi_free_QCryptoBlockInfoLUKSSlot(QCryptoBlockInfoLUKSSlot *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockInfoLUKSSlot, qapi_free_QCryptoBlockInfoLUKSSlot) ++ ++struct QCryptoBlockInfoLUKSSlotList { ++ QCryptoBlockInfoLUKSSlotList *next; ++ QCryptoBlockInfoLUKSSlot *value; ++}; ++ ++void qapi_free_QCryptoBlockInfoLUKSSlotList(QCryptoBlockInfoLUKSSlotList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockInfoLUKSSlotList, qapi_free_QCryptoBlockInfoLUKSSlotList) ++ ++struct QCryptoBlockInfoLUKS { ++ QCryptoCipherAlgorithm cipher_alg; ++ QCryptoCipherMode cipher_mode; ++ QCryptoIVGenAlgorithm ivgen_alg; ++ bool has_ivgen_hash_alg; ++ QCryptoHashAlgorithm ivgen_hash_alg; ++ QCryptoHashAlgorithm hash_alg; ++ bool detached_header; ++ int64_t payload_offset; ++ int64_t master_key_iters; ++ char *uuid; ++ QCryptoBlockInfoLUKSSlotList *slots; ++}; ++ ++void qapi_free_QCryptoBlockInfoLUKS(QCryptoBlockInfoLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockInfoLUKS, qapi_free_QCryptoBlockInfoLUKS) ++ ++struct QCryptoBlockInfo { ++ /* Members inherited from QCryptoBlockInfoBase: */ ++ QCryptoBlockFormat format; ++ /* Own members: */ ++ union { /* union tag is @format */ ++ QCryptoBlockInfoLUKS luks; ++ } u; ++}; ++ ++static inline QCryptoBlockInfoBase *qapi_QCryptoBlockInfo_base(const QCryptoBlockInfo *obj) ++{ ++ return (QCryptoBlockInfoBase *)obj; ++} ++ ++void qapi_free_QCryptoBlockInfo(QCryptoBlockInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockInfo, qapi_free_QCryptoBlockInfo) ++ ++struct QCryptoBlockAmendOptionsLUKS { ++ QCryptoBlockLUKSKeyslotState state; ++ char *new_secret; ++ char *old_secret; ++ bool has_keyslot; ++ int64_t keyslot; ++ bool has_iter_time; ++ int64_t iter_time; ++ char *secret; ++}; ++ ++void qapi_free_QCryptoBlockAmendOptionsLUKS(QCryptoBlockAmendOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockAmendOptionsLUKS, qapi_free_QCryptoBlockAmendOptionsLUKS) ++ ++struct QCryptoBlockAmendOptions { ++ /* Members inherited from QCryptoBlockOptionsBase: */ ++ QCryptoBlockFormat format; ++ /* Own members: */ ++ union { /* union tag is @format */ ++ QCryptoBlockAmendOptionsLUKS luks; ++ } u; ++}; ++ ++static inline QCryptoBlockOptionsBase *qapi_QCryptoBlockAmendOptions_base(const QCryptoBlockAmendOptions *obj) ++{ ++ return (QCryptoBlockOptionsBase *)obj; ++} ++ ++void qapi_free_QCryptoBlockAmendOptions(QCryptoBlockAmendOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockAmendOptions, qapi_free_QCryptoBlockAmendOptions) ++ ++struct SecretCommonProperties { ++ bool has_loaded; ++ bool loaded; ++ bool has_format; ++ QCryptoSecretFormat format; ++ char *keyid; ++ char *iv; ++}; ++ ++void qapi_free_SecretCommonProperties(SecretCommonProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SecretCommonProperties, qapi_free_SecretCommonProperties) ++ ++struct SecretProperties { ++ /* Members inherited from SecretCommonProperties: */ ++ bool has_loaded; ++ bool loaded; ++ bool has_format; ++ QCryptoSecretFormat format; ++ char *keyid; ++ char *iv; ++ /* Own members: */ ++ char *data; ++ char *file; ++}; ++ ++static inline SecretCommonProperties *qapi_SecretProperties_base(const SecretProperties *obj) ++{ ++ return (SecretCommonProperties *)obj; ++} ++ ++void qapi_free_SecretProperties(SecretProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SecretProperties, qapi_free_SecretProperties) ++ ++#if defined(CONFIG_SECRET_KEYRING) ++struct SecretKeyringProperties { ++ /* Members inherited from SecretCommonProperties: */ ++ bool has_loaded; ++ bool loaded; ++ bool has_format; ++ QCryptoSecretFormat format; ++ char *keyid; ++ char *iv; ++ /* Own members: */ ++ int32_t serial; ++}; ++#endif /* defined(CONFIG_SECRET_KEYRING) */ ++ ++#if defined(CONFIG_SECRET_KEYRING) ++static inline SecretCommonProperties *qapi_SecretKeyringProperties_base(const SecretKeyringProperties *obj) ++{ ++ return (SecretCommonProperties *)obj; ++} ++ ++void qapi_free_SecretKeyringProperties(SecretKeyringProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SecretKeyringProperties, qapi_free_SecretKeyringProperties) ++#endif /* defined(CONFIG_SECRET_KEYRING) */ ++ ++struct TlsCredsProperties { ++ bool has_verify_peer; ++ bool verify_peer; ++ char *dir; ++ bool has_endpoint; ++ QCryptoTLSCredsEndpoint endpoint; ++ char *priority; ++}; ++ ++void qapi_free_TlsCredsProperties(TlsCredsProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(TlsCredsProperties, qapi_free_TlsCredsProperties) ++ ++struct TlsCredsAnonProperties { ++ /* Members inherited from TlsCredsProperties: */ ++ bool has_verify_peer; ++ bool verify_peer; ++ char *dir; ++ bool has_endpoint; ++ QCryptoTLSCredsEndpoint endpoint; ++ char *priority; ++ /* Own members: */ ++ bool has_loaded; ++ bool loaded; ++}; ++ ++static inline TlsCredsProperties *qapi_TlsCredsAnonProperties_base(const TlsCredsAnonProperties *obj) ++{ ++ return (TlsCredsProperties *)obj; ++} ++ ++void qapi_free_TlsCredsAnonProperties(TlsCredsAnonProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(TlsCredsAnonProperties, qapi_free_TlsCredsAnonProperties) ++ ++struct TlsCredsPskProperties { ++ /* Members inherited from TlsCredsProperties: */ ++ bool has_verify_peer; ++ bool verify_peer; ++ char *dir; ++ bool has_endpoint; ++ QCryptoTLSCredsEndpoint endpoint; ++ char *priority; ++ /* Own members: */ ++ bool has_loaded; ++ bool loaded; ++ char *username; ++}; ++ ++static inline TlsCredsProperties *qapi_TlsCredsPskProperties_base(const TlsCredsPskProperties *obj) ++{ ++ return (TlsCredsProperties *)obj; ++} ++ ++void qapi_free_TlsCredsPskProperties(TlsCredsPskProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(TlsCredsPskProperties, qapi_free_TlsCredsPskProperties) ++ ++struct TlsCredsX509Properties { ++ /* Members inherited from TlsCredsProperties: */ ++ bool has_verify_peer; ++ bool verify_peer; ++ char *dir; ++ bool has_endpoint; ++ QCryptoTLSCredsEndpoint endpoint; ++ char *priority; ++ /* Own members: */ ++ bool has_loaded; ++ bool loaded; ++ bool has_sanity_check; ++ bool sanity_check; ++ char *passwordid; ++}; ++ ++static inline TlsCredsProperties *qapi_TlsCredsX509Properties_base(const TlsCredsX509Properties *obj) ++{ ++ return (TlsCredsProperties *)obj; ++} ++ ++void qapi_free_TlsCredsX509Properties(TlsCredsX509Properties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(TlsCredsX509Properties, qapi_free_TlsCredsX509Properties) ++ ++struct QCryptoAkCipherOptionsRSA { ++ QCryptoHashAlgorithm hash_alg; ++ QCryptoRSAPaddingAlgorithm padding_alg; ++}; ++ ++void qapi_free_QCryptoAkCipherOptionsRSA(QCryptoAkCipherOptionsRSA *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherOptionsRSA, qapi_free_QCryptoAkCipherOptionsRSA) ++ ++struct q_obj_QCryptoAkCipherOptions_base { ++ QCryptoAkCipherAlgorithm alg; ++}; ++ ++struct QCryptoAkCipherOptions { ++ QCryptoAkCipherAlgorithm alg; ++ union { /* union tag is @alg */ ++ QCryptoAkCipherOptionsRSA rsa; ++ } u; ++}; ++ ++void qapi_free_QCryptoAkCipherOptions(QCryptoAkCipherOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherOptions, qapi_free_QCryptoAkCipherOptions) ++ ++#endif /* QAPI_TYPES_CRYPTO_H */ +diff --git a/include/qapi/qapi-types-error.h b/include/qapi/qapi-types-error.h +new file mode 100644 +index 00000000..549dcfcc +--- /dev/null ++++ b/include/qapi/qapi-types-error.h +@@ -0,0 +1,32 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_ERROR_H ++#define QAPI_TYPES_ERROR_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum QapiErrorClass { ++ QAPI_ERROR_CLASS_GENERICERROR, ++ QAPI_ERROR_CLASS_COMMANDNOTFOUND, ++ QAPI_ERROR_CLASS_DEVICENOTACTIVE, ++ QAPI_ERROR_CLASS_DEVICENOTFOUND, ++ QAPI_ERROR_CLASS_KVMMISSINGCAP, ++ QAPI_ERROR_CLASS__MAX, ++} QapiErrorClass; ++ ++#define QapiErrorClass_str(val) \ ++ qapi_enum_lookup(&QapiErrorClass_lookup, (val)) ++ ++extern const QEnumLookup QapiErrorClass_lookup; ++ ++#endif /* QAPI_TYPES_ERROR_H */ +diff --git a/include/qapi/qapi-types-job.h b/include/qapi/qapi-types-job.h +new file mode 100644 +index 00000000..7c3d08e4 +--- /dev/null ++++ b/include/qapi/qapi-types-job.h +@@ -0,0 +1,140 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_JOB_H ++#define QAPI_TYPES_JOB_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum JobType { ++ JOB_TYPE_COMMIT, ++ JOB_TYPE_STREAM, ++ JOB_TYPE_MIRROR, ++ JOB_TYPE_BACKUP, ++ JOB_TYPE_CREATE, ++ JOB_TYPE_AMEND, ++ JOB_TYPE_SNAPSHOT_LOAD, ++ JOB_TYPE_SNAPSHOT_SAVE, ++ JOB_TYPE_SNAPSHOT_DELETE, ++ JOB_TYPE__MAX, ++} JobType; ++ ++#define JobType_str(val) \ ++ qapi_enum_lookup(&JobType_lookup, (val)) ++ ++extern const QEnumLookup JobType_lookup; ++ ++typedef enum JobStatus { ++ JOB_STATUS_UNDEFINED, ++ JOB_STATUS_CREATED, ++ JOB_STATUS_RUNNING, ++ JOB_STATUS_PAUSED, ++ JOB_STATUS_READY, ++ JOB_STATUS_STANDBY, ++ JOB_STATUS_WAITING, ++ JOB_STATUS_PENDING, ++ JOB_STATUS_ABORTING, ++ JOB_STATUS_CONCLUDED, ++ JOB_STATUS_NULL, ++ JOB_STATUS__MAX, ++} JobStatus; ++ ++#define JobStatus_str(val) \ ++ qapi_enum_lookup(&JobStatus_lookup, (val)) ++ ++extern const QEnumLookup JobStatus_lookup; ++ ++typedef enum JobVerb { ++ JOB_VERB_CANCEL, ++ JOB_VERB_PAUSE, ++ JOB_VERB_RESUME, ++ JOB_VERB_SET_SPEED, ++ JOB_VERB_COMPLETE, ++ JOB_VERB_DISMISS, ++ JOB_VERB_FINALIZE, ++ JOB_VERB_CHANGE, ++ JOB_VERB__MAX, ++} JobVerb; ++ ++#define JobVerb_str(val) \ ++ qapi_enum_lookup(&JobVerb_lookup, (val)) ++ ++extern const QEnumLookup JobVerb_lookup; ++ ++typedef struct q_obj_JOB_STATUS_CHANGE_arg q_obj_JOB_STATUS_CHANGE_arg; ++ ++typedef struct q_obj_job_pause_arg q_obj_job_pause_arg; ++ ++typedef struct q_obj_job_resume_arg q_obj_job_resume_arg; ++ ++typedef struct q_obj_job_cancel_arg q_obj_job_cancel_arg; ++ ++typedef struct q_obj_job_complete_arg q_obj_job_complete_arg; ++ ++typedef struct q_obj_job_dismiss_arg q_obj_job_dismiss_arg; ++ ++typedef struct q_obj_job_finalize_arg q_obj_job_finalize_arg; ++ ++typedef struct JobInfo JobInfo; ++ ++typedef struct JobInfoList JobInfoList; ++ ++struct q_obj_JOB_STATUS_CHANGE_arg { ++ char *id; ++ JobStatus status; ++}; ++ ++struct q_obj_job_pause_arg { ++ char *id; ++}; ++ ++struct q_obj_job_resume_arg { ++ char *id; ++}; ++ ++struct q_obj_job_cancel_arg { ++ char *id; ++}; ++ ++struct q_obj_job_complete_arg { ++ char *id; ++}; ++ ++struct q_obj_job_dismiss_arg { ++ char *id; ++}; ++ ++struct q_obj_job_finalize_arg { ++ char *id; ++}; ++ ++struct JobInfo { ++ char *id; ++ JobType type; ++ JobStatus status; ++ int64_t current_progress; ++ int64_t total_progress; ++ char *error; ++}; ++ ++void qapi_free_JobInfo(JobInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(JobInfo, qapi_free_JobInfo) ++ ++struct JobInfoList { ++ JobInfoList *next; ++ JobInfo *value; ++}; ++ ++void qapi_free_JobInfoList(JobInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(JobInfoList, qapi_free_JobInfoList) ++ ++#endif /* QAPI_TYPES_JOB_H */ +diff --git a/include/qapi/qapi-types-run-state.h b/include/qapi/qapi-types-run-state.h +new file mode 100644 +index 00000000..caaac070 +--- /dev/null ++++ b/include/qapi/qapi-types-run-state.h +@@ -0,0 +1,312 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_RUN_STATE_H ++#define QAPI_TYPES_RUN_STATE_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum RunState { ++ RUN_STATE_DEBUG, ++ RUN_STATE_INMIGRATE, ++ RUN_STATE_INTERNAL_ERROR, ++ RUN_STATE_IO_ERROR, ++ RUN_STATE_PAUSED, ++ RUN_STATE_POSTMIGRATE, ++ RUN_STATE_PRELAUNCH, ++ RUN_STATE_FINISH_MIGRATE, ++ RUN_STATE_RESTORE_VM, ++ RUN_STATE_RUNNING, ++ RUN_STATE_SAVE_VM, ++ RUN_STATE_SHUTDOWN, ++ RUN_STATE_SUSPENDED, ++ RUN_STATE_WATCHDOG, ++ RUN_STATE_GUEST_PANICKED, ++ RUN_STATE_COLO, ++ RUN_STATE__MAX, ++} RunState; ++ ++#define RunState_str(val) \ ++ qapi_enum_lookup(&RunState_lookup, (val)) ++ ++extern const QEnumLookup RunState_lookup; ++ ++typedef enum ShutdownCause { ++ SHUTDOWN_CAUSE_NONE, ++ SHUTDOWN_CAUSE_HOST_ERROR, ++ SHUTDOWN_CAUSE_HOST_QMP_QUIT, ++ SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET, ++ SHUTDOWN_CAUSE_HOST_SIGNAL, ++ SHUTDOWN_CAUSE_HOST_UI, ++ SHUTDOWN_CAUSE_GUEST_SHUTDOWN, ++ SHUTDOWN_CAUSE_GUEST_RESET, ++ SHUTDOWN_CAUSE_GUEST_PANIC, ++ SHUTDOWN_CAUSE_SUBSYSTEM_RESET, ++ SHUTDOWN_CAUSE_SNAPSHOT_LOAD, ++ SHUTDOWN_CAUSE__MAX, ++} ShutdownCause; ++ ++#define ShutdownCause_str(val) \ ++ qapi_enum_lookup(&ShutdownCause_lookup, (val)) ++ ++extern const QEnumLookup ShutdownCause_lookup; ++ ++typedef struct StatusInfo StatusInfo; ++ ++typedef struct q_obj_SHUTDOWN_arg q_obj_SHUTDOWN_arg; ++ ++typedef struct q_obj_RESET_arg q_obj_RESET_arg; ++ ++typedef struct q_obj_WATCHDOG_arg q_obj_WATCHDOG_arg; ++ ++typedef enum WatchdogAction { ++ WATCHDOG_ACTION_RESET, ++ WATCHDOG_ACTION_SHUTDOWN, ++ WATCHDOG_ACTION_POWEROFF, ++ WATCHDOG_ACTION_PAUSE, ++ WATCHDOG_ACTION_DEBUG, ++ WATCHDOG_ACTION_NONE, ++ WATCHDOG_ACTION_INJECT_NMI, ++ WATCHDOG_ACTION__MAX, ++} WatchdogAction; ++ ++#define WatchdogAction_str(val) \ ++ qapi_enum_lookup(&WatchdogAction_lookup, (val)) ++ ++extern const QEnumLookup WatchdogAction_lookup; ++ ++typedef enum RebootAction { ++ REBOOT_ACTION_RESET, ++ REBOOT_ACTION_SHUTDOWN, ++ REBOOT_ACTION__MAX, ++} RebootAction; ++ ++#define RebootAction_str(val) \ ++ qapi_enum_lookup(&RebootAction_lookup, (val)) ++ ++extern const QEnumLookup RebootAction_lookup; ++ ++typedef enum ShutdownAction { ++ SHUTDOWN_ACTION_POWEROFF, ++ SHUTDOWN_ACTION_PAUSE, ++ SHUTDOWN_ACTION__MAX, ++} ShutdownAction; ++ ++#define ShutdownAction_str(val) \ ++ qapi_enum_lookup(&ShutdownAction_lookup, (val)) ++ ++extern const QEnumLookup ShutdownAction_lookup; ++ ++typedef enum PanicAction { ++ PANIC_ACTION_PAUSE, ++ PANIC_ACTION_SHUTDOWN, ++ PANIC_ACTION_EXIT_FAILURE, ++ PANIC_ACTION_NONE, ++ PANIC_ACTION__MAX, ++} PanicAction; ++ ++#define PanicAction_str(val) \ ++ qapi_enum_lookup(&PanicAction_lookup, (val)) ++ ++extern const QEnumLookup PanicAction_lookup; ++ ++typedef struct q_obj_watchdog_set_action_arg q_obj_watchdog_set_action_arg; ++ ++typedef struct q_obj_set_action_arg q_obj_set_action_arg; ++ ++typedef struct q_obj_GUEST_PANICKED_arg q_obj_GUEST_PANICKED_arg; ++ ++typedef struct q_obj_GUEST_CRASHLOADED_arg q_obj_GUEST_CRASHLOADED_arg; ++ ++typedef enum GuestPanicAction { ++ GUEST_PANIC_ACTION_PAUSE, ++ GUEST_PANIC_ACTION_POWEROFF, ++ GUEST_PANIC_ACTION_RUN, ++ GUEST_PANIC_ACTION__MAX, ++} GuestPanicAction; ++ ++#define GuestPanicAction_str(val) \ ++ qapi_enum_lookup(&GuestPanicAction_lookup, (val)) ++ ++extern const QEnumLookup GuestPanicAction_lookup; ++ ++typedef enum GuestPanicInformationType { ++ GUEST_PANIC_INFORMATION_TYPE_HYPER_V, ++ GUEST_PANIC_INFORMATION_TYPE_S390, ++ GUEST_PANIC_INFORMATION_TYPE__MAX, ++} GuestPanicInformationType; ++ ++#define GuestPanicInformationType_str(val) \ ++ qapi_enum_lookup(&GuestPanicInformationType_lookup, (val)) ++ ++extern const QEnumLookup GuestPanicInformationType_lookup; ++ ++typedef struct q_obj_GuestPanicInformation_base q_obj_GuestPanicInformation_base; ++ ++typedef struct GuestPanicInformation GuestPanicInformation; ++ ++typedef struct GuestPanicInformationHyperV GuestPanicInformationHyperV; ++ ++typedef enum S390CrashReason { ++ S390_CRASH_REASON_UNKNOWN, ++ S390_CRASH_REASON_DISABLED_WAIT, ++ S390_CRASH_REASON_EXTINT_LOOP, ++ S390_CRASH_REASON_PGMINT_LOOP, ++ S390_CRASH_REASON_OPINT_LOOP, ++ S390_CRASH_REASON__MAX, ++} S390CrashReason; ++ ++#define S390CrashReason_str(val) \ ++ qapi_enum_lookup(&S390CrashReason_lookup, (val)) ++ ++extern const QEnumLookup S390CrashReason_lookup; ++ ++typedef struct GuestPanicInformationS390 GuestPanicInformationS390; ++ ++typedef struct q_obj_MEMORY_FAILURE_arg q_obj_MEMORY_FAILURE_arg; ++ ++typedef enum MemoryFailureRecipient { ++ MEMORY_FAILURE_RECIPIENT_HYPERVISOR, ++ MEMORY_FAILURE_RECIPIENT_GUEST, ++ MEMORY_FAILURE_RECIPIENT__MAX, ++} MemoryFailureRecipient; ++ ++#define MemoryFailureRecipient_str(val) \ ++ qapi_enum_lookup(&MemoryFailureRecipient_lookup, (val)) ++ ++extern const QEnumLookup MemoryFailureRecipient_lookup; ++ ++typedef enum MemoryFailureAction { ++ MEMORY_FAILURE_ACTION_IGNORE, ++ MEMORY_FAILURE_ACTION_INJECT, ++ MEMORY_FAILURE_ACTION_FATAL, ++ MEMORY_FAILURE_ACTION_RESET, ++ MEMORY_FAILURE_ACTION__MAX, ++} MemoryFailureAction; ++ ++#define MemoryFailureAction_str(val) \ ++ qapi_enum_lookup(&MemoryFailureAction_lookup, (val)) ++ ++extern const QEnumLookup MemoryFailureAction_lookup; ++ ++typedef struct MemoryFailureFlags MemoryFailureFlags; ++ ++typedef enum NotifyVmexitOption { ++ NOTIFY_VMEXIT_OPTION_RUN, ++ NOTIFY_VMEXIT_OPTION_INTERNAL_ERROR, ++ NOTIFY_VMEXIT_OPTION_DISABLE, ++ NOTIFY_VMEXIT_OPTION__MAX, ++} NotifyVmexitOption; ++ ++#define NotifyVmexitOption_str(val) \ ++ qapi_enum_lookup(&NotifyVmexitOption_lookup, (val)) ++ ++extern const QEnumLookup NotifyVmexitOption_lookup; ++ ++struct StatusInfo { ++ bool running; ++ RunState status; ++}; ++ ++void qapi_free_StatusInfo(StatusInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(StatusInfo, qapi_free_StatusInfo) ++ ++struct q_obj_SHUTDOWN_arg { ++ bool guest; ++ ShutdownCause reason; ++}; ++ ++struct q_obj_RESET_arg { ++ bool guest; ++ ShutdownCause reason; ++}; ++ ++struct q_obj_WATCHDOG_arg { ++ WatchdogAction action; ++}; ++ ++struct q_obj_watchdog_set_action_arg { ++ WatchdogAction action; ++}; ++ ++struct q_obj_set_action_arg { ++ bool has_reboot; ++ RebootAction reboot; ++ bool has_shutdown; ++ ShutdownAction shutdown; ++ bool has_panic; ++ PanicAction panic; ++ bool has_watchdog; ++ WatchdogAction watchdog; ++}; ++ ++struct q_obj_GUEST_PANICKED_arg { ++ GuestPanicAction action; ++ GuestPanicInformation *info; ++}; ++ ++struct q_obj_GUEST_CRASHLOADED_arg { ++ GuestPanicAction action; ++ GuestPanicInformation *info; ++}; ++ ++struct q_obj_GuestPanicInformation_base { ++ GuestPanicInformationType type; ++}; ++ ++struct GuestPanicInformationHyperV { ++ uint64_t arg1; ++ uint64_t arg2; ++ uint64_t arg3; ++ uint64_t arg4; ++ uint64_t arg5; ++}; ++ ++struct GuestPanicInformationS390 { ++ uint32_t core; ++ uint64_t psw_mask; ++ uint64_t psw_addr; ++ S390CrashReason reason; ++}; ++ ++struct GuestPanicInformation { ++ GuestPanicInformationType type; ++ union { /* union tag is @type */ ++ GuestPanicInformationHyperV hyper_v; ++ GuestPanicInformationS390 s390; ++ } u; ++}; ++ ++void qapi_free_GuestPanicInformation(GuestPanicInformation *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(GuestPanicInformation, qapi_free_GuestPanicInformation) ++ ++void qapi_free_GuestPanicInformationHyperV(GuestPanicInformationHyperV *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(GuestPanicInformationHyperV, qapi_free_GuestPanicInformationHyperV) ++ ++void qapi_free_GuestPanicInformationS390(GuestPanicInformationS390 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(GuestPanicInformationS390, qapi_free_GuestPanicInformationS390) ++ ++struct q_obj_MEMORY_FAILURE_arg { ++ MemoryFailureRecipient recipient; ++ MemoryFailureAction action; ++ MemoryFailureFlags *flags; ++}; ++ ++struct MemoryFailureFlags { ++ bool action_required; ++ bool recursive; ++}; ++ ++void qapi_free_MemoryFailureFlags(MemoryFailureFlags *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(MemoryFailureFlags, qapi_free_MemoryFailureFlags) ++ ++#endif /* QAPI_TYPES_RUN_STATE_H */ +diff --git a/include/qapi/qapi-types-sockets.h b/include/qapi/qapi-types-sockets.h +new file mode 100644 +index 00000000..3efdfe75 +--- /dev/null ++++ b/include/qapi/qapi-types-sockets.h +@@ -0,0 +1,220 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_SOCKETS_H ++#define QAPI_TYPES_SOCKETS_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum NetworkAddressFamily { ++ NETWORK_ADDRESS_FAMILY_IPV4, ++ NETWORK_ADDRESS_FAMILY_IPV6, ++ NETWORK_ADDRESS_FAMILY_UNIX, ++ NETWORK_ADDRESS_FAMILY_VSOCK, ++ NETWORK_ADDRESS_FAMILY_UNKNOWN, ++ NETWORK_ADDRESS_FAMILY__MAX, ++} NetworkAddressFamily; ++ ++#define NetworkAddressFamily_str(val) \ ++ qapi_enum_lookup(&NetworkAddressFamily_lookup, (val)) ++ ++extern const QEnumLookup NetworkAddressFamily_lookup; ++ ++typedef struct InetSocketAddressBase InetSocketAddressBase; ++ ++typedef struct InetSocketAddress InetSocketAddress; ++ ++typedef struct UnixSocketAddress UnixSocketAddress; ++ ++typedef struct VsockSocketAddress VsockSocketAddress; ++ ++typedef struct FdSocketAddress FdSocketAddress; ++ ++typedef struct InetSocketAddressWrapper InetSocketAddressWrapper; ++ ++typedef struct UnixSocketAddressWrapper UnixSocketAddressWrapper; ++ ++typedef struct VsockSocketAddressWrapper VsockSocketAddressWrapper; ++ ++typedef struct FdSocketAddressWrapper FdSocketAddressWrapper; ++ ++typedef struct q_obj_SocketAddressLegacy_base q_obj_SocketAddressLegacy_base; ++ ++typedef struct SocketAddressLegacy SocketAddressLegacy; ++ ++typedef enum SocketAddressType { ++ SOCKET_ADDRESS_TYPE_INET, ++ SOCKET_ADDRESS_TYPE_UNIX, ++ SOCKET_ADDRESS_TYPE_VSOCK, ++ SOCKET_ADDRESS_TYPE_FD, ++ SOCKET_ADDRESS_TYPE__MAX, ++} SocketAddressType; ++ ++#define SocketAddressType_str(val) \ ++ qapi_enum_lookup(&SocketAddressType_lookup, (val)) ++ ++extern const QEnumLookup SocketAddressType_lookup; ++ ++typedef struct q_obj_SocketAddress_base q_obj_SocketAddress_base; ++ ++typedef struct SocketAddress SocketAddress; ++ ++typedef struct SocketAddressList SocketAddressList; ++ ++typedef struct InetSocketAddressBaseList InetSocketAddressBaseList; ++ ++struct InetSocketAddressBase { ++ char *host; ++ char *port; ++}; ++ ++void qapi_free_InetSocketAddressBase(InetSocketAddressBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(InetSocketAddressBase, qapi_free_InetSocketAddressBase) ++ ++struct InetSocketAddress { ++ /* Members inherited from InetSocketAddressBase: */ ++ char *host; ++ char *port; ++ /* Own members: */ ++ bool has_numeric; ++ bool numeric; ++ bool has_to; ++ uint16_t to; ++ bool has_ipv4; ++ bool ipv4; ++ bool has_ipv6; ++ bool ipv6; ++ bool has_keep_alive; ++ bool keep_alive; ++#if defined(HAVE_IPPROTO_MPTCP) ++ bool has_mptcp; ++ bool mptcp; ++#endif /* defined(HAVE_IPPROTO_MPTCP) */ ++}; ++ ++static inline InetSocketAddressBase *qapi_InetSocketAddress_base(const InetSocketAddress *obj) ++{ ++ return (InetSocketAddressBase *)obj; ++} ++ ++void qapi_free_InetSocketAddress(InetSocketAddress *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(InetSocketAddress, qapi_free_InetSocketAddress) ++ ++struct UnixSocketAddress { ++ char *path; ++#if defined(CONFIG_LINUX) ++ bool has_abstract; ++ bool abstract; ++#endif /* defined(CONFIG_LINUX) */ ++#if defined(CONFIG_LINUX) ++ bool has_tight; ++ bool tight; ++#endif /* defined(CONFIG_LINUX) */ ++}; ++ ++void qapi_free_UnixSocketAddress(UnixSocketAddress *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(UnixSocketAddress, qapi_free_UnixSocketAddress) ++ ++struct VsockSocketAddress { ++ char *cid; ++ char *port; ++}; ++ ++void qapi_free_VsockSocketAddress(VsockSocketAddress *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(VsockSocketAddress, qapi_free_VsockSocketAddress) ++ ++struct FdSocketAddress { ++ char *str; ++}; ++ ++void qapi_free_FdSocketAddress(FdSocketAddress *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(FdSocketAddress, qapi_free_FdSocketAddress) ++ ++struct InetSocketAddressWrapper { ++ InetSocketAddress *data; ++}; ++ ++void qapi_free_InetSocketAddressWrapper(InetSocketAddressWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(InetSocketAddressWrapper, qapi_free_InetSocketAddressWrapper) ++ ++struct UnixSocketAddressWrapper { ++ UnixSocketAddress *data; ++}; ++ ++void qapi_free_UnixSocketAddressWrapper(UnixSocketAddressWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(UnixSocketAddressWrapper, qapi_free_UnixSocketAddressWrapper) ++ ++struct VsockSocketAddressWrapper { ++ VsockSocketAddress *data; ++}; ++ ++void qapi_free_VsockSocketAddressWrapper(VsockSocketAddressWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(VsockSocketAddressWrapper, qapi_free_VsockSocketAddressWrapper) ++ ++struct FdSocketAddressWrapper { ++ FdSocketAddress *data; ++}; ++ ++void qapi_free_FdSocketAddressWrapper(FdSocketAddressWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(FdSocketAddressWrapper, qapi_free_FdSocketAddressWrapper) ++ ++struct q_obj_SocketAddressLegacy_base { ++ SocketAddressType type; ++}; ++ ++struct SocketAddressLegacy { ++ SocketAddressType type; ++ union { /* union tag is @type */ ++ InetSocketAddressWrapper inet; ++ UnixSocketAddressWrapper q_unix; ++ VsockSocketAddressWrapper vsock; ++ FdSocketAddressWrapper fd; ++ } u; ++}; ++ ++void qapi_free_SocketAddressLegacy(SocketAddressLegacy *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SocketAddressLegacy, qapi_free_SocketAddressLegacy) ++ ++struct q_obj_SocketAddress_base { ++ SocketAddressType type; ++}; ++ ++struct SocketAddress { ++ SocketAddressType type; ++ union { /* union tag is @type */ ++ InetSocketAddress inet; ++ UnixSocketAddress q_unix; ++ VsockSocketAddress vsock; ++ FdSocketAddress fd; ++ } u; ++}; ++ ++void qapi_free_SocketAddress(SocketAddress *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SocketAddress, qapi_free_SocketAddress) ++ ++struct SocketAddressList { ++ SocketAddressList *next; ++ SocketAddress *value; ++}; ++ ++void qapi_free_SocketAddressList(SocketAddressList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SocketAddressList, qapi_free_SocketAddressList) ++ ++struct InetSocketAddressBaseList { ++ InetSocketAddressBaseList *next; ++ InetSocketAddressBase *value; ++}; ++ ++void qapi_free_InetSocketAddressBaseList(InetSocketAddressBaseList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(InetSocketAddressBaseList, qapi_free_InetSocketAddressBaseList) ++ ++#endif /* QAPI_TYPES_SOCKETS_H */ +diff --git a/include/qapi/qapi-types-yank.h b/include/qapi/qapi-types-yank.h +new file mode 100644 +index 00000000..da699a92 +--- /dev/null ++++ b/include/qapi/qapi-types-yank.h +@@ -0,0 +1,83 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_YANK_H ++#define QAPI_TYPES_YANK_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum YankInstanceType { ++ YANK_INSTANCE_TYPE_BLOCK_NODE, ++ YANK_INSTANCE_TYPE_CHARDEV, ++ YANK_INSTANCE_TYPE_MIGRATION, ++ YANK_INSTANCE_TYPE__MAX, ++} YankInstanceType; ++ ++#define YankInstanceType_str(val) \ ++ qapi_enum_lookup(&YankInstanceType_lookup, (val)) ++ ++extern const QEnumLookup YankInstanceType_lookup; ++ ++typedef struct YankInstanceBlockNode YankInstanceBlockNode; ++ ++typedef struct YankInstanceChardev YankInstanceChardev; ++ ++typedef struct q_obj_YankInstance_base q_obj_YankInstance_base; ++ ++typedef struct YankInstance YankInstance; ++ ++typedef struct YankInstanceList YankInstanceList; ++ ++typedef struct q_obj_yank_arg q_obj_yank_arg; ++ ++struct YankInstanceBlockNode { ++ char *node_name; ++}; ++ ++void qapi_free_YankInstanceBlockNode(YankInstanceBlockNode *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(YankInstanceBlockNode, qapi_free_YankInstanceBlockNode) ++ ++struct YankInstanceChardev { ++ char *id; ++}; ++ ++void qapi_free_YankInstanceChardev(YankInstanceChardev *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(YankInstanceChardev, qapi_free_YankInstanceChardev) ++ ++struct q_obj_YankInstance_base { ++ YankInstanceType type; ++}; ++ ++struct YankInstance { ++ YankInstanceType type; ++ union { /* union tag is @type */ ++ YankInstanceBlockNode block_node; ++ YankInstanceChardev chardev; ++ } u; ++}; ++ ++void qapi_free_YankInstance(YankInstance *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(YankInstance, qapi_free_YankInstance) ++ ++struct YankInstanceList { ++ YankInstanceList *next; ++ YankInstance *value; ++}; ++ ++void qapi_free_YankInstanceList(YankInstanceList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(YankInstanceList, qapi_free_YankInstanceList) ++ ++struct q_obj_yank_arg { ++ YankInstanceList *instances; ++}; ++ ++#endif /* QAPI_TYPES_YANK_H */ +diff --git a/include/qapi/qapi-visit-block-core.h b/include/qapi/qapi-visit-block-core.h +new file mode 100644 +index 00000000..9462b281 +--- /dev/null ++++ b/include/qapi/qapi-visit-block-core.h +@@ -0,0 +1,960 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_VISIT_BLOCK_CORE_H ++#define QAPI_VISIT_BLOCK_CORE_H ++ ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi-types-block-core.h" ++ ++#include "qapi-visit-common.h" ++#include "qapi-visit-crypto.h" ++#include "qapi-visit-job.h" ++#include "qapi-visit-sockets.h" ++ ++bool visit_type_SnapshotInfo_members(Visitor *v, SnapshotInfo *obj, Error **errp); ++ ++bool visit_type_SnapshotInfo(Visitor *v, const char *name, ++ SnapshotInfo **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2EncryptionBase_members(Visitor *v, ImageInfoSpecificQCow2EncryptionBase *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2EncryptionBase(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2EncryptionBase **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2Encryption_members(Visitor *v, ImageInfoSpecificQCow2Encryption *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2Encryption(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2Encryption **obj, Error **errp); ++ ++bool visit_type_Qcow2BitmapInfoList(Visitor *v, const char *name, ++ Qcow2BitmapInfoList **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2_members(Visitor *v, ImageInfoSpecificQCow2 *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2 **obj, Error **errp); ++ ++bool visit_type_VmdkExtentInfoList(Visitor *v, const char *name, ++ VmdkExtentInfoList **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificVmdk_members(Visitor *v, ImageInfoSpecificVmdk *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificVmdk(Visitor *v, const char *name, ++ ImageInfoSpecificVmdk **obj, Error **errp); ++ ++bool visit_type_VmdkExtentInfo_members(Visitor *v, VmdkExtentInfo *obj, Error **errp); ++ ++bool visit_type_VmdkExtentInfo(Visitor *v, const char *name, ++ VmdkExtentInfo **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificRbd_members(Visitor *v, ImageInfoSpecificRbd *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificRbd(Visitor *v, const char *name, ++ ImageInfoSpecificRbd **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificFile_members(Visitor *v, ImageInfoSpecificFile *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificFile(Visitor *v, const char *name, ++ ImageInfoSpecificFile **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificKind(Visitor *v, const char *name, ++ ImageInfoSpecificKind *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2Wrapper_members(Visitor *v, ImageInfoSpecificQCow2Wrapper *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2Wrapper(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2Wrapper **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificVmdkWrapper_members(Visitor *v, ImageInfoSpecificVmdkWrapper *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificVmdkWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificVmdkWrapper **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificLUKSWrapper_members(Visitor *v, ImageInfoSpecificLUKSWrapper *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificLUKSWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificLUKSWrapper **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificRbdWrapper_members(Visitor *v, ImageInfoSpecificRbdWrapper *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificRbdWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificRbdWrapper **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificFileWrapper_members(Visitor *v, ImageInfoSpecificFileWrapper *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificFileWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificFileWrapper **obj, Error **errp); ++ ++bool visit_type_q_obj_ImageInfoSpecific_base_members(Visitor *v, q_obj_ImageInfoSpecific_base *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecific_members(Visitor *v, ImageInfoSpecific *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecific(Visitor *v, const char *name, ++ ImageInfoSpecific **obj, Error **errp); ++ ++bool visit_type_SnapshotInfoList(Visitor *v, const char *name, ++ SnapshotInfoList **obj, Error **errp); ++ ++bool visit_type_BlockNodeInfo_members(Visitor *v, BlockNodeInfo *obj, Error **errp); ++ ++bool visit_type_BlockNodeInfo(Visitor *v, const char *name, ++ BlockNodeInfo **obj, Error **errp); ++ ++bool visit_type_ImageInfo_members(Visitor *v, ImageInfo *obj, Error **errp); ++ ++bool visit_type_ImageInfo(Visitor *v, const char *name, ++ ImageInfo **obj, Error **errp); ++ ++bool visit_type_BlockChildInfo_members(Visitor *v, BlockChildInfo *obj, Error **errp); ++ ++bool visit_type_BlockChildInfo(Visitor *v, const char *name, ++ BlockChildInfo **obj, Error **errp); ++ ++bool visit_type_BlockChildInfoList(Visitor *v, const char *name, ++ BlockChildInfoList **obj, Error **errp); ++ ++bool visit_type_BlockGraphInfo_members(Visitor *v, BlockGraphInfo *obj, Error **errp); ++ ++bool visit_type_BlockGraphInfo(Visitor *v, const char *name, ++ BlockGraphInfo **obj, Error **errp); ++ ++bool visit_type_ImageCheck_members(Visitor *v, ImageCheck *obj, Error **errp); ++ ++bool visit_type_ImageCheck(Visitor *v, const char *name, ++ ImageCheck **obj, Error **errp); ++ ++bool visit_type_MapEntry_members(Visitor *v, MapEntry *obj, Error **errp); ++ ++bool visit_type_MapEntry(Visitor *v, const char *name, ++ MapEntry **obj, Error **errp); ++ ++bool visit_type_BlockdevCacheInfo_members(Visitor *v, BlockdevCacheInfo *obj, Error **errp); ++ ++bool visit_type_BlockdevCacheInfo(Visitor *v, const char *name, ++ BlockdevCacheInfo **obj, Error **errp); ++ ++bool visit_type_BlockDirtyInfoList(Visitor *v, const char *name, ++ BlockDirtyInfoList **obj, Error **errp); ++ ++bool visit_type_BlockDeviceInfo_members(Visitor *v, BlockDeviceInfo *obj, Error **errp); ++ ++bool visit_type_BlockDeviceInfo(Visitor *v, const char *name, ++ BlockDeviceInfo **obj, Error **errp); ++ ++bool visit_type_BlockDeviceIoStatus(Visitor *v, const char *name, ++ BlockDeviceIoStatus *obj, Error **errp); ++ ++bool visit_type_BlockDirtyInfo_members(Visitor *v, BlockDirtyInfo *obj, Error **errp); ++ ++bool visit_type_BlockDirtyInfo(Visitor *v, const char *name, ++ BlockDirtyInfo **obj, Error **errp); ++ ++bool visit_type_Qcow2BitmapInfoFlags(Visitor *v, const char *name, ++ Qcow2BitmapInfoFlags *obj, Error **errp); ++ ++bool visit_type_Qcow2BitmapInfoFlagsList(Visitor *v, const char *name, ++ Qcow2BitmapInfoFlagsList **obj, Error **errp); ++ ++bool visit_type_Qcow2BitmapInfo_members(Visitor *v, Qcow2BitmapInfo *obj, Error **errp); ++ ++bool visit_type_Qcow2BitmapInfo(Visitor *v, const char *name, ++ Qcow2BitmapInfo **obj, Error **errp); ++ ++bool visit_type_BlockLatencyHistogramInfo_members(Visitor *v, BlockLatencyHistogramInfo *obj, Error **errp); ++ ++bool visit_type_BlockLatencyHistogramInfo(Visitor *v, const char *name, ++ BlockLatencyHistogramInfo **obj, Error **errp); ++ ++bool visit_type_BlockInfo_members(Visitor *v, BlockInfo *obj, Error **errp); ++ ++bool visit_type_BlockInfo(Visitor *v, const char *name, ++ BlockInfo **obj, Error **errp); ++ ++bool visit_type_BlockMeasureInfo_members(Visitor *v, BlockMeasureInfo *obj, Error **errp); ++ ++bool visit_type_BlockMeasureInfo(Visitor *v, const char *name, ++ BlockMeasureInfo **obj, Error **errp); ++ ++bool visit_type_BlockInfoList(Visitor *v, const char *name, ++ BlockInfoList **obj, Error **errp); ++ ++bool visit_type_BlockDeviceTimedStats_members(Visitor *v, BlockDeviceTimedStats *obj, Error **errp); ++ ++bool visit_type_BlockDeviceTimedStats(Visitor *v, const char *name, ++ BlockDeviceTimedStats **obj, Error **errp); ++ ++bool visit_type_BlockDeviceTimedStatsList(Visitor *v, const char *name, ++ BlockDeviceTimedStatsList **obj, Error **errp); ++ ++bool visit_type_BlockDeviceStats_members(Visitor *v, BlockDeviceStats *obj, Error **errp); ++ ++bool visit_type_BlockDeviceStats(Visitor *v, const char *name, ++ BlockDeviceStats **obj, Error **errp); ++ ++bool visit_type_BlockStatsSpecificFile_members(Visitor *v, BlockStatsSpecificFile *obj, Error **errp); ++ ++bool visit_type_BlockStatsSpecificFile(Visitor *v, const char *name, ++ BlockStatsSpecificFile **obj, Error **errp); ++ ++bool visit_type_BlockStatsSpecificNvme_members(Visitor *v, BlockStatsSpecificNvme *obj, Error **errp); ++ ++bool visit_type_BlockStatsSpecificNvme(Visitor *v, const char *name, ++ BlockStatsSpecificNvme **obj, Error **errp); ++ ++bool visit_type_q_obj_BlockStatsSpecific_base_members(Visitor *v, q_obj_BlockStatsSpecific_base *obj, Error **errp); ++ ++bool visit_type_BlockStatsSpecific_members(Visitor *v, BlockStatsSpecific *obj, Error **errp); ++ ++bool visit_type_BlockStatsSpecific(Visitor *v, const char *name, ++ BlockStatsSpecific **obj, Error **errp); ++ ++bool visit_type_BlockStats_members(Visitor *v, BlockStats *obj, Error **errp); ++ ++bool visit_type_BlockStats(Visitor *v, const char *name, ++ BlockStats **obj, Error **errp); ++ ++bool visit_type_q_obj_query_blockstats_arg_members(Visitor *v, q_obj_query_blockstats_arg *obj, Error **errp); ++ ++bool visit_type_BlockStatsList(Visitor *v, const char *name, ++ BlockStatsList **obj, Error **errp); ++ ++bool visit_type_BlockdevOnError(Visitor *v, const char *name, ++ BlockdevOnError *obj, Error **errp); ++ ++bool visit_type_MirrorSyncMode(Visitor *v, const char *name, ++ MirrorSyncMode *obj, Error **errp); ++ ++bool visit_type_BitmapSyncMode(Visitor *v, const char *name, ++ BitmapSyncMode *obj, Error **errp); ++ ++bool visit_type_MirrorCopyMode(Visitor *v, const char *name, ++ MirrorCopyMode *obj, Error **errp); ++ ++bool visit_type_BlockJobInfoMirror_members(Visitor *v, BlockJobInfoMirror *obj, Error **errp); ++ ++bool visit_type_BlockJobInfoMirror(Visitor *v, const char *name, ++ BlockJobInfoMirror **obj, Error **errp); ++ ++bool visit_type_q_obj_BlockJobInfo_base_members(Visitor *v, q_obj_BlockJobInfo_base *obj, Error **errp); ++ ++bool visit_type_BlockJobInfo_members(Visitor *v, BlockJobInfo *obj, Error **errp); ++ ++bool visit_type_BlockJobInfo(Visitor *v, const char *name, ++ BlockJobInfo **obj, Error **errp); ++ ++bool visit_type_BlockJobInfoList(Visitor *v, const char *name, ++ BlockJobInfoList **obj, Error **errp); ++ ++bool visit_type_q_obj_block_resize_arg_members(Visitor *v, q_obj_block_resize_arg *obj, Error **errp); ++ ++bool visit_type_NewImageMode(Visitor *v, const char *name, ++ NewImageMode *obj, Error **errp); ++ ++bool visit_type_BlockdevSnapshotSync_members(Visitor *v, BlockdevSnapshotSync *obj, Error **errp); ++ ++bool visit_type_BlockdevSnapshotSync(Visitor *v, const char *name, ++ BlockdevSnapshotSync **obj, Error **errp); ++ ++bool visit_type_BlockdevSnapshot_members(Visitor *v, BlockdevSnapshot *obj, Error **errp); ++ ++bool visit_type_BlockdevSnapshot(Visitor *v, const char *name, ++ BlockdevSnapshot **obj, Error **errp); ++ ++bool visit_type_BackupPerf_members(Visitor *v, BackupPerf *obj, Error **errp); ++ ++bool visit_type_BackupPerf(Visitor *v, const char *name, ++ BackupPerf **obj, Error **errp); ++ ++bool visit_type_BackupCommon_members(Visitor *v, BackupCommon *obj, Error **errp); ++ ++bool visit_type_BackupCommon(Visitor *v, const char *name, ++ BackupCommon **obj, Error **errp); ++ ++bool visit_type_DriveBackup_members(Visitor *v, DriveBackup *obj, Error **errp); ++ ++bool visit_type_DriveBackup(Visitor *v, const char *name, ++ DriveBackup **obj, Error **errp); ++ ++bool visit_type_BlockdevBackup_members(Visitor *v, BlockdevBackup *obj, Error **errp); ++ ++bool visit_type_BlockdevBackup(Visitor *v, const char *name, ++ BlockdevBackup **obj, Error **errp); ++ ++bool visit_type_q_obj_change_backing_file_arg_members(Visitor *v, q_obj_change_backing_file_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_commit_arg_members(Visitor *v, q_obj_block_commit_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_query_named_block_nodes_arg_members(Visitor *v, q_obj_query_named_block_nodes_arg *obj, Error **errp); ++ ++bool visit_type_BlockDeviceInfoList(Visitor *v, const char *name, ++ BlockDeviceInfoList **obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphNodeType(Visitor *v, const char *name, ++ XDbgBlockGraphNodeType *obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphNode_members(Visitor *v, XDbgBlockGraphNode *obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphNode(Visitor *v, const char *name, ++ XDbgBlockGraphNode **obj, Error **errp); ++ ++bool visit_type_BlockPermission(Visitor *v, const char *name, ++ BlockPermission *obj, Error **errp); ++ ++bool visit_type_BlockPermissionList(Visitor *v, const char *name, ++ BlockPermissionList **obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphEdge_members(Visitor *v, XDbgBlockGraphEdge *obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphEdge(Visitor *v, const char *name, ++ XDbgBlockGraphEdge **obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphNodeList(Visitor *v, const char *name, ++ XDbgBlockGraphNodeList **obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphEdgeList(Visitor *v, const char *name, ++ XDbgBlockGraphEdgeList **obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraph_members(Visitor *v, XDbgBlockGraph *obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraph(Visitor *v, const char *name, ++ XDbgBlockGraph **obj, Error **errp); ++ ++bool visit_type_DriveMirror_members(Visitor *v, DriveMirror *obj, Error **errp); ++ ++bool visit_type_DriveMirror(Visitor *v, const char *name, ++ DriveMirror **obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmap_members(Visitor *v, BlockDirtyBitmap *obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmap(Visitor *v, const char *name, ++ BlockDirtyBitmap **obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapAdd_members(Visitor *v, BlockDirtyBitmapAdd *obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapAdd(Visitor *v, const char *name, ++ BlockDirtyBitmapAdd **obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapOrStr(Visitor *v, const char *name, ++ BlockDirtyBitmapOrStr **obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapOrStrList(Visitor *v, const char *name, ++ BlockDirtyBitmapOrStrList **obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapMerge_members(Visitor *v, BlockDirtyBitmapMerge *obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapMerge(Visitor *v, const char *name, ++ BlockDirtyBitmapMerge **obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapSha256_members(Visitor *v, BlockDirtyBitmapSha256 *obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapSha256(Visitor *v, const char *name, ++ BlockDirtyBitmapSha256 **obj, Error **errp); ++ ++bool visit_type_q_obj_blockdev_mirror_arg_members(Visitor *v, q_obj_blockdev_mirror_arg *obj, Error **errp); ++ ++bool visit_type_BlockIOThrottle_members(Visitor *v, BlockIOThrottle *obj, Error **errp); ++ ++bool visit_type_BlockIOThrottle(Visitor *v, const char *name, ++ BlockIOThrottle **obj, Error **errp); ++ ++bool visit_type_ThrottleLimits_members(Visitor *v, ThrottleLimits *obj, Error **errp); ++ ++bool visit_type_ThrottleLimits(Visitor *v, const char *name, ++ ThrottleLimits **obj, Error **errp); ++ ++bool visit_type_ThrottleGroupProperties_members(Visitor *v, ThrottleGroupProperties *obj, Error **errp); ++ ++bool visit_type_ThrottleGroupProperties(Visitor *v, const char *name, ++ ThrottleGroupProperties **obj, Error **errp); ++ ++bool visit_type_q_obj_block_stream_arg_members(Visitor *v, q_obj_block_stream_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_set_speed_arg_members(Visitor *v, q_obj_block_job_set_speed_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_cancel_arg_members(Visitor *v, q_obj_block_job_cancel_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_pause_arg_members(Visitor *v, q_obj_block_job_pause_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_resume_arg_members(Visitor *v, q_obj_block_job_resume_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_complete_arg_members(Visitor *v, q_obj_block_job_complete_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_dismiss_arg_members(Visitor *v, q_obj_block_job_dismiss_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_finalize_arg_members(Visitor *v, q_obj_block_job_finalize_arg *obj, Error **errp); ++ ++bool visit_type_BlockJobChangeOptionsMirror_members(Visitor *v, BlockJobChangeOptionsMirror *obj, Error **errp); ++ ++bool visit_type_BlockJobChangeOptionsMirror(Visitor *v, const char *name, ++ BlockJobChangeOptionsMirror **obj, Error **errp); ++ ++bool visit_type_q_obj_BlockJobChangeOptions_base_members(Visitor *v, q_obj_BlockJobChangeOptions_base *obj, Error **errp); ++ ++bool visit_type_BlockJobChangeOptions_members(Visitor *v, BlockJobChangeOptions *obj, Error **errp); ++ ++bool visit_type_BlockJobChangeOptions(Visitor *v, const char *name, ++ BlockJobChangeOptions **obj, Error **errp); ++ ++bool visit_type_BlockdevDiscardOptions(Visitor *v, const char *name, ++ BlockdevDiscardOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevDetectZeroesOptions(Visitor *v, const char *name, ++ BlockdevDetectZeroesOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevAioOptions(Visitor *v, const char *name, ++ BlockdevAioOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevCacheOptions_members(Visitor *v, BlockdevCacheOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevCacheOptions(Visitor *v, const char *name, ++ BlockdevCacheOptions **obj, Error **errp); ++ ++bool visit_type_BlockdevDriver(Visitor *v, const char *name, ++ BlockdevDriver *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsFile_members(Visitor *v, BlockdevOptionsFile *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsFile(Visitor *v, const char *name, ++ BlockdevOptionsFile **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNull_members(Visitor *v, BlockdevOptionsNull *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNull(Visitor *v, const char *name, ++ BlockdevOptionsNull **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNVMe_members(Visitor *v, BlockdevOptionsNVMe *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNVMe(Visitor *v, const char *name, ++ BlockdevOptionsNVMe **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsVVFAT_members(Visitor *v, BlockdevOptionsVVFAT *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsVVFAT(Visitor *v, const char *name, ++ BlockdevOptionsVVFAT **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsGenericFormat_members(Visitor *v, BlockdevOptionsGenericFormat *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsGenericFormat(Visitor *v, const char *name, ++ BlockdevOptionsGenericFormat **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsLUKS_members(Visitor *v, BlockdevOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsLUKS(Visitor *v, const char *name, ++ BlockdevOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsGenericCOWFormat_members(Visitor *v, BlockdevOptionsGenericCOWFormat *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsGenericCOWFormat(Visitor *v, const char *name, ++ BlockdevOptionsGenericCOWFormat **obj, Error **errp); ++ ++bool visit_type_Qcow2OverlapCheckMode(Visitor *v, const char *name, ++ Qcow2OverlapCheckMode *obj, Error **errp); ++ ++bool visit_type_Qcow2OverlapCheckFlags_members(Visitor *v, Qcow2OverlapCheckFlags *obj, Error **errp); ++ ++bool visit_type_Qcow2OverlapCheckFlags(Visitor *v, const char *name, ++ Qcow2OverlapCheckFlags **obj, Error **errp); ++ ++bool visit_type_Qcow2OverlapChecks(Visitor *v, const char *name, ++ Qcow2OverlapChecks **obj, Error **errp); ++ ++bool visit_type_BlockdevQcowEncryptionFormat(Visitor *v, const char *name, ++ BlockdevQcowEncryptionFormat *obj, Error **errp); ++ ++bool visit_type_q_obj_BlockdevQcowEncryption_base_members(Visitor *v, q_obj_BlockdevQcowEncryption_base *obj, Error **errp); ++ ++bool visit_type_BlockdevQcowEncryption_members(Visitor *v, BlockdevQcowEncryption *obj, Error **errp); ++ ++bool visit_type_BlockdevQcowEncryption(Visitor *v, const char *name, ++ BlockdevQcowEncryption **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsQcow_members(Visitor *v, BlockdevOptionsQcow *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsQcow(Visitor *v, const char *name, ++ BlockdevOptionsQcow **obj, Error **errp); ++ ++bool visit_type_BlockdevQcow2EncryptionFormat(Visitor *v, const char *name, ++ BlockdevQcow2EncryptionFormat *obj, Error **errp); ++ ++bool visit_type_q_obj_BlockdevQcow2Encryption_base_members(Visitor *v, q_obj_BlockdevQcow2Encryption_base *obj, Error **errp); ++ ++bool visit_type_BlockdevQcow2Encryption_members(Visitor *v, BlockdevQcow2Encryption *obj, Error **errp); ++ ++bool visit_type_BlockdevQcow2Encryption(Visitor *v, const char *name, ++ BlockdevQcow2Encryption **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsPreallocate_members(Visitor *v, BlockdevOptionsPreallocate *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsPreallocate(Visitor *v, const char *name, ++ BlockdevOptionsPreallocate **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsQcow2_members(Visitor *v, BlockdevOptionsQcow2 *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsQcow2(Visitor *v, const char *name, ++ BlockdevOptionsQcow2 **obj, Error **errp); ++ ++bool visit_type_SshHostKeyCheckMode(Visitor *v, const char *name, ++ SshHostKeyCheckMode *obj, Error **errp); ++ ++bool visit_type_SshHostKeyCheckHashType(Visitor *v, const char *name, ++ SshHostKeyCheckHashType *obj, Error **errp); ++ ++bool visit_type_SshHostKeyHash_members(Visitor *v, SshHostKeyHash *obj, Error **errp); ++ ++bool visit_type_SshHostKeyHash(Visitor *v, const char *name, ++ SshHostKeyHash **obj, Error **errp); ++ ++bool visit_type_q_obj_SshHostKeyCheck_base_members(Visitor *v, q_obj_SshHostKeyCheck_base *obj, Error **errp); ++ ++bool visit_type_SshHostKeyCheck_members(Visitor *v, SshHostKeyCheck *obj, Error **errp); ++ ++bool visit_type_SshHostKeyCheck(Visitor *v, const char *name, ++ SshHostKeyCheck **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsSsh_members(Visitor *v, BlockdevOptionsSsh *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsSsh(Visitor *v, const char *name, ++ BlockdevOptionsSsh **obj, Error **errp); ++ ++bool visit_type_BlkdebugEvent(Visitor *v, const char *name, ++ BlkdebugEvent *obj, Error **errp); ++ ++bool visit_type_BlkdebugIOType(Visitor *v, const char *name, ++ BlkdebugIOType *obj, Error **errp); ++ ++bool visit_type_BlkdebugInjectErrorOptions_members(Visitor *v, BlkdebugInjectErrorOptions *obj, Error **errp); ++ ++bool visit_type_BlkdebugInjectErrorOptions(Visitor *v, const char *name, ++ BlkdebugInjectErrorOptions **obj, Error **errp); ++ ++bool visit_type_BlkdebugSetStateOptions_members(Visitor *v, BlkdebugSetStateOptions *obj, Error **errp); ++ ++bool visit_type_BlkdebugSetStateOptions(Visitor *v, const char *name, ++ BlkdebugSetStateOptions **obj, Error **errp); ++ ++bool visit_type_BlkdebugInjectErrorOptionsList(Visitor *v, const char *name, ++ BlkdebugInjectErrorOptionsList **obj, Error **errp); ++ ++bool visit_type_BlkdebugSetStateOptionsList(Visitor *v, const char *name, ++ BlkdebugSetStateOptionsList **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlkdebug_members(Visitor *v, BlockdevOptionsBlkdebug *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlkdebug(Visitor *v, const char *name, ++ BlockdevOptionsBlkdebug **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlklogwrites_members(Visitor *v, BlockdevOptionsBlklogwrites *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlklogwrites(Visitor *v, const char *name, ++ BlockdevOptionsBlklogwrites **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlkverify_members(Visitor *v, BlockdevOptionsBlkverify *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlkverify(Visitor *v, const char *name, ++ BlockdevOptionsBlkverify **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlkreplay_members(Visitor *v, BlockdevOptionsBlkreplay *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlkreplay(Visitor *v, const char *name, ++ BlockdevOptionsBlkreplay **obj, Error **errp); ++ ++bool visit_type_QuorumReadPattern(Visitor *v, const char *name, ++ QuorumReadPattern *obj, Error **errp); ++ ++bool visit_type_BlockdevRefList(Visitor *v, const char *name, ++ BlockdevRefList **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsQuorum_members(Visitor *v, BlockdevOptionsQuorum *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsQuorum(Visitor *v, const char *name, ++ BlockdevOptionsQuorum **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsGluster_members(Visitor *v, BlockdevOptionsGluster *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsGluster(Visitor *v, const char *name, ++ BlockdevOptionsGluster **obj, Error **errp); ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsIoUring_members(Visitor *v, BlockdevOptionsIoUring *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsIoUring(Visitor *v, const char *name, ++ BlockdevOptionsIoUring **obj, Error **errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsNvmeIoUring_members(Visitor *v, BlockdevOptionsNvmeIoUring *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNvmeIoUring(Visitor *v, const char *name, ++ BlockdevOptionsNvmeIoUring **obj, Error **errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsVirtioBlkVfioPci_members(Visitor *v, BlockdevOptionsVirtioBlkVfioPci *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsVirtioBlkVfioPci(Visitor *v, const char *name, ++ BlockdevOptionsVirtioBlkVfioPci **obj, Error **errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsVirtioBlkVhostUser_members(Visitor *v, BlockdevOptionsVirtioBlkVhostUser *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsVirtioBlkVhostUser(Visitor *v, const char *name, ++ BlockdevOptionsVirtioBlkVhostUser **obj, Error **errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsVirtioBlkVhostVdpa_members(Visitor *v, BlockdevOptionsVirtioBlkVhostVdpa *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsVirtioBlkVhostVdpa(Visitor *v, const char *name, ++ BlockdevOptionsVirtioBlkVhostVdpa **obj, Error **errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ ++bool visit_type_IscsiTransport(Visitor *v, const char *name, ++ IscsiTransport *obj, Error **errp); ++ ++bool visit_type_IscsiHeaderDigest(Visitor *v, const char *name, ++ IscsiHeaderDigest *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsIscsi_members(Visitor *v, BlockdevOptionsIscsi *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsIscsi(Visitor *v, const char *name, ++ BlockdevOptionsIscsi **obj, Error **errp); ++ ++bool visit_type_RbdAuthMode(Visitor *v, const char *name, ++ RbdAuthMode *obj, Error **errp); ++ ++bool visit_type_RbdImageEncryptionFormat(Visitor *v, const char *name, ++ RbdImageEncryptionFormat *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKSBase_members(Visitor *v, RbdEncryptionOptionsLUKSBase *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKSBase(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKSBase **obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKSBase_members(Visitor *v, RbdEncryptionCreateOptionsLUKSBase *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKSBase(Visitor *v, const char *name, ++ RbdEncryptionCreateOptionsLUKSBase **obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKS_members(Visitor *v, RbdEncryptionOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKS(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKS2_members(Visitor *v, RbdEncryptionOptionsLUKS2 *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKS2(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKS2 **obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKSAny_members(Visitor *v, RbdEncryptionOptionsLUKSAny *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKSAny(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKSAny **obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS_members(Visitor *v, RbdEncryptionCreateOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS(Visitor *v, const char *name, ++ RbdEncryptionCreateOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS2_members(Visitor *v, RbdEncryptionCreateOptionsLUKS2 *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS2(Visitor *v, const char *name, ++ RbdEncryptionCreateOptionsLUKS2 **obj, Error **errp); ++ ++bool visit_type_q_obj_RbdEncryptionOptions_base_members(Visitor *v, q_obj_RbdEncryptionOptions_base *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptions_members(Visitor *v, RbdEncryptionOptions *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptions(Visitor *v, const char *name, ++ RbdEncryptionOptions **obj, Error **errp); ++ ++bool visit_type_q_obj_RbdEncryptionCreateOptions_base_members(Visitor *v, q_obj_RbdEncryptionCreateOptions_base *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptions_members(Visitor *v, RbdEncryptionCreateOptions *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptions(Visitor *v, const char *name, ++ RbdEncryptionCreateOptions **obj, Error **errp); ++ ++bool visit_type_RbdAuthModeList(Visitor *v, const char *name, ++ RbdAuthModeList **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsRbd_members(Visitor *v, BlockdevOptionsRbd *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsRbd(Visitor *v, const char *name, ++ BlockdevOptionsRbd **obj, Error **errp); ++ ++#if defined(CONFIG_REPLICATION) ++bool visit_type_ReplicationMode(Visitor *v, const char *name, ++ ReplicationMode *obj, Error **errp); ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++#if defined(CONFIG_REPLICATION) ++bool visit_type_BlockdevOptionsReplication_members(Visitor *v, BlockdevOptionsReplication *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsReplication(Visitor *v, const char *name, ++ BlockdevOptionsReplication **obj, Error **errp); ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++bool visit_type_NFSTransport(Visitor *v, const char *name, ++ NFSTransport *obj, Error **errp); ++ ++bool visit_type_NFSServer_members(Visitor *v, NFSServer *obj, Error **errp); ++ ++bool visit_type_NFSServer(Visitor *v, const char *name, ++ NFSServer **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNfs_members(Visitor *v, BlockdevOptionsNfs *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNfs(Visitor *v, const char *name, ++ BlockdevOptionsNfs **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlBase_members(Visitor *v, BlockdevOptionsCurlBase *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlBase(Visitor *v, const char *name, ++ BlockdevOptionsCurlBase **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlHttp_members(Visitor *v, BlockdevOptionsCurlHttp *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlHttp(Visitor *v, const char *name, ++ BlockdevOptionsCurlHttp **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlHttps_members(Visitor *v, BlockdevOptionsCurlHttps *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlHttps(Visitor *v, const char *name, ++ BlockdevOptionsCurlHttps **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlFtp_members(Visitor *v, BlockdevOptionsCurlFtp *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlFtp(Visitor *v, const char *name, ++ BlockdevOptionsCurlFtp **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlFtps_members(Visitor *v, BlockdevOptionsCurlFtps *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlFtps(Visitor *v, const char *name, ++ BlockdevOptionsCurlFtps **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNbd_members(Visitor *v, BlockdevOptionsNbd *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNbd(Visitor *v, const char *name, ++ BlockdevOptionsNbd **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsRaw_members(Visitor *v, BlockdevOptionsRaw *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsRaw(Visitor *v, const char *name, ++ BlockdevOptionsRaw **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsThrottle_members(Visitor *v, BlockdevOptionsThrottle *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsThrottle(Visitor *v, const char *name, ++ BlockdevOptionsThrottle **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCor_members(Visitor *v, BlockdevOptionsCor *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCor(Visitor *v, const char *name, ++ BlockdevOptionsCor **obj, Error **errp); ++ ++bool visit_type_OnCbwError(Visitor *v, const char *name, ++ OnCbwError *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCbw_members(Visitor *v, BlockdevOptionsCbw *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCbw(Visitor *v, const char *name, ++ BlockdevOptionsCbw **obj, Error **errp); ++ ++bool visit_type_q_obj_BlockdevOptions_base_members(Visitor *v, q_obj_BlockdevOptions_base *obj, Error **errp); ++ ++bool visit_type_BlockdevOptions_members(Visitor *v, BlockdevOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevOptions(Visitor *v, const char *name, ++ BlockdevOptions **obj, Error **errp); ++ ++bool visit_type_BlockdevRef(Visitor *v, const char *name, ++ BlockdevRef **obj, Error **errp); ++ ++bool visit_type_BlockdevRefOrNull(Visitor *v, const char *name, ++ BlockdevRefOrNull **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsList(Visitor *v, const char *name, ++ BlockdevOptionsList **obj, Error **errp); ++ ++bool visit_type_q_obj_blockdev_reopen_arg_members(Visitor *v, q_obj_blockdev_reopen_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_blockdev_del_arg_members(Visitor *v, q_obj_blockdev_del_arg *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsFile_members(Visitor *v, BlockdevCreateOptionsFile *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsFile(Visitor *v, const char *name, ++ BlockdevCreateOptionsFile **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsGluster_members(Visitor *v, BlockdevCreateOptionsGluster *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsGluster(Visitor *v, const char *name, ++ BlockdevCreateOptionsGluster **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsLUKS_members(Visitor *v, BlockdevCreateOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsLUKS(Visitor *v, const char *name, ++ BlockdevCreateOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsNfs_members(Visitor *v, BlockdevCreateOptionsNfs *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsNfs(Visitor *v, const char *name, ++ BlockdevCreateOptionsNfs **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsParallels_members(Visitor *v, BlockdevCreateOptionsParallels *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsParallels(Visitor *v, const char *name, ++ BlockdevCreateOptionsParallels **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsQcow_members(Visitor *v, BlockdevCreateOptionsQcow *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsQcow(Visitor *v, const char *name, ++ BlockdevCreateOptionsQcow **obj, Error **errp); ++ ++bool visit_type_BlockdevQcow2Version(Visitor *v, const char *name, ++ BlockdevQcow2Version *obj, Error **errp); ++ ++bool visit_type_Qcow2CompressionType(Visitor *v, const char *name, ++ Qcow2CompressionType *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsQcow2_members(Visitor *v, BlockdevCreateOptionsQcow2 *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsQcow2(Visitor *v, const char *name, ++ BlockdevCreateOptionsQcow2 **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsQed_members(Visitor *v, BlockdevCreateOptionsQed *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsQed(Visitor *v, const char *name, ++ BlockdevCreateOptionsQed **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsRbd_members(Visitor *v, BlockdevCreateOptionsRbd *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsRbd(Visitor *v, const char *name, ++ BlockdevCreateOptionsRbd **obj, Error **errp); ++ ++bool visit_type_BlockdevVmdkSubformat(Visitor *v, const char *name, ++ BlockdevVmdkSubformat *obj, Error **errp); ++ ++bool visit_type_BlockdevVmdkAdapterType(Visitor *v, const char *name, ++ BlockdevVmdkAdapterType *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVmdk_members(Visitor *v, BlockdevCreateOptionsVmdk *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVmdk(Visitor *v, const char *name, ++ BlockdevCreateOptionsVmdk **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsSsh_members(Visitor *v, BlockdevCreateOptionsSsh *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsSsh(Visitor *v, const char *name, ++ BlockdevCreateOptionsSsh **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVdi_members(Visitor *v, BlockdevCreateOptionsVdi *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVdi(Visitor *v, const char *name, ++ BlockdevCreateOptionsVdi **obj, Error **errp); ++ ++bool visit_type_BlockdevVhdxSubformat(Visitor *v, const char *name, ++ BlockdevVhdxSubformat *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVhdx_members(Visitor *v, BlockdevCreateOptionsVhdx *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVhdx(Visitor *v, const char *name, ++ BlockdevCreateOptionsVhdx **obj, Error **errp); ++ ++bool visit_type_BlockdevVpcSubformat(Visitor *v, const char *name, ++ BlockdevVpcSubformat *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVpc_members(Visitor *v, BlockdevCreateOptionsVpc *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVpc(Visitor *v, const char *name, ++ BlockdevCreateOptionsVpc **obj, Error **errp); ++ ++bool visit_type_q_obj_BlockdevCreateOptions_base_members(Visitor *v, q_obj_BlockdevCreateOptions_base *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptions_members(Visitor *v, BlockdevCreateOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptions(Visitor *v, const char *name, ++ BlockdevCreateOptions **obj, Error **errp); ++ ++bool visit_type_q_obj_blockdev_create_arg_members(Visitor *v, q_obj_blockdev_create_arg *obj, Error **errp); ++ ++bool visit_type_BlockdevAmendOptionsLUKS_members(Visitor *v, BlockdevAmendOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_BlockdevAmendOptionsLUKS(Visitor *v, const char *name, ++ BlockdevAmendOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_BlockdevAmendOptionsQcow2_members(Visitor *v, BlockdevAmendOptionsQcow2 *obj, Error **errp); ++ ++bool visit_type_BlockdevAmendOptionsQcow2(Visitor *v, const char *name, ++ BlockdevAmendOptionsQcow2 **obj, Error **errp); ++ ++bool visit_type_q_obj_BlockdevAmendOptions_base_members(Visitor *v, q_obj_BlockdevAmendOptions_base *obj, Error **errp); ++ ++bool visit_type_BlockdevAmendOptions_members(Visitor *v, BlockdevAmendOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevAmendOptions(Visitor *v, const char *name, ++ BlockdevAmendOptions **obj, Error **errp); ++ ++bool visit_type_q_obj_x_blockdev_amend_arg_members(Visitor *v, q_obj_x_blockdev_amend_arg *obj, Error **errp); ++ ++bool visit_type_BlockErrorAction(Visitor *v, const char *name, ++ BlockErrorAction *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_IMAGE_CORRUPTED_arg_members(Visitor *v, q_obj_BLOCK_IMAGE_CORRUPTED_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_IO_ERROR_arg_members(Visitor *v, q_obj_BLOCK_IO_ERROR_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_JOB_COMPLETED_arg_members(Visitor *v, q_obj_BLOCK_JOB_COMPLETED_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_JOB_CANCELLED_arg_members(Visitor *v, q_obj_BLOCK_JOB_CANCELLED_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_JOB_ERROR_arg_members(Visitor *v, q_obj_BLOCK_JOB_ERROR_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_JOB_READY_arg_members(Visitor *v, q_obj_BLOCK_JOB_READY_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_JOB_PENDING_arg_members(Visitor *v, q_obj_BLOCK_JOB_PENDING_arg *obj, Error **errp); ++ ++bool visit_type_PreallocMode(Visitor *v, const char *name, ++ PreallocMode *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_WRITE_THRESHOLD_arg_members(Visitor *v, q_obj_BLOCK_WRITE_THRESHOLD_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_set_write_threshold_arg_members(Visitor *v, q_obj_block_set_write_threshold_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_x_blockdev_change_arg_members(Visitor *v, q_obj_x_blockdev_change_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_x_blockdev_set_iothread_arg_members(Visitor *v, q_obj_x_blockdev_set_iothread_arg *obj, Error **errp); ++ ++bool visit_type_QuorumOpType(Visitor *v, const char *name, ++ QuorumOpType *obj, Error **errp); ++ ++bool visit_type_q_obj_QUORUM_FAILURE_arg_members(Visitor *v, q_obj_QUORUM_FAILURE_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_QUORUM_REPORT_BAD_arg_members(Visitor *v, q_obj_QUORUM_REPORT_BAD_arg *obj, Error **errp); ++ ++bool visit_type_BlockdevSnapshotInternal_members(Visitor *v, BlockdevSnapshotInternal *obj, Error **errp); ++ ++bool visit_type_BlockdevSnapshotInternal(Visitor *v, const char *name, ++ BlockdevSnapshotInternal **obj, Error **errp); ++ ++bool visit_type_q_obj_blockdev_snapshot_delete_internal_sync_arg_members(Visitor *v, q_obj_blockdev_snapshot_delete_internal_sync_arg *obj, Error **errp); ++ ++bool visit_type_BlockGraphInfoList(Visitor *v, const char *name, ++ BlockGraphInfoList **obj, Error **errp); ++ ++bool visit_type_DummyBlockCoreForceArrays_members(Visitor *v, DummyBlockCoreForceArrays *obj, Error **errp); ++ ++bool visit_type_DummyBlockCoreForceArrays(Visitor *v, const char *name, ++ DummyBlockCoreForceArrays **obj, Error **errp); ++ ++#endif /* QAPI_VISIT_BLOCK_CORE_H */ +diff --git a/include/qapi/qapi-visit-common.h b/include/qapi/qapi-visit-common.h +new file mode 100644 +index 00000000..f0e770d0 +--- /dev/null ++++ b/include/qapi/qapi-visit-common.h +@@ -0,0 +1,55 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_VISIT_COMMON_H ++#define QAPI_VISIT_COMMON_H ++ ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi-types-common.h" ++ ++ ++bool visit_type_IoOperationType(Visitor *v, const char *name, ++ IoOperationType *obj, Error **errp); ++ ++bool visit_type_OnOffAuto(Visitor *v, const char *name, ++ OnOffAuto *obj, Error **errp); ++ ++bool visit_type_OnOffSplit(Visitor *v, const char *name, ++ OnOffSplit *obj, Error **errp); ++ ++bool visit_type_StrOrNull(Visitor *v, const char *name, ++ StrOrNull **obj, Error **errp); ++ ++bool visit_type_OffAutoPCIBAR(Visitor *v, const char *name, ++ OffAutoPCIBAR *obj, Error **errp); ++ ++bool visit_type_PCIELinkSpeed(Visitor *v, const char *name, ++ PCIELinkSpeed *obj, Error **errp); ++ ++bool visit_type_PCIELinkWidth(Visitor *v, const char *name, ++ PCIELinkWidth *obj, Error **errp); ++ ++bool visit_type_HostMemPolicy(Visitor *v, const char *name, ++ HostMemPolicy *obj, Error **errp); ++ ++bool visit_type_NetFilterDirection(Visitor *v, const char *name, ++ NetFilterDirection *obj, Error **errp); ++ ++bool visit_type_GrabToggleKeys(Visitor *v, const char *name, ++ GrabToggleKeys *obj, Error **errp); ++ ++bool visit_type_HumanReadableText_members(Visitor *v, HumanReadableText *obj, Error **errp); ++ ++bool visit_type_HumanReadableText(Visitor *v, const char *name, ++ HumanReadableText **obj, Error **errp); ++ ++#endif /* QAPI_VISIT_COMMON_H */ +diff --git a/include/qapi/qapi-visit-crypto.h b/include/qapi/qapi-visit-crypto.h +new file mode 100644 +index 00000000..64b5404f +--- /dev/null ++++ b/include/qapi/qapi-visit-crypto.h +@@ -0,0 +1,165 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_VISIT_CRYPTO_H ++#define QAPI_VISIT_CRYPTO_H ++ ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi-types-crypto.h" ++ ++ ++bool visit_type_QCryptoTLSCredsEndpoint(Visitor *v, const char *name, ++ QCryptoTLSCredsEndpoint *obj, Error **errp); ++ ++bool visit_type_QCryptoSecretFormat(Visitor *v, const char *name, ++ QCryptoSecretFormat *obj, Error **errp); ++ ++bool visit_type_QCryptoHashAlgorithm(Visitor *v, const char *name, ++ QCryptoHashAlgorithm *obj, Error **errp); ++ ++bool visit_type_QCryptoCipherAlgorithm(Visitor *v, const char *name, ++ QCryptoCipherAlgorithm *obj, Error **errp); ++ ++bool visit_type_QCryptoCipherMode(Visitor *v, const char *name, ++ QCryptoCipherMode *obj, Error **errp); ++ ++bool visit_type_QCryptoIVGenAlgorithm(Visitor *v, const char *name, ++ QCryptoIVGenAlgorithm *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockFormat(Visitor *v, const char *name, ++ QCryptoBlockFormat *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOptionsBase_members(Visitor *v, QCryptoBlockOptionsBase *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOptionsBase(Visitor *v, const char *name, ++ QCryptoBlockOptionsBase **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOptionsQCow_members(Visitor *v, QCryptoBlockOptionsQCow *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOptionsQCow(Visitor *v, const char *name, ++ QCryptoBlockOptionsQCow **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOptionsLUKS_members(Visitor *v, QCryptoBlockOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOptionsLUKS(Visitor *v, const char *name, ++ QCryptoBlockOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockCreateOptionsLUKS_members(Visitor *v, QCryptoBlockCreateOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockCreateOptionsLUKS(Visitor *v, const char *name, ++ QCryptoBlockCreateOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOpenOptions_members(Visitor *v, QCryptoBlockOpenOptions *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOpenOptions(Visitor *v, const char *name, ++ QCryptoBlockOpenOptions **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockCreateOptions_members(Visitor *v, QCryptoBlockCreateOptions *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockCreateOptions(Visitor *v, const char *name, ++ QCryptoBlockCreateOptions **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoBase_members(Visitor *v, QCryptoBlockInfoBase *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoBase(Visitor *v, const char *name, ++ QCryptoBlockInfoBase **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoLUKSSlot_members(Visitor *v, QCryptoBlockInfoLUKSSlot *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoLUKSSlot(Visitor *v, const char *name, ++ QCryptoBlockInfoLUKSSlot **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoLUKSSlotList(Visitor *v, const char *name, ++ QCryptoBlockInfoLUKSSlotList **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoLUKS_members(Visitor *v, QCryptoBlockInfoLUKS *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoLUKS(Visitor *v, const char *name, ++ QCryptoBlockInfoLUKS **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfo_members(Visitor *v, QCryptoBlockInfo *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfo(Visitor *v, const char *name, ++ QCryptoBlockInfo **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockLUKSKeyslotState(Visitor *v, const char *name, ++ QCryptoBlockLUKSKeyslotState *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockAmendOptionsLUKS_members(Visitor *v, QCryptoBlockAmendOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockAmendOptionsLUKS(Visitor *v, const char *name, ++ QCryptoBlockAmendOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockAmendOptions_members(Visitor *v, QCryptoBlockAmendOptions *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockAmendOptions(Visitor *v, const char *name, ++ QCryptoBlockAmendOptions **obj, Error **errp); ++ ++bool visit_type_SecretCommonProperties_members(Visitor *v, SecretCommonProperties *obj, Error **errp); ++ ++bool visit_type_SecretCommonProperties(Visitor *v, const char *name, ++ SecretCommonProperties **obj, Error **errp); ++ ++bool visit_type_SecretProperties_members(Visitor *v, SecretProperties *obj, Error **errp); ++ ++bool visit_type_SecretProperties(Visitor *v, const char *name, ++ SecretProperties **obj, Error **errp); ++ ++#if defined(CONFIG_SECRET_KEYRING) ++bool visit_type_SecretKeyringProperties_members(Visitor *v, SecretKeyringProperties *obj, Error **errp); ++ ++bool visit_type_SecretKeyringProperties(Visitor *v, const char *name, ++ SecretKeyringProperties **obj, Error **errp); ++#endif /* defined(CONFIG_SECRET_KEYRING) */ ++ ++bool visit_type_TlsCredsProperties_members(Visitor *v, TlsCredsProperties *obj, Error **errp); ++ ++bool visit_type_TlsCredsProperties(Visitor *v, const char *name, ++ TlsCredsProperties **obj, Error **errp); ++ ++bool visit_type_TlsCredsAnonProperties_members(Visitor *v, TlsCredsAnonProperties *obj, Error **errp); ++ ++bool visit_type_TlsCredsAnonProperties(Visitor *v, const char *name, ++ TlsCredsAnonProperties **obj, Error **errp); ++ ++bool visit_type_TlsCredsPskProperties_members(Visitor *v, TlsCredsPskProperties *obj, Error **errp); ++ ++bool visit_type_TlsCredsPskProperties(Visitor *v, const char *name, ++ TlsCredsPskProperties **obj, Error **errp); ++ ++bool visit_type_TlsCredsX509Properties_members(Visitor *v, TlsCredsX509Properties *obj, Error **errp); ++ ++bool visit_type_TlsCredsX509Properties(Visitor *v, const char *name, ++ TlsCredsX509Properties **obj, Error **errp); ++ ++bool visit_type_QCryptoAkCipherAlgorithm(Visitor *v, const char *name, ++ QCryptoAkCipherAlgorithm *obj, Error **errp); ++ ++bool visit_type_QCryptoAkCipherKeyType(Visitor *v, const char *name, ++ QCryptoAkCipherKeyType *obj, Error **errp); ++ ++bool visit_type_QCryptoRSAPaddingAlgorithm(Visitor *v, const char *name, ++ QCryptoRSAPaddingAlgorithm *obj, Error **errp); ++ ++bool visit_type_QCryptoAkCipherOptionsRSA_members(Visitor *v, QCryptoAkCipherOptionsRSA *obj, Error **errp); ++ ++bool visit_type_QCryptoAkCipherOptionsRSA(Visitor *v, const char *name, ++ QCryptoAkCipherOptionsRSA **obj, Error **errp); ++ ++bool visit_type_q_obj_QCryptoAkCipherOptions_base_members(Visitor *v, q_obj_QCryptoAkCipherOptions_base *obj, Error **errp); ++ ++bool visit_type_QCryptoAkCipherOptions_members(Visitor *v, QCryptoAkCipherOptions *obj, Error **errp); ++ ++bool visit_type_QCryptoAkCipherOptions(Visitor *v, const char *name, ++ QCryptoAkCipherOptions **obj, Error **errp); ++ ++#endif /* QAPI_VISIT_CRYPTO_H */ +diff --git a/include/qapi/qapi-visit-job.h b/include/qapi/qapi-visit-job.h +new file mode 100644 +index 00000000..5b5df705 +--- /dev/null ++++ b/include/qapi/qapi-visit-job.h +@@ -0,0 +1,51 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_VISIT_JOB_H ++#define QAPI_VISIT_JOB_H ++ ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi-types-job.h" ++ ++ ++bool visit_type_JobType(Visitor *v, const char *name, ++ JobType *obj, Error **errp); ++ ++bool visit_type_JobStatus(Visitor *v, const char *name, ++ JobStatus *obj, Error **errp); ++ ++bool visit_type_JobVerb(Visitor *v, const char *name, ++ JobVerb *obj, Error **errp); ++ ++bool visit_type_q_obj_JOB_STATUS_CHANGE_arg_members(Visitor *v, q_obj_JOB_STATUS_CHANGE_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_job_pause_arg_members(Visitor *v, q_obj_job_pause_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_job_resume_arg_members(Visitor *v, q_obj_job_resume_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_job_cancel_arg_members(Visitor *v, q_obj_job_cancel_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_job_complete_arg_members(Visitor *v, q_obj_job_complete_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_job_dismiss_arg_members(Visitor *v, q_obj_job_dismiss_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_job_finalize_arg_members(Visitor *v, q_obj_job_finalize_arg *obj, Error **errp); ++ ++bool visit_type_JobInfo_members(Visitor *v, JobInfo *obj, Error **errp); ++ ++bool visit_type_JobInfo(Visitor *v, const char *name, ++ JobInfo **obj, Error **errp); ++ ++bool visit_type_JobInfoList(Visitor *v, const char *name, ++ JobInfoList **obj, Error **errp); ++ ++#endif /* QAPI_VISIT_JOB_H */ +diff --git a/include/qapi/qapi-visit-sockets.h b/include/qapi/qapi-visit-sockets.h +new file mode 100644 +index 00000000..5cec7a91 +--- /dev/null ++++ b/include/qapi/qapi-visit-sockets.h +@@ -0,0 +1,91 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_VISIT_SOCKETS_H ++#define QAPI_VISIT_SOCKETS_H ++ ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi-types-sockets.h" ++ ++ ++bool visit_type_NetworkAddressFamily(Visitor *v, const char *name, ++ NetworkAddressFamily *obj, Error **errp); ++ ++bool visit_type_InetSocketAddressBase_members(Visitor *v, InetSocketAddressBase *obj, Error **errp); ++ ++bool visit_type_InetSocketAddressBase(Visitor *v, const char *name, ++ InetSocketAddressBase **obj, Error **errp); ++ ++bool visit_type_InetSocketAddress_members(Visitor *v, InetSocketAddress *obj, Error **errp); ++ ++bool visit_type_InetSocketAddress(Visitor *v, const char *name, ++ InetSocketAddress **obj, Error **errp); ++ ++bool visit_type_UnixSocketAddress_members(Visitor *v, UnixSocketAddress *obj, Error **errp); ++ ++bool visit_type_UnixSocketAddress(Visitor *v, const char *name, ++ UnixSocketAddress **obj, Error **errp); ++ ++bool visit_type_VsockSocketAddress_members(Visitor *v, VsockSocketAddress *obj, Error **errp); ++ ++bool visit_type_VsockSocketAddress(Visitor *v, const char *name, ++ VsockSocketAddress **obj, Error **errp); ++ ++bool visit_type_FdSocketAddress_members(Visitor *v, FdSocketAddress *obj, Error **errp); ++ ++bool visit_type_FdSocketAddress(Visitor *v, const char *name, ++ FdSocketAddress **obj, Error **errp); ++ ++bool visit_type_InetSocketAddressWrapper_members(Visitor *v, InetSocketAddressWrapper *obj, Error **errp); ++ ++bool visit_type_InetSocketAddressWrapper(Visitor *v, const char *name, ++ InetSocketAddressWrapper **obj, Error **errp); ++ ++bool visit_type_UnixSocketAddressWrapper_members(Visitor *v, UnixSocketAddressWrapper *obj, Error **errp); ++ ++bool visit_type_UnixSocketAddressWrapper(Visitor *v, const char *name, ++ UnixSocketAddressWrapper **obj, Error **errp); ++ ++bool visit_type_VsockSocketAddressWrapper_members(Visitor *v, VsockSocketAddressWrapper *obj, Error **errp); ++ ++bool visit_type_VsockSocketAddressWrapper(Visitor *v, const char *name, ++ VsockSocketAddressWrapper **obj, Error **errp); ++ ++bool visit_type_FdSocketAddressWrapper_members(Visitor *v, FdSocketAddressWrapper *obj, Error **errp); ++ ++bool visit_type_FdSocketAddressWrapper(Visitor *v, const char *name, ++ FdSocketAddressWrapper **obj, Error **errp); ++ ++bool visit_type_q_obj_SocketAddressLegacy_base_members(Visitor *v, q_obj_SocketAddressLegacy_base *obj, Error **errp); ++ ++bool visit_type_SocketAddressLegacy_members(Visitor *v, SocketAddressLegacy *obj, Error **errp); ++ ++bool visit_type_SocketAddressLegacy(Visitor *v, const char *name, ++ SocketAddressLegacy **obj, Error **errp); ++ ++bool visit_type_SocketAddressType(Visitor *v, const char *name, ++ SocketAddressType *obj, Error **errp); ++ ++bool visit_type_q_obj_SocketAddress_base_members(Visitor *v, q_obj_SocketAddress_base *obj, Error **errp); ++ ++bool visit_type_SocketAddress_members(Visitor *v, SocketAddress *obj, Error **errp); ++ ++bool visit_type_SocketAddress(Visitor *v, const char *name, ++ SocketAddress **obj, Error **errp); ++ ++bool visit_type_SocketAddressList(Visitor *v, const char *name, ++ SocketAddressList **obj, Error **errp); ++ ++bool visit_type_InetSocketAddressBaseList(Visitor *v, const char *name, ++ InetSocketAddressBaseList **obj, Error **errp); ++ ++#endif /* QAPI_VISIT_SOCKETS_H */ +diff --git a/include/qapi/qapi-visit-yank.h b/include/qapi/qapi-visit-yank.h +new file mode 100644 +index 00000000..3397ee66 +--- /dev/null ++++ b/include/qapi/qapi-visit-yank.h +@@ -0,0 +1,45 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_VISIT_YANK_H ++#define QAPI_VISIT_YANK_H ++ ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi-types-yank.h" ++ ++ ++bool visit_type_YankInstanceType(Visitor *v, const char *name, ++ YankInstanceType *obj, Error **errp); ++ ++bool visit_type_YankInstanceBlockNode_members(Visitor *v, YankInstanceBlockNode *obj, Error **errp); ++ ++bool visit_type_YankInstanceBlockNode(Visitor *v, const char *name, ++ YankInstanceBlockNode **obj, Error **errp); ++ ++bool visit_type_YankInstanceChardev_members(Visitor *v, YankInstanceChardev *obj, Error **errp); ++ ++bool visit_type_YankInstanceChardev(Visitor *v, const char *name, ++ YankInstanceChardev **obj, Error **errp); ++ ++bool visit_type_q_obj_YankInstance_base_members(Visitor *v, q_obj_YankInstance_base *obj, Error **errp); ++ ++bool visit_type_YankInstance_members(Visitor *v, YankInstance *obj, Error **errp); ++ ++bool visit_type_YankInstance(Visitor *v, const char *name, ++ YankInstance **obj, Error **errp); ++ ++bool visit_type_YankInstanceList(Visitor *v, const char *name, ++ YankInstanceList **obj, Error **errp); ++ ++bool visit_type_q_obj_yank_arg_members(Visitor *v, q_obj_yank_arg *obj, Error **errp); ++ ++#endif /* QAPI_VISIT_YANK_H */ +diff --git a/include/qapi/qmp-event.h b/include/qapi/qmp-event.h +new file mode 100644 +index 00000000..b60f1d3a +--- /dev/null ++++ b/include/qapi/qmp-event.h +@@ -0,0 +1,18 @@ ++/* ++ * QMP Event related ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * ++ * Authors: ++ * Wenchao Xia ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QMP_EVENT_H ++#define QMP_EVENT_H ++ ++QDict *qmp_event_build_dict(const char *event_name); ++#endif +diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h +new file mode 100644 +index 00000000..f2e95681 +--- /dev/null ++++ b/include/qapi/qmp/dispatch.h +@@ -0,0 +1,67 @@ ++/* ++ * Core Definitions for QAPI/QMP Dispatch ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QAPI_QMP_DISPATCH_H ++#define QAPI_QMP_DISPATCH_H ++ ++#include "monitor/monitor.h" ++#include "qemu/queue.h" ++ ++typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); ++ ++typedef enum QmpCommandOptions ++{ ++ QCO_NO_SUCCESS_RESP = (1U << 0), ++ QCO_ALLOW_OOB = (1U << 1), ++ QCO_ALLOW_PRECONFIG = (1U << 2), ++ QCO_COROUTINE = (1U << 3), ++} QmpCommandOptions; ++ ++typedef struct QmpCommand ++{ ++ const char *name; ++ /* Runs in coroutine context if QCO_COROUTINE is set */ ++ QmpCommandFunc *fn; ++ QmpCommandOptions options; ++ unsigned special_features; ++ QTAILQ_ENTRY(QmpCommand) node; ++ bool enabled; ++ const char *disable_reason; ++} QmpCommand; ++ ++typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; ++ ++void qmp_register_command(QmpCommandList *cmds, const char *name, ++ QmpCommandFunc *fn, QmpCommandOptions options, ++ unsigned special_features); ++const QmpCommand *qmp_find_command(const QmpCommandList *cmds, ++ const char *name); ++void qmp_disable_command(QmpCommandList *cmds, const char *name, ++ const char *err_msg); ++void qmp_enable_command(QmpCommandList *cmds, const char *name); ++ ++bool qmp_command_is_enabled(const QmpCommand *cmd); ++bool qmp_command_available(const QmpCommand *cmd, Error **errp); ++const char *qmp_command_name(const QmpCommand *cmd); ++bool qmp_has_success_response(const QmpCommand *cmd); ++QDict *qmp_error_response(Error *err); ++QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *request, ++ bool allow_oob, Monitor *cur_mon); ++bool qmp_is_oob(const QDict *dict); ++ ++typedef void (*qmp_cmd_callback_fn)(const QmpCommand *cmd, void *opaque); ++ ++void qmp_for_each_command(const QmpCommandList *cmds, qmp_cmd_callback_fn fn, ++ void *opaque); ++ ++#endif +diff --git a/include/qapi/qmp/json-parser.h b/include/qapi/qmp/json-parser.h +new file mode 100644 +index 00000000..7345a9bd +--- /dev/null ++++ b/include/qapi/qmp/json-parser.h +@@ -0,0 +1,46 @@ ++/* ++ * JSON Parser ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QAPI_QMP_JSON_PARSER_H ++#define QAPI_QMP_JSON_PARSER_H ++ ++typedef struct JSONLexer { ++ int start_state, state; ++ GString *token; ++ int x, y; ++} JSONLexer; ++ ++typedef struct JSONMessageParser { ++ void (*emit)(void *opaque, QObject *json, Error *err); ++ void *opaque; ++ va_list *ap; ++ JSONLexer lexer; ++ int brace_count; ++ int bracket_count; ++ GQueue tokens; ++ uint64_t token_size; ++} JSONMessageParser; ++ ++void json_message_parser_init(JSONMessageParser *parser, ++ void (*emit)(void *opaque, QObject *json, ++ Error *err), ++ void *opaque, va_list *ap); ++ ++void json_message_parser_feed(JSONMessageParser *parser, ++ const char *buffer, size_t size); ++ ++void json_message_parser_flush(JSONMessageParser *parser); ++ ++void json_message_parser_destroy(JSONMessageParser *parser); ++ ++#endif +diff --git a/include/qapi/qmp/json-writer.h b/include/qapi/qmp/json-writer.h +new file mode 100644 +index 00000000..b70ba640 +--- /dev/null ++++ b/include/qapi/qmp/json-writer.h +@@ -0,0 +1,35 @@ ++/* ++ * JSON Writer ++ * ++ * Copyright (c) 2020 Red Hat Inc. ++ * ++ * Authors: ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef JSON_WRITER_H ++#define JSON_WRITER_H ++ ++JSONWriter *json_writer_new(bool pretty); ++const char *json_writer_get(JSONWriter *); ++GString *json_writer_get_and_free(JSONWriter *); ++void json_writer_free(JSONWriter *); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(JSONWriter, json_writer_free) ++ ++void json_writer_start_object(JSONWriter *, const char *name); ++void json_writer_end_object(JSONWriter *); ++void json_writer_start_array(JSONWriter *, const char *name); ++void json_writer_end_array(JSONWriter *); ++void json_writer_bool(JSONWriter *, const char *name, bool val); ++void json_writer_null(JSONWriter *, const char *name); ++void json_writer_int64(JSONWriter *, const char *name, int64_t val); ++void json_writer_uint64(JSONWriter *, const char *name, uint64_t val); ++void json_writer_double(JSONWriter *, const char *name, double val); ++void json_writer_str(JSONWriter *, const char *name, const char *str); ++ ++#endif +diff --git a/include/qapi/qmp/qbool.h b/include/qapi/qmp/qbool.h +new file mode 100644 +index 00000000..0d097269 +--- /dev/null ++++ b/include/qapi/qmp/qbool.h +@@ -0,0 +1,31 @@ ++/* ++ * QBool Module ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QBOOL_H ++#define QBOOL_H ++ ++#include "qapi/qmp/qobject.h" ++ ++struct QBool { ++ struct QObjectBase_ base; ++ bool value; ++}; ++ ++void qbool_unref(QBool *q); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QBool, qbool_unref) ++ ++QBool *qbool_from_bool(bool value); ++bool qbool_get_bool(const QBool *qb); ++ ++#endif /* QBOOL_H */ +diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h +new file mode 100644 +index 00000000..82e90fc0 +--- /dev/null ++++ b/include/qapi/qmp/qdict.h +@@ -0,0 +1,71 @@ ++/* ++ * QDict Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QDICT_H ++#define QDICT_H ++ ++#include "qapi/qmp/qobject.h" ++#include "qemu/queue.h" ++ ++#define QDICT_BUCKET_MAX 512 ++ ++typedef struct QDictEntry { ++ char *key; ++ QObject *value; ++ QLIST_ENTRY(QDictEntry) next; ++} QDictEntry; ++ ++struct QDict { ++ struct QObjectBase_ base; ++ size_t size; ++ QLIST_HEAD(,QDictEntry) table[QDICT_BUCKET_MAX]; ++}; ++ ++void qdict_unref(QDict *q); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QDict, qdict_unref) ++ ++/* Object API */ ++QDict *qdict_new(void); ++const char *qdict_entry_key(const QDictEntry *entry); ++QObject *qdict_entry_value(const QDictEntry *entry); ++size_t qdict_size(const QDict *qdict); ++void qdict_put_obj(QDict *qdict, const char *key, QObject *value); ++void qdict_del(QDict *qdict, const char *key); ++int qdict_haskey(const QDict *qdict, const char *key); ++QObject *qdict_get(const QDict *qdict, const char *key); ++const QDictEntry *qdict_first(const QDict *qdict); ++const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry); ++ ++/* Helper to qdict_put_obj(), accepts any object */ ++#define qdict_put(qdict, key, obj) \ ++ qdict_put_obj(qdict, key, QOBJECT(obj)) ++ ++void qdict_put_bool(QDict *qdict, const char *key, bool value); ++void qdict_put_int(QDict *qdict, const char *key, int64_t value); ++void qdict_put_null(QDict *qdict, const char *key); ++void qdict_put_str(QDict *qdict, const char *key, const char *value); ++ ++double qdict_get_double(const QDict *qdict, const char *key); ++int64_t qdict_get_int(const QDict *qdict, const char *key); ++bool qdict_get_bool(const QDict *qdict, const char *key); ++QList *qdict_get_qlist(const QDict *qdict, const char *key); ++QDict *qdict_get_qdict(const QDict *qdict, const char *key); ++const char *qdict_get_str(const QDict *qdict, const char *key); ++int64_t qdict_get_try_int(const QDict *qdict, const char *key, ++ int64_t def_value); ++bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value); ++const char *qdict_get_try_str(const QDict *qdict, const char *key); ++ ++QDict *qdict_clone_shallow(const QDict *src); ++ ++#endif /* QDICT_H */ +diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h +new file mode 100644 +index 00000000..38e89762 +--- /dev/null ++++ b/include/qapi/qmp/qerror.h +@@ -0,0 +1,32 @@ ++/* ++ * QError Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++#ifndef QERROR_H ++#define QERROR_H ++ ++/* ++ * These macros will go away, please don't use in new code, and do not ++ * add new ones! ++ */ ++ ++#define QERR_INVALID_PARAMETER_VALUE \ ++ "Parameter '%s' expects %s" ++ ++#define QERR_MISSING_PARAMETER \ ++ "Parameter '%s' is missing" ++ ++#define QERR_PROPERTY_VALUE_OUT_OF_RANGE \ ++ "Property %s.%s doesn't take value %" PRId64 " (minimum: %" PRId64 ", maximum: %" PRId64 ")" ++ ++#define QERR_UNSUPPORTED \ ++ "this feature or command is not currently supported" ++ ++#endif /* QERROR_H */ +diff --git a/include/qapi/qmp/qjson.h b/include/qapi/qmp/qjson.h +new file mode 100644 +index 00000000..7bd8d2de +--- /dev/null ++++ b/include/qapi/qmp/qjson.h +@@ -0,0 +1,31 @@ ++/* ++ * QObject JSON integration ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QJSON_H ++#define QJSON_H ++ ++QObject *qobject_from_json(const char *string, Error **errp); ++ ++QObject *qobject_from_vjsonf_nofail(const char *string, va_list ap) ++ G_GNUC_PRINTF(1, 0); ++QObject *qobject_from_jsonf_nofail(const char *string, ...) ++ G_GNUC_PRINTF(1, 2); ++QDict *qdict_from_vjsonf_nofail(const char *string, va_list ap) ++ G_GNUC_PRINTF(1, 0); ++QDict *qdict_from_jsonf_nofail(const char *string, ...) ++ G_GNUC_PRINTF(1, 2); ++ ++GString *qobject_to_json(const QObject *obj); ++GString *qobject_to_json_pretty(const QObject *obj, bool pretty); ++ ++#endif /* QJSON_H */ +diff --git a/include/qapi/qmp/qlist.h b/include/qapi/qmp/qlist.h +new file mode 100644 +index 00000000..e4e985d4 +--- /dev/null ++++ b/include/qapi/qmp/qlist.h +@@ -0,0 +1,69 @@ ++/* ++ * QList Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QLIST_H ++#define QLIST_H ++ ++#include "qapi/qmp/qobject.h" ++#include "qemu/queue.h" ++ ++typedef struct QListEntry { ++ QObject *value; ++ QTAILQ_ENTRY(QListEntry) next; ++} QListEntry; ++ ++struct QList { ++ struct QObjectBase_ base; ++ QTAILQ_HEAD(,QListEntry) head; ++}; ++ ++void qlist_unref(QList *q); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QList, qlist_unref) ++ ++#define qlist_append(qlist, obj) \ ++ qlist_append_obj(qlist, QOBJECT(obj)) ++ ++void qlist_append_bool(QList *qlist, bool value); ++void qlist_append_int(QList *qlist, int64_t value); ++void qlist_append_null(QList *qlist); ++void qlist_append_str(QList *qlist, const char *value); ++ ++#define QLIST_FOREACH_ENTRY(qlist, var) \ ++ for ((var) = QTAILQ_FIRST(&(qlist)->head); \ ++ (var); \ ++ (var) = QTAILQ_NEXT((var), next)) ++ ++static inline QObject *qlist_entry_obj(const QListEntry *entry) ++{ ++ return entry->value; ++} ++ ++QList *qlist_new(void); ++QList *qlist_copy(QList *src); ++void qlist_append_obj(QList *qlist, QObject *obj); ++QObject *qlist_pop(QList *qlist); ++QObject *qlist_peek(QList *qlist); ++int qlist_empty(const QList *qlist); ++size_t qlist_size(const QList *qlist); ++ ++static inline const QListEntry *qlist_first(const QList *qlist) ++{ ++ return QTAILQ_FIRST(&qlist->head); ++} ++ ++static inline const QListEntry *qlist_next(const QListEntry *entry) ++{ ++ return QTAILQ_NEXT(entry, next); ++} ++ ++#endif /* QLIST_H */ +diff --git a/include/qapi/qmp/qnull.h b/include/qapi/qmp/qnull.h +new file mode 100644 +index 00000000..7feb7c7d +--- /dev/null ++++ b/include/qapi/qmp/qnull.h +@@ -0,0 +1,33 @@ ++/* ++ * QNull ++ * ++ * Copyright (C) 2015 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QNULL_H ++#define QNULL_H ++ ++#include "qapi/qmp/qobject.h" ++ ++struct QNull { ++ struct QObjectBase_ base; ++}; ++ ++extern QNull qnull_; ++ ++static inline QNull *qnull(void) ++{ ++ return qobject_ref(&qnull_); ++} ++ ++void qnull_unref(QNull *q); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QNull, qnull_unref) ++ ++#endif /* QNULL_H */ +diff --git a/include/qapi/qmp/qnum.h b/include/qapi/qmp/qnum.h +new file mode 100644 +index 00000000..e86788dd +--- /dev/null ++++ b/include/qapi/qmp/qnum.h +@@ -0,0 +1,75 @@ ++/* ++ * QNum Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * Anthony Liguori ++ * Marc-André Lureau ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QNUM_H ++#define QNUM_H ++ ++#include "qapi/qmp/qobject.h" ++ ++typedef enum { ++ QNUM_I64, ++ QNUM_U64, ++ QNUM_DOUBLE ++} QNumKind; ++ ++/* ++ * QNum encapsulates how our dialect of JSON fills in the blanks left ++ * by the JSON specification (RFC 8259) regarding numbers. ++ * ++ * Conceptually, we treat number as an abstract type with three ++ * concrete subtypes: floating-point, signed integer, unsigned ++ * integer. QNum implements this as a discriminated union of double, ++ * int64_t, uint64_t. ++ * ++ * The JSON parser picks the subtype as follows. If the number has a ++ * decimal point or an exponent, it is floating-point. Else if it ++ * fits into int64_t, it's signed integer. Else if it fits into ++ * uint64_t, it's unsigned integer. Else it's floating-point. ++ * ++ * Any number can serve as double: qnum_get_double() converts under ++ * the hood. ++ * ++ * An integer can serve as signed / unsigned integer as long as it is ++ * in range: qnum_get_try_int() / qnum_get_try_uint() check range and ++ * convert under the hood. ++ */ ++struct QNum { ++ struct QObjectBase_ base; ++ QNumKind kind; ++ union { ++ int64_t i64; ++ uint64_t u64; ++ double dbl; ++ } u; ++}; ++ ++void qnum_unref(QNum *q); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QNum, qnum_unref) ++ ++QNum *qnum_from_int(int64_t value); ++QNum *qnum_from_uint(uint64_t value); ++QNum *qnum_from_double(double value); ++ ++bool qnum_get_try_int(const QNum *qn, int64_t *val); ++int64_t qnum_get_int(const QNum *qn); ++ ++bool qnum_get_try_uint(const QNum *qn, uint64_t *val); ++uint64_t qnum_get_uint(const QNum *qn); ++ ++double qnum_get_double(QNum *qn); ++ ++char *qnum_to_string(QNum *qn); ++ ++#endif /* QNUM_H */ +diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h +new file mode 100644 +index 00000000..256d7826 +--- /dev/null ++++ b/include/qapi/qmp/qobject.h +@@ -0,0 +1,144 @@ ++/* ++ * QEMU Object Model. ++ * ++ * Based on ideas by Avi Kivity ++ * ++ * Copyright (C) 2009, 2015 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ * QObject Reference Counts Terminology ++ * ------------------------------------ ++ * ++ * - Returning references: A function that returns an object may ++ * return it as either a weak or a strong reference. If the ++ * reference is strong, you are responsible for calling ++ * qobject_unref() on the reference when you are done. ++ * ++ * If the reference is weak, the owner of the reference may free it at ++ * any time in the future. Before storing the reference anywhere, you ++ * should call qobject_ref() to make the reference strong. ++ * ++ * - Transferring ownership: when you transfer ownership of a reference ++ * by calling a function, you are no longer responsible for calling ++ * qobject_unref() when the reference is no longer needed. In other words, ++ * when the function returns you must behave as if the reference to the ++ * passed object was weak. ++ */ ++#ifndef QOBJECT_H ++#define QOBJECT_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++/* Not for use outside include/qapi/qmp/ */ ++struct QObjectBase_ { ++ QType type; ++ size_t refcnt; ++}; ++ ++/* this struct must have no other members than base */ ++struct QObject { ++ struct QObjectBase_ base; ++}; ++ ++/* ++ * Preprocessor sorcery ahead: use a different identifier for the ++ * local variable in each expansion, so we can nest macro calls ++ * without shadowing variables. ++ */ ++#define QOBJECT_INTERNAL(obj, _obj) ({ \ ++ typeof(obj) _obj = (obj); \ ++ _obj ? container_of(&_obj->base, QObject, base) : NULL; \ ++}) ++#define QOBJECT(obj) QOBJECT_INTERNAL((obj), MAKE_IDENTIFIER(_obj)) ++ ++/* Required for qobject_to() */ ++#define QTYPE_CAST_TO_QNull QTYPE_QNULL ++#define QTYPE_CAST_TO_QNum QTYPE_QNUM ++#define QTYPE_CAST_TO_QString QTYPE_QSTRING ++#define QTYPE_CAST_TO_QDict QTYPE_QDICT ++#define QTYPE_CAST_TO_QList QTYPE_QLIST ++#define QTYPE_CAST_TO_QBool QTYPE_QBOOL ++ ++QEMU_BUILD_BUG_MSG(QTYPE__MAX != 7, ++ "The QTYPE_CAST_TO_* list needs to be extended"); ++ ++#define qobject_to(type, obj) \ ++ ((type *)qobject_check_type(obj, glue(QTYPE_CAST_TO_, type))) ++ ++static inline void qobject_ref_impl(QObject *obj) ++{ ++ if (obj) { ++ obj->base.refcnt++; ++ } ++} ++ ++/** ++ * qobject_is_equal(): Return whether the two objects are equal. ++ * ++ * Any of the pointers may be NULL; return true if both are. Always ++ * return false if only one is (therefore a QNull object is not ++ * considered equal to a NULL pointer). ++ */ ++bool qobject_is_equal(const QObject *x, const QObject *y); ++ ++/** ++ * qobject_destroy(): Free resources used by the object ++ * For use via qobject_unref() only! ++ */ ++void qobject_destroy(QObject *obj); ++ ++static inline void qobject_unref_impl(QObject *obj) ++{ ++ assert(!obj || obj->base.refcnt); ++ if (obj && --obj->base.refcnt == 0) { ++ qobject_destroy(obj); ++ } ++} ++ ++/** ++ * qobject_ref(): Increment QObject's reference count ++ * ++ * Returns: the same @obj. The type of @obj will be propagated to the ++ * return type. ++ */ ++#define qobject_ref(obj) ({ \ ++ typeof(obj) _o = (obj); \ ++ qobject_ref_impl(QOBJECT(_o)); \ ++ _o; \ ++}) ++ ++/** ++ * qobject_unref(): Decrement QObject's reference count, deallocate ++ * when it reaches zero ++ */ ++#define qobject_unref(obj) qobject_unref_impl(QOBJECT(obj)) ++ ++/** ++ * qobject_type(): Return the QObject's type ++ */ ++static inline QType qobject_type(const QObject *obj) ++{ ++ assert(QTYPE_NONE < obj->base.type && obj->base.type < QTYPE__MAX); ++ return obj->base.type; ++} ++ ++/** ++ * qobject_check_type(): Helper function for the qobject_to() macro. ++ * Return @obj, but only if @obj is not NULL and @type is equal to ++ * @obj's type. Return NULL otherwise. ++ */ ++static inline QObject *qobject_check_type(const QObject *obj, QType type) ++{ ++ if (obj && qobject_type(obj) == type) { ++ return (QObject *)obj; ++ } else { ++ return NULL; ++ } ++} ++ ++#endif /* QOBJECT_H */ +diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h +new file mode 100644 +index 00000000..318d815d +--- /dev/null ++++ b/include/qapi/qmp/qstring.h +@@ -0,0 +1,33 @@ ++/* ++ * QString Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QSTRING_H ++#define QSTRING_H ++ ++#include "qapi/qmp/qobject.h" ++ ++struct QString { ++ struct QObjectBase_ base; ++ const char *string; ++}; ++ ++void qstring_unref(QString *q); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QString, qstring_unref) ++ ++QString *qstring_new(void); ++QString *qstring_from_str(const char *str); ++QString *qstring_from_substr(const char *str, size_t start, size_t end); ++QString *qstring_from_gstring(GString *gstr); ++const char *qstring_get_str(const QString *qstring); ++ ++#endif /* QSTRING_H */ +diff --git a/include/qapi/qobject-input-visitor.h b/include/qapi/qobject-input-visitor.h +new file mode 100644 +index 00000000..95985e25 +--- /dev/null ++++ b/include/qapi/qobject-input-visitor.h +@@ -0,0 +1,82 @@ ++/* ++ * Input Visitor ++ * ++ * Copyright (C) 2017 Red Hat, Inc. ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QOBJECT_INPUT_VISITOR_H ++#define QOBJECT_INPUT_VISITOR_H ++ ++#include "qapi/visitor.h" ++ ++typedef struct QObjectInputVisitor QObjectInputVisitor; ++ ++/* ++ * Create a QObject input visitor for @obj ++ * ++ * A QObject input visitor visit builds a QAPI object from a QObject. ++ * This simultaneously walks the QAPI object being built and the ++ * QObject. The latter walk starts at @obj. ++ * ++ * visit_type_FOO() creates an instance of QAPI type FOO. The visited ++ * QObject must match FOO. QDict matches struct/union types, QList ++ * matches list types, QString matches type 'str' and enumeration ++ * types, QNum matches integer and float types, QBool matches type ++ * 'bool'. Type 'any' is matched by QObject. A QAPI alternate type ++ * is matched when one of its member types is. ++ * ++ * visit_start_struct() ... visit_end_struct() visits a QDict and ++ * creates a QAPI struct/union. Visits in between visit the ++ * dictionary members. visit_optional() is true when the QDict has ++ * this member. visit_check_struct() fails if unvisited members ++ * remain. ++ * ++ * visit_start_list() ... visit_end_list() visits a QList and creates ++ * a QAPI list. Visits in between visit list members, one after the ++ * other. visit_next_list() returns NULL when all QList members have ++ * been visited. visit_check_list() fails if unvisited members ++ * remain. ++ * ++ * visit_start_alternate() ... visit_end_alternate() visits a QObject ++ * and creates a QAPI alternate. The visit in between visits the same ++ * QObject and initializes the alternate member that is in use. ++ * ++ * Error messages refer to parts of @obj in JavaScript/Python syntax. ++ * For example, 'a.b[2]' refers to the second member of the QList ++ * member 'b' of the QDict member 'a' of QDict @obj. ++ * ++ * The caller is responsible for freeing the visitor with ++ * visit_free(). ++ */ ++Visitor *qobject_input_visitor_new(QObject *obj); ++ ++/* ++ * Create a QObject input visitor for @obj for use with keyval_parse() ++ * ++ * This is like qobject_input_visitor_new(), except scalars are all ++ * QString, and error messages refer to parts of @obj in the syntax ++ * keyval_parse() uses for KEYs. ++ */ ++Visitor *qobject_input_visitor_new_keyval(QObject *obj); ++ ++/* ++ * Create a QObject input visitor for parsing @str. ++ * ++ * If @str looks like JSON, parse it as JSON, else as KEY=VALUE,... ++ * @implied_key applies to KEY=VALUE, and works as in keyval_parse(). ++ * On failure, store an error through @errp and return NULL. ++ * On success, return a new QObject input visitor for the parse. ++ */ ++Visitor *qobject_input_visitor_new_str(const char *str, ++ const char *implied_key, ++ Error **errp); ++ ++#endif +diff --git a/include/qapi/qobject-output-visitor.h b/include/qapi/qobject-output-visitor.h +new file mode 100644 +index 00000000..2b1726ba +--- /dev/null ++++ b/include/qapi/qobject-output-visitor.h +@@ -0,0 +1,56 @@ ++/* ++ * Output Visitor ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QOBJECT_OUTPUT_VISITOR_H ++#define QOBJECT_OUTPUT_VISITOR_H ++ ++#include "qapi/visitor.h" ++ ++typedef struct QObjectOutputVisitor QObjectOutputVisitor; ++ ++/** ++ * Create a QObject output visitor for @obj ++ * ++ * A QObject output visitor visit builds a QObject from QAPI Object. ++ * This simultaneously walks the QAPI object and the QObject being ++ * built. The latter walk starts at @obj. ++ * ++ * visit_type_FOO() creates a QObject for QAPI type FOO. It creates a ++ * QDict for struct/union types, a QList for list types, QString for ++ * type 'str' and enumeration types, QNum for integer and float ++ * types, QBool for type 'bool'. For type 'any', it increments the ++ * QObject's reference count. For QAPI alternate types, it creates ++ * the QObject for the member that is in use. ++ * ++ * visit_start_struct() ... visit_end_struct() visits a QAPI ++ * struct/union and creates a QDict. Visits in between visit the ++ * members. visit_optional() is true when the struct/union has this ++ * member. visit_check_struct() does nothing. ++ * ++ * visit_start_list() ... visit_end_list() visits a QAPI list and ++ * creates a QList. Visits in between visit list members, one after ++ * the other. visit_next_list() returns NULL when all QAPI list ++ * members have been visited. visit_check_list() does nothing. ++ * ++ * visit_start_alternate() ... visit_end_alternate() visits a QAPI ++ * alternate. The visit in between creates the QObject for the ++ * alternate member that is in use. ++ * ++ * Errors are not expected to happen. ++ * ++ * The caller is responsible for freeing the visitor with ++ * visit_free(). ++ */ ++Visitor *qobject_output_visitor_new(QObject **result); ++ ++#endif +diff --git a/include/qapi/string-input-visitor.h b/include/qapi/string-input-visitor.h +new file mode 100644 +index 00000000..921f3875 +--- /dev/null ++++ b/include/qapi/string-input-visitor.h +@@ -0,0 +1,27 @@ ++/* ++ * String parsing Visitor ++ * ++ * Copyright Red Hat, Inc. 2012 ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef STRING_INPUT_VISITOR_H ++#define STRING_INPUT_VISITOR_H ++ ++#include "qapi/visitor.h" ++ ++typedef struct StringInputVisitor StringInputVisitor; ++ ++/* ++ * The string input visitor does not implement support for visiting ++ * QAPI structs, alternates, null, or arbitrary QTypes. Only flat lists ++ * of integers (except type "size") are supported. ++ */ ++Visitor *string_input_visitor_new(const char *str); ++ ++#endif +diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h +new file mode 100644 +index 00000000..b1ee473b +--- /dev/null ++++ b/include/qapi/string-output-visitor.h +@@ -0,0 +1,35 @@ ++/* ++ * String printing Visitor ++ * ++ * Copyright Red Hat, Inc. 2012 ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef STRING_OUTPUT_VISITOR_H ++#define STRING_OUTPUT_VISITOR_H ++ ++#include "qapi/visitor.h" ++ ++typedef struct StringOutputVisitor StringOutputVisitor; ++ ++/* ++ * Create a new string output visitor. ++ * ++ * Using @human creates output that is a bit easier for humans to read ++ * (for example, showing integer values in both decimal and hex). ++ * ++ * If everything else succeeds, pass @result to visit_complete() to ++ * collect the result of the visit. ++ * ++ * The string output visitor does not implement support for alternates, null, ++ * or arbitrary QTypes. Struct fields are not shown. It also requires a ++ * non-null list argument to visit_start_list(). ++ */ ++Visitor *string_output_visitor_new(bool human, char **result); ++ ++#endif +diff --git a/include/qapi/util.h b/include/qapi/util.h +new file mode 100644 +index 00000000..b8254247 +--- /dev/null ++++ b/include/qapi/util.h +@@ -0,0 +1,72 @@ ++/* ++ * QAPI util functions ++ * ++ * Copyright Fujitsu, Inc. 2014 ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QAPI_UTIL_H ++#define QAPI_UTIL_H ++ ++typedef enum { ++ QAPI_DEPRECATED, ++ QAPI_UNSTABLE, ++} QapiSpecialFeature; ++ ++typedef struct QEnumLookup { ++ const char *const *array; ++ const unsigned char *const special_features; ++ const int size; ++} QEnumLookup; ++ ++const char *qapi_enum_lookup(const QEnumLookup *lookup, int val); ++int qapi_enum_parse(const QEnumLookup *lookup, const char *buf, ++ int def, Error **errp); ++bool qapi_bool_parse(const char *name, const char *value, bool *obj, ++ Error **errp); ++ ++int parse_qapi_name(const char *name, bool complete); ++ ++/* ++ * For any GenericList @list, insert @element at the front. ++ * ++ * Note that this macro evaluates @element exactly once, so it is safe ++ * to have side-effects with that argument. ++ */ ++#define QAPI_LIST_PREPEND(list, element) do { \ ++ typeof(list) _tmp = g_malloc(sizeof(*(list))); \ ++ _tmp->value = (element); \ ++ _tmp->next = (list); \ ++ (list) = _tmp; \ ++} while (0) ++ ++/* ++ * For any pointer to a GenericList @tail (usually the 'next' member of a ++ * list element), insert @element at the back and update the tail. ++ * ++ * Note that this macro evaluates @element exactly once, so it is safe ++ * to have side-effects with that argument. ++ */ ++#define QAPI_LIST_APPEND(tail, element) do { \ ++ *(tail) = g_malloc0(sizeof(**(tail))); \ ++ (*(tail))->value = (element); \ ++ (tail) = &(*(tail))->next; \ ++} while (0) ++ ++/* ++ * For any GenericList @list, return its length. ++ */ ++#define QAPI_LIST_LENGTH(list) \ ++ ({ \ ++ size_t _len = 0; \ ++ typeof_strip_qual(list) _tail; \ ++ for (_tail = list; _tail != NULL; _tail = _tail->next) { \ ++ _len++; \ ++ } \ ++ _len; \ ++ }) ++ ++#endif +diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h +new file mode 100644 +index 00000000..2badec5b +--- /dev/null ++++ b/include/qapi/visitor-impl.h +@@ -0,0 +1,137 @@ ++/* ++ * Core Definitions for QAPI Visitor implementations ++ * ++ * Copyright (C) 2012-2016 Red Hat, Inc. ++ * ++ * Author: Paolo Bonizni ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++#ifndef QAPI_VISITOR_IMPL_H ++#define QAPI_VISITOR_IMPL_H ++ ++#include "qapi/visitor.h" ++ ++/* ++ * This file describes the callback interface for implementing a QAPI ++ * visitor. For the client interface, see visitor.h. When ++ * implementing the callbacks, it is easiest to declare a struct with ++ * 'Visitor visitor;' as the first member. A callback's contract ++ * matches the corresponding public functions' contract unless stated ++ * otherwise. In the comments below, some callbacks are marked "must ++ * be set for $TYPE visits to work"; if a visitor implementation omits ++ * that callback, it should also document that it is only useful for a ++ * subset of QAPI. ++ */ ++ ++/* ++ * There are four classes of visitors; setting the class determines ++ * how QAPI enums are visited, as well as what additional restrictions ++ * can be asserted. The values are intentionally chosen so as to ++ * permit some assertions based on whether a given bit is set (that ++ * is, some assertions apply to input and clone visitors, some ++ * assertions apply to output and clone visitors). ++ */ ++typedef enum VisitorType { ++ VISITOR_INPUT = 1, ++ VISITOR_OUTPUT = 2, ++ VISITOR_CLONE = 3, ++ VISITOR_DEALLOC = 4, ++} VisitorType; ++ ++struct Visitor ++{ ++ /* ++ * Only input visitors may fail! ++ */ ++ ++ /* Must be set to visit structs */ ++ bool (*start_struct)(Visitor *v, const char *name, void **obj, ++ size_t size, Error **errp); ++ ++ /* Optional; intended for input visitors */ ++ bool (*check_struct)(Visitor *v, Error **errp); ++ ++ /* Must be set to visit structs */ ++ void (*end_struct)(Visitor *v, void **obj); ++ ++ /* Must be set; implementations may require @list to be non-null, ++ * but must document it. */ ++ bool (*start_list)(Visitor *v, const char *name, GenericList **list, ++ size_t size, Error **errp); ++ ++ /* Must be set */ ++ GenericList *(*next_list)(Visitor *v, GenericList *tail, size_t size); ++ ++ /* Optional; intended for input visitors */ ++ bool (*check_list)(Visitor *v, Error **errp); ++ ++ /* Must be set */ ++ void (*end_list)(Visitor *v, void **list); ++ ++ /* Must be set by input and clone visitors to visit alternates */ ++ bool (*start_alternate)(Visitor *v, const char *name, ++ GenericAlternate **obj, size_t size, ++ Error **errp); ++ ++ /* Optional */ ++ void (*end_alternate)(Visitor *v, void **obj); ++ ++ /* Must be set */ ++ bool (*type_int64)(Visitor *v, const char *name, int64_t *obj, ++ Error **errp); ++ ++ /* Must be set */ ++ bool (*type_uint64)(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp); ++ ++ /* Optional; fallback is type_uint64() */ ++ bool (*type_size)(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp); ++ ++ /* Must be set */ ++ bool (*type_bool)(Visitor *v, const char *name, bool *obj, Error **errp); ++ ++ /* Must be set */ ++ bool (*type_str)(Visitor *v, const char *name, char **obj, Error **errp); ++ ++ /* Must be set to visit numbers */ ++ bool (*type_number)(Visitor *v, const char *name, double *obj, ++ Error **errp); ++ ++ /* Must be set to visit arbitrary QTypes */ ++ bool (*type_any)(Visitor *v, const char *name, QObject **obj, ++ Error **errp); ++ ++ /* Must be set to visit explicit null values. */ ++ bool (*type_null)(Visitor *v, const char *name, QNull **obj, ++ Error **errp); ++ ++ /* Must be set for input visitors to visit structs, optional otherwise. ++ The core takes care of the return type in the public interface. */ ++ void (*optional)(Visitor *v, const char *name, bool *present); ++ ++ /* Optional */ ++ bool (*policy_reject)(Visitor *v, const char *name, ++ unsigned special_features, Error **errp); ++ ++ /* Optional */ ++ bool (*policy_skip)(Visitor *v, const char *name, ++ unsigned special_features); ++ ++ /* Must be set */ ++ VisitorType type; ++ ++ /* Optional */ ++ struct CompatPolicy compat_policy; ++ ++ /* Must be set for output visitors, optional otherwise. */ ++ void (*complete)(Visitor *v, void *opaque); ++ ++ /* Must be set */ ++ void (*free)(Visitor *v); ++}; ++ ++#endif +diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h +new file mode 100644 +index 00000000..27b85d47 +--- /dev/null ++++ b/include/qapi/visitor.h +@@ -0,0 +1,713 @@ ++/* ++ * Core Definitions for QAPI Visitor Classes ++ * ++ * Copyright (C) 2012-2016 Red Hat, Inc. ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QAPI_VISITOR_H ++#define QAPI_VISITOR_H ++ ++#include "qapi/qapi-builtin-types.h" ++#include "qapi/qapi-types-compat.h" ++ ++/* ++ * The QAPI schema defines both a set of C data types, and a QMP wire ++ * format. QAPI objects can contain references to other QAPI objects, ++ * resulting in a directed acyclic graph. QAPI also generates visitor ++ * functions to walk these graphs. This file represents the interface ++ * for doing work at each node of a QAPI graph; it can also be used ++ * for a virtual walk, where there is no actual QAPI C struct. ++ * ++ * There are four kinds of visitors: input visitors (QObject, string, ++ * and QemuOpts) parse an external representation and build the ++ * corresponding QAPI object, output visitors (QObject and string) ++ * take a QAPI object and generate an external representation, the ++ * dealloc visitor takes a QAPI object (possibly partially ++ * constructed) and recursively frees it, and the clone visitor ++ * performs a deep clone of a QAPI object. ++ * ++ * While the dealloc and QObject input/output visitors are general, ++ * the string, QemuOpts, and clone visitors have some implementation ++ * limitations; see the documentation for each visitor for more ++ * details on what it supports. Also, see visitor-impl.h for the ++ * callback contracts implemented by each visitor, and ++ * docs/devel/qapi-code-gen.rst for more about the QAPI code ++ * generator. ++ * ++ * All of the visitors are created via: ++ * ++ * Visitor *subtype_visitor_new(parameters...); ++ * ++ * A visitor should be used for exactly one top-level visit_type_FOO() ++ * or virtual walk; if that is successful, the caller can optionally ++ * call visit_complete() (useful only for output visits, but safe to ++ * call on all visits). Then, regardless of success or failure, the ++ * user should call visit_free() to clean up resources. It is okay to ++ * free the visitor without completing the visit, if some other error ++ * is detected in the meantime. ++ * ++ * The clone and dealloc visitor should not be used directly outside ++ * of QAPI code. Use the qapi_free_FOO() and QAPI_CLONE() instead, ++ * described below. ++ * ++ * All QAPI types have a corresponding function with a signature ++ * roughly compatible with this: ++ * ++ * bool visit_type_FOO(Visitor *v, const char *name, T obj, Error **errp); ++ * ++ * where T is FOO for scalar types, and FOO * otherwise. The scalar ++ * visitors are declared here; the remaining visitors are generated in ++ * qapi-visit-MODULE.h. ++ * ++ * The @name parameter of visit_type_FOO() describes the relation ++ * between this QAPI value and its parent container. When visiting ++ * the root of a tree, @name is ignored; when visiting a member of an ++ * object, @name is the key associated with the value; when visiting a ++ * member of a list, @name is NULL; and when visiting the member of an ++ * alternate, @name should equal the name used for visiting the ++ * alternate. ++ * ++ * The visit_type_FOO() functions take a non-null @obj argument; they ++ * allocate *@obj during input visits, leave it unchanged during ++ * output and clone visits, and free it (recursively) during a dealloc ++ * visit. ++ * ++ * Each function also takes the customary @errp argument (see ++ * qapi/error.h for details), for reporting any errors (such as if a ++ * member @name is not present, or is present but not the specified ++ * type). Only input visitors can fail. ++ * ++ * If an error is detected during visit_type_FOO() with an input ++ * visitor, then *@obj will be set to NULL for pointer types, and left ++ * unchanged for scalar types. ++ * ++ * Using an output or clone visitor with an incomplete object has ++ * undefined behavior (other than a special case for visit_type_str() ++ * treating NULL like ""), while the dealloc visitor safely handles ++ * incomplete objects. Since input visitors never produce an ++ * incomplete object, such an object is possible only by manual ++ * construction. ++ * ++ * visit_type_FOO() returns true on success, false on error. ++ * ++ * For the QAPI object types (structs, unions, and alternates), there ++ * is an additional generated function in qapi-visit-MODULE.h ++ * compatible with: ++ * ++ * bool visit_type_FOO_members(Visitor *v, FOO *obj, Error **errp); ++ * ++ * for visiting the members of a type without also allocating the QAPI ++ * struct. It also returns true on success, false on error. ++ * ++ * Additionally, QAPI pointer types (structs, unions, alternates, and ++ * lists) have a generated function in qapi-types-MODULE.h compatible ++ * with: ++ * ++ * void qapi_free_FOO(FOO *obj); ++ * ++ * Does nothing when @obj is NULL. ++ * ++ * Such objects may also be used with macro ++ * ++ * Type *QAPI_CLONE(Type, src); ++ * ++ * in order to perform a deep clone of @src. ++ * ++ * For QAPI types can that inherit from a base type, a function is ++ * generated for going from the derived type to the base type: ++ * ++ * BASE *qapi_CHILD_base(CHILD *obj); ++ * ++ * Typical input visitor usage involves: ++ * ++ * ++ * Foo *f; ++ * Error *err = NULL; ++ * Visitor *v; ++ * ++ * v = FOO_visitor_new(...); ++ * if (!visit_type_Foo(v, NULL, &f, &err)) { ++ * ...handle error... ++ * } else { ++ * ...use f... ++ * } ++ * visit_free(v); ++ * qapi_free_Foo(f); ++ * ++ * ++ * For a list, it is: ++ * ++ * FooList *l; ++ * Error *err = NULL; ++ * Visitor *v; ++ * ++ * v = FOO_visitor_new(...); ++ * if (!visit_type_FooList(v, NULL, &l, &err)) { ++ * ...handle error... ++ * } else { ++ * for ( ; l; l = l->next) { ++ * ...use l->value... ++ * } ++ * } ++ * visit_free(v); ++ * qapi_free_FooList(l); ++ * ++ * ++ * Typical output visitor usage: ++ * ++ * ++ * Foo *f = ...obtain populated object... ++ * Visitor *v; ++ * Type *result; ++ * ++ * v = FOO_visitor_new(..., &result); ++ * visit_type_Foo(v, NULL, &f, &error_abort); ++ * visit_complete(v, &result); ++ * visit_free(v); ++ * ...use result... ++ * ++ * ++ * It is also possible to use the visitors to do a virtual walk, where ++ * no actual QAPI object is present. In this situation, decisions ++ * about what needs to be walked are made by the calling code, and ++ * structured visits are split between pairs of start and end methods ++ * (where the end method must be called if the start function ++ * succeeded, even if an intermediate visit encounters an error). ++ * Thus, a virtual walk corresponding to '{ "list": [1, 2] }' looks ++ * like: ++ * ++ * ++ * Visitor *v; ++ * Error *err = NULL; ++ * bool ok = false; ++ * int value; ++ * ++ * v = FOO_visitor_new(...); ++ * if (!visit_start_struct(v, NULL, NULL, 0, &err)) { ++ * goto out; ++ * } ++ * if (!visit_start_list(v, "list", NULL, 0, &err)) { ++ * goto outobj; ++ * } ++ * value = 1; ++ * if (!visit_type_int(v, NULL, &value, &err)) { ++ * goto outlist; ++ * } ++ * value = 2; ++ * if (!visit_type_int(v, NULL, &value, &err)) { ++ * goto outlist; ++ * } ++ * ok = true; ++ * outlist: ++ * if (ok) { ++ * ok = visit_check_list(v, &err); ++ * } ++ * visit_end_list(v, NULL); ++ * if (ok) { ++ * ok = visit_check_struct(v, &err); ++ * } ++ * outobj: ++ * visit_end_struct(v, NULL); ++ * out: ++ * visit_free(v); ++ * ++ * ++ * This file provides helpers for use by the generated ++ * visit_type_FOO(): visit_optional() for the 'has_member' field ++ * associated with optional 'member' in the C struct, ++ * visit_next_list() for advancing through a FooList linked list, and ++ * visit_is_input() for cleaning up on failure. ++ */ ++ ++/*** Useful types ***/ ++ ++/* This struct is layout-compatible with all other *List structs ++ * created by the QAPI generator. It is used as a typical ++ * singly-linked list. */ ++typedef struct GenericList { ++ struct GenericList *next; ++ char padding[]; ++} GenericList; ++ ++/* This struct is layout-compatible with all Alternate types ++ * created by the QAPI generator. */ ++typedef struct GenericAlternate { ++ QType type; ++ char padding[]; ++} GenericAlternate; ++ ++/*** Visitor cleanup ***/ ++ ++/* ++ * Complete the visit, collecting any output. ++ * ++ * May only be called only once after a successful top-level ++ * visit_type_FOO() or visit_end_ITEM(), and marks the end of the ++ * visit. The @opaque pointer should match the output parameter ++ * passed to the subtype_visitor_new() used to create an output ++ * visitor, or NULL for any other visitor. Needed for output ++ * visitors, but may also be called with other visitors. ++ */ ++void visit_complete(Visitor *v, void *opaque); ++ ++/* ++ * Free @v and any resources it has tied up. ++ * ++ * May be called whether or not the visit has been successfully ++ * completed, but should not be called until a top-level ++ * visit_type_FOO() or visit_start_ITEM() has been performed on the ++ * visitor. Safe if @v is NULL. ++ */ ++void visit_free(Visitor *v); ++ ++ ++/*** Visiting structures ***/ ++ ++/* ++ * Start visiting an object @obj (struct or union). ++ * ++ * @name expresses the relationship of this object to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL for a real walk, in which case @size ++ * determines how much memory an input or clone visitor will allocate ++ * into *@obj. @obj may also be NULL for a virtual walk, in which ++ * case @size is ignored. ++ * ++ * On failure, set *@obj to NULL and store an error through @errp. ++ * Can happen only when @v is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * After visit_start_struct() succeeds, the caller may visit its ++ * members one after the other, passing the member's name and address ++ * within the struct. Finally, visit_end_struct() needs to be called ++ * with the same @obj to clean up, even if intermediate visits fail. ++ * See the examples above. ++ * ++ * FIXME Should this be named visit_start_object, since it is also ++ * used for QAPI unions, and maps to JSON objects? ++ */ ++bool visit_start_struct(Visitor *v, const char *name, void **obj, ++ size_t size, Error **errp); ++ ++/* ++ * Prepare for completing an object visit. ++ * ++ * On failure, store an error through @errp. Can happen only when @v ++ * is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * Should be called prior to visit_end_struct() if all other ++ * intermediate visit steps were successful, to allow the visitor one ++ * last chance to report errors. May be skipped on a cleanup path, ++ * where there is no need to check for further errors. ++ */ ++bool visit_check_struct(Visitor *v, Error **errp); ++ ++/* ++ * Complete an object visit started earlier. ++ * ++ * @obj must match what was passed to the paired visit_start_struct(). ++ * ++ * Must be called after any successful use of visit_start_struct(), ++ * even if intermediate processing was skipped due to errors, to allow ++ * the backend to release any resources. Destroying the visitor early ++ * with visit_free() behaves as if this was implicitly called. ++ */ ++void visit_end_struct(Visitor *v, void **obj); ++ ++ ++/*** Visiting lists ***/ ++ ++/* ++ * Start visiting a list. ++ * ++ * @name expresses the relationship of this list to its parent ++ * container; see the general description of @name above. ++ * ++ * @list must be non-NULL for a real walk, in which case @size ++ * determines how much memory an input or clone visitor will allocate ++ * into *@list (at least sizeof(GenericList)). Some visitors also ++ * allow @list to be NULL for a virtual walk, in which case @size is ++ * ignored. ++ * ++ * On failure, set *@list to NULL and store an error through @errp. ++ * Can happen only when @v is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * After visit_start_list() succeeds, the caller may visit its members ++ * one after the other. A real visit (where @list is non-NULL) uses ++ * visit_next_list() for traversing the linked list, while a virtual ++ * visit (where @list is NULL) uses other means. For each list ++ * element, call the appropriate visit_type_FOO() with name set to ++ * NULL and obj set to the address of the value member of the list ++ * element. Finally, visit_end_list() needs to be called with the ++ * same @list to clean up, even if intermediate visits fail. See the ++ * examples above. ++ */ ++bool visit_start_list(Visitor *v, const char *name, GenericList **list, ++ size_t size, Error **errp); ++ ++/* ++ * Iterate over a GenericList during a non-virtual list visit. ++ * ++ * @size represents the size of a linked list node (at least ++ * sizeof(GenericList)). ++ * ++ * @tail must not be NULL; on the first call, @tail is the value of ++ * *list after visit_start_list(), and on subsequent calls @tail must ++ * be the previously returned value. Should be called in a loop until ++ * a NULL return; for each non-NULL return, the caller then calls the ++ * appropriate visit_type_*() for the element type of the list, with ++ * that function's name parameter set to NULL and obj set to the ++ * address of @tail->value. ++ */ ++GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size); ++ ++/* ++ * Prepare for completing a list visit. ++ * ++ * On failure, store an error through @errp. Can happen only when @v ++ * is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * Should be called prior to visit_end_list() if all other ++ * intermediate visit steps were successful, to allow the visitor one ++ * last chance to report errors. May be skipped on a cleanup path, ++ * where there is no need to check for further errors. ++ */ ++bool visit_check_list(Visitor *v, Error **errp); ++ ++/* ++ * Complete a list visit started earlier. ++ * ++ * @list must match what was passed to the paired visit_start_list(). ++ * ++ * Must be called after any successful use of visit_start_list(), even ++ * if intermediate processing was skipped due to errors, to allow the ++ * backend to release any resources. Destroying the visitor early ++ * with visit_free() behaves as if this was implicitly called. ++ */ ++void visit_end_list(Visitor *v, void **list); ++ ++ ++/*** Visiting alternates ***/ ++ ++/* ++ * Start the visit of an alternate @obj. ++ * ++ * @name expresses the relationship of this alternate to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must not be NULL. Input and clone visitors use @size to ++ * determine how much memory to allocate into *@obj, then determine ++ * the qtype of the next thing to be visited, and store it in ++ * (*@obj)->type. Other visitors leave @obj unchanged. ++ * ++ * On failure, set *@obj to NULL and store an error through @errp. ++ * Can happen only when @v is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * If successful, this must be paired with visit_end_alternate() with ++ * the same @obj to clean up, even if visiting the contents of the ++ * alternate fails. ++ */ ++bool visit_start_alternate(Visitor *v, const char *name, ++ GenericAlternate **obj, size_t size, ++ Error **errp); ++ ++/* ++ * Finish visiting an alternate type. ++ * ++ * @obj must match what was passed to the paired visit_start_alternate(). ++ * ++ * Must be called after any successful use of visit_start_alternate(), ++ * even if intermediate processing was skipped due to errors, to allow ++ * the backend to release any resources. Destroying the visitor early ++ * with visit_free() behaves as if this was implicitly called. ++ * ++ */ ++void visit_end_alternate(Visitor *v, void **obj); ++ ++ ++/*** Other helpers ***/ ++ ++/* ++ * Does optional struct member @name need visiting? ++ * ++ * @name must not be NULL. This function is only useful between ++ * visit_start_struct() and visit_end_struct(), since only objects ++ * have optional keys. ++ * ++ * @present points to the address of the optional member's has_ flag. ++ * ++ * Input visitors set *@present according to input; other visitors ++ * leave it unchanged. In either case, return *@present for ++ * convenience. ++ */ ++bool visit_optional(Visitor *v, const char *name, bool *present); ++ ++/* ++ * Should we reject member @name due to policy? ++ * ++ * @special_features is the member's special features encoded as a ++ * bitset of QapiSpecialFeature. ++ * ++ * @name must not be NULL. This function is only useful between ++ * visit_start_struct() and visit_end_struct(), since only objects ++ * have deprecated members. ++ */ ++bool visit_policy_reject(Visitor *v, const char *name, ++ unsigned special_features, Error **errp); ++ ++/* ++ * ++ * Should we skip member @name due to policy? ++ * ++ * @special_features is the member's special features encoded as a ++ * bitset of QapiSpecialFeature. ++ * ++ * @name must not be NULL. This function is only useful between ++ * visit_start_struct() and visit_end_struct(), since only objects ++ * have deprecated members. ++ */ ++bool visit_policy_skip(Visitor *v, const char *name, ++ unsigned special_features); ++ ++/* ++ * Set policy for handling deprecated management interfaces. ++ * ++ * Intended use: call visit_set_policy(v, &compat_policy) when ++ * visiting management interface input or output. ++ */ ++void visit_set_policy(Visitor *v, CompatPolicy *policy); ++ ++/* ++ * Visit an enum value. ++ * ++ * @name expresses the relationship of this enum to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input visitors parse input and set *@obj to ++ * the enumeration value, leaving @obj unchanged on error; other ++ * visitors use *@obj but leave it unchanged. ++ * ++ * Currently, all input visitors parse text input, and all output ++ * visitors produce text output. The mapping between enumeration ++ * values and strings is done by the visitor core, using @lookup. ++ * ++ * On failure, store an error through @errp. Can happen only when @v ++ * is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * May call visit_type_str() under the hood, and the enum visit may ++ * fail even if the corresponding string visit succeeded; this implies ++ * that an input visitor's visit_type_str() must have no unwelcome ++ * side effects. ++ */ ++bool visit_type_enum(Visitor *v, const char *name, int *obj, ++ const QEnumLookup *lookup, Error **errp); ++ ++/* ++ * Check if visitor is an input visitor. ++ */ ++bool visit_is_input(Visitor *v); ++ ++/* ++ * Check if visitor is a dealloc visitor. ++ */ ++bool visit_is_dealloc(Visitor *v); ++ ++/*** Visiting built-in types ***/ ++ ++/* ++ * Visit an integer value. ++ * ++ * @name expresses the relationship of this integer to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input visitors set *@obj to the value; ++ * other visitors will leave *@obj unchanged. ++ * ++ * On failure, store an error through @errp. Can happen only when @v ++ * is an input visitor. ++ * ++ * Return true on success, false on failure. ++ */ ++bool visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp); ++ ++/* ++ * Visit a uint8_t value. ++ * Like visit_type_int(), except clamps the value to uint8_t range. ++ */ ++bool visit_type_uint8(Visitor *v, const char *name, uint8_t *obj, ++ Error **errp); ++ ++/* ++ * Visit a uint16_t value. ++ * Like visit_type_int(), except clamps the value to uint16_t range. ++ */ ++bool visit_type_uint16(Visitor *v, const char *name, uint16_t *obj, ++ Error **errp); ++ ++/* ++ * Visit a uint32_t value. ++ * Like visit_type_int(), except clamps the value to uint32_t range. ++ */ ++bool visit_type_uint32(Visitor *v, const char *name, uint32_t *obj, ++ Error **errp); ++ ++/* ++ * Visit a uint64_t value. ++ * Like visit_type_int(), except clamps the value to uint64_t range, ++ * that is, ensures it is unsigned. ++ */ ++bool visit_type_uint64(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp); ++ ++/* ++ * Visit an int8_t value. ++ * Like visit_type_int(), except clamps the value to int8_t range. ++ */ ++bool visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp); ++ ++/* ++ * Visit an int16_t value. ++ * Like visit_type_int(), except clamps the value to int16_t range. ++ */ ++bool visit_type_int16(Visitor *v, const char *name, int16_t *obj, ++ Error **errp); ++ ++/* ++ * Visit an int32_t value. ++ * Like visit_type_int(), except clamps the value to int32_t range. ++ */ ++bool visit_type_int32(Visitor *v, const char *name, int32_t *obj, ++ Error **errp); ++ ++/* ++ * Visit an int64_t value. ++ * Identical to visit_type_int(). ++ */ ++bool visit_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp); ++ ++/* ++ * Visit a uint64_t value. ++ * Like visit_type_uint64(), except that some visitors may choose to ++ * recognize additional syntax, such as suffixes for easily scaling ++ * values. ++ */ ++bool visit_type_size(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp); ++ ++/* ++ * Visit a boolean value. ++ * ++ * @name expresses the relationship of this boolean to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input visitors set *@obj to the value; ++ * other visitors will leave *@obj unchanged. ++ * ++ * On failure, store an error through @errp. Can happen only when @v ++ * is an input visitor. ++ * ++ * Return true on success, false on failure. ++ */ ++bool visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp); ++ ++/* ++ * Visit a string value. ++ * ++ * @name expresses the relationship of this string to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input and clone visitors set *@obj to the ++ * value (always using "" rather than NULL for an empty string). ++ * Other visitors leave *@obj unchanged, and commonly treat NULL like ++ * "". ++ * ++ * It is safe to cast away const when preparing a (const char *) value ++ * into @obj for use by an output visitor. ++ * ++ * On failure, set *@obj to NULL and store an error through @errp. ++ * Can happen only when @v is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * FIXME: Callers that try to output NULL *obj should not be allowed. ++ */ ++bool visit_type_str(Visitor *v, const char *name, char **obj, Error **errp); ++ ++/* ++ * Visit a number (i.e. double) value. ++ * ++ * @name expresses the relationship of this number to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input visitors set *@obj to the value; ++ * other visitors will leave *@obj unchanged. Visitors should ++ * document if infinity or NaN are not permitted. ++ * ++ * On failure, store an error through @errp. Can happen only when @v ++ * is an input visitor. ++ * ++ * Return true on success, false on failure. ++ */ ++bool visit_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp); ++ ++/* ++ * Visit an arbitrary value. ++ * ++ * @name expresses the relationship of this value to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input visitors set *@obj to the value; ++ * other visitors will leave *@obj unchanged. *@obj must be non-NULL ++ * for output visitors. ++ * ++ * On failure, set *@obj to NULL and store an error through @errp. ++ * Can happen only when @v is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * Note that some kinds of input can't express arbitrary QObject. ++ * E.g. the visitor returned by qobject_input_visitor_new_keyval() ++ * can't create numbers or booleans, only strings. ++ */ ++bool visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp); ++ ++/* ++ * Visit a JSON null value. ++ * ++ * @name expresses the relationship of the null value to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input visitors set *@obj to the value; ++ * other visitors ignore *@obj. ++ * ++ * On failure, set *@obj to NULL and store an error through @errp. ++ * Can happen only when @v is an input visitor. ++ * ++ * Return true on success, false on failure. ++ */ ++bool visit_type_null(Visitor *v, const char *name, QNull **obj, ++ Error **errp); ++ ++#endif +diff --git a/include/qcow2.h b/include/qcow2.h +new file mode 100644 +index 00000000..a9e3481c +--- /dev/null ++++ b/include/qcow2.h +@@ -0,0 +1,1074 @@ ++/* ++ * Block driver for the QCOW version 2 format ++ * ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCK_QCOW2_H ++#define BLOCK_QCOW2_H ++ ++#include "crypto/block.h" ++#include "qemu/coroutine.h" ++#include "qemu/units.h" ++#include "block/block_int.h" ++ ++//#define DEBUG_ALLOC ++//#define DEBUG_ALLOC2 ++//#define DEBUG_EXT ++ ++#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) ++ ++#define QCOW_CRYPT_NONE 0 ++#define QCOW_CRYPT_AES 1 ++#define QCOW_CRYPT_LUKS 2 ++ ++#define QCOW_MAX_CRYPT_CLUSTERS 32 ++#define QCOW_MAX_SNAPSHOTS 65536 ++ ++/* Field widths in qcow2 mean normal cluster offsets cannot reach ++ * 64PB; depending on cluster size, compressed clusters can have a ++ * smaller limit (64PB for up to 16k clusters, then ramps down to ++ * 512TB for 2M clusters). */ ++#define QCOW_MAX_CLUSTER_OFFSET ((1ULL << 56) - 1) ++ ++/* 8 MB refcount table is enough for 2 PB images at 64k cluster size ++ * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ ++#define QCOW_MAX_REFTABLE_SIZE (8 * MiB) ++ ++/* 32 MB L1 table is enough for 2 PB images at 64k cluster size ++ * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ ++#define QCOW_MAX_L1_SIZE (32 * MiB) ++ ++/* Allow for an average of 1k per snapshot table entry, should be plenty of ++ * space for snapshot names and IDs */ ++#define QCOW_MAX_SNAPSHOTS_SIZE (1024 * QCOW_MAX_SNAPSHOTS) ++ ++/* Maximum amount of extra data per snapshot table entry to accept */ ++#define QCOW_MAX_SNAPSHOT_EXTRA_DATA 1024 ++ ++/* Bitmap header extension constraints */ ++#define QCOW2_MAX_BITMAPS 65535 ++#define QCOW2_MAX_BITMAP_DIRECTORY_SIZE (1024 * QCOW2_MAX_BITMAPS) ++ ++/* Maximum of parallel sub-request per guest request */ ++#define QCOW2_MAX_WORKERS 8 ++ ++/* indicate that the refcount of the referenced cluster is exactly one. */ ++#define QCOW_OFLAG_COPIED (1ULL << 63) ++/* indicate that the cluster is compressed (they never have the copied flag) */ ++#define QCOW_OFLAG_COMPRESSED (1ULL << 62) ++/* The cluster reads as all zeros */ ++#define QCOW_OFLAG_ZERO (1ULL << 0) ++ ++#define QCOW_EXTL2_SUBCLUSTERS_PER_CLUSTER 32 ++ ++/* The subcluster X [0..31] is allocated */ ++#define QCOW_OFLAG_SUB_ALLOC(X) (1ULL << (X)) ++/* The subcluster X [0..31] reads as zeroes */ ++#define QCOW_OFLAG_SUB_ZERO(X) (QCOW_OFLAG_SUB_ALLOC(X) << 32) ++/* Subclusters [X, Y) (0 <= X <= Y <= 32) are allocated */ ++#define QCOW_OFLAG_SUB_ALLOC_RANGE(X, Y) \ ++ (QCOW_OFLAG_SUB_ALLOC(Y) - QCOW_OFLAG_SUB_ALLOC(X)) ++/* Subclusters [X, Y) (0 <= X <= Y <= 32) read as zeroes */ ++#define QCOW_OFLAG_SUB_ZERO_RANGE(X, Y) \ ++ (QCOW_OFLAG_SUB_ALLOC_RANGE(X, Y) << 32) ++/* L2 entry bitmap with all allocation bits set */ ++#define QCOW_L2_BITMAP_ALL_ALLOC (QCOW_OFLAG_SUB_ALLOC_RANGE(0, 32)) ++/* L2 entry bitmap with all "read as zeroes" bits set */ ++#define QCOW_L2_BITMAP_ALL_ZEROES (QCOW_OFLAG_SUB_ZERO_RANGE(0, 32)) ++ ++/* Size of normal and extended L2 entries */ ++#define L2E_SIZE_NORMAL (sizeof(uint64_t)) ++#define L2E_SIZE_EXTENDED (sizeof(uint64_t) * 2) ++ ++/* Size of L1 table entries */ ++#define L1E_SIZE (sizeof(uint64_t)) ++ ++/* Size of reftable entries */ ++#define REFTABLE_ENTRY_SIZE (sizeof(uint64_t)) ++ ++#define MIN_CLUSTER_BITS 9 ++#define MAX_CLUSTER_BITS 21 ++ ++/* Defined in the qcow2 spec (compressed cluster descriptor) */ ++#define QCOW2_COMPRESSED_SECTOR_SIZE 512U ++ ++/* Must be at least 2 to cover COW */ ++#define MIN_L2_CACHE_SIZE 2 /* cache entries */ ++ ++/* Must be at least 4 to cover all cases of refcount table growth */ ++#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ ++ ++#ifdef CONFIG_LINUX ++#define DEFAULT_L2_CACHE_MAX_SIZE (32 * MiB) ++#define DEFAULT_CACHE_CLEAN_INTERVAL 600 /* seconds */ ++#else ++#define DEFAULT_L2_CACHE_MAX_SIZE (8 * MiB) ++/* Cache clean interval is currently available only on Linux, so must be 0 */ ++#define DEFAULT_CACHE_CLEAN_INTERVAL 0 ++#endif ++ ++#define DEFAULT_CLUSTER_SIZE 65536 ++ ++#define QCOW2_OPT_DATA_FILE "data-file" ++#define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts" ++#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" ++#define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot" ++#define QCOW2_OPT_DISCARD_OTHER "pass-discard-other" ++#define QCOW2_OPT_DISCARD_NO_UNREF "discard-no-unref" ++#define QCOW2_OPT_OVERLAP "overlap-check" ++#define QCOW2_OPT_OVERLAP_TEMPLATE "overlap-check.template" ++#define QCOW2_OPT_OVERLAP_MAIN_HEADER "overlap-check.main-header" ++#define QCOW2_OPT_OVERLAP_ACTIVE_L1 "overlap-check.active-l1" ++#define QCOW2_OPT_OVERLAP_ACTIVE_L2 "overlap-check.active-l2" ++#define QCOW2_OPT_OVERLAP_REFCOUNT_TABLE "overlap-check.refcount-table" ++#define QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK "overlap-check.refcount-block" ++#define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table" ++#define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1" ++#define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2" ++#define QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY "overlap-check.bitmap-directory" ++#define QCOW2_OPT_CACHE_SIZE "cache-size" ++#define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" ++#define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size" ++#define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size" ++#define QCOW2_OPT_CACHE_CLEAN_INTERVAL "cache-clean-interval" ++ ++typedef struct QCowHeader { ++ uint32_t magic; ++ uint32_t version; ++ uint64_t backing_file_offset; ++ uint32_t backing_file_size; ++ uint32_t cluster_bits; ++ uint64_t size; /* in bytes */ ++ uint32_t crypt_method; ++ uint32_t l1_size; /* XXX: save number of clusters instead ? */ ++ uint64_t l1_table_offset; ++ uint64_t refcount_table_offset; ++ uint32_t refcount_table_clusters; ++ uint32_t nb_snapshots; ++ uint64_t snapshots_offset; ++ ++ /* The following fields are only valid for version >= 3 */ ++ uint64_t incompatible_features; ++ uint64_t compatible_features; ++ uint64_t autoclear_features; ++ ++ uint32_t refcount_order; ++ uint32_t header_length; ++ ++ /* Additional fields */ ++ uint8_t compression_type; ++ ++ /* header must be a multiple of 8 */ ++ uint8_t padding[7]; ++} QEMU_PACKED QCowHeader; ++ ++QEMU_BUILD_BUG_ON(!QEMU_IS_ALIGNED(sizeof(QCowHeader), 8)); ++ ++typedef struct QEMU_PACKED QCowSnapshotHeader { ++ /* header is 8 byte aligned */ ++ uint64_t l1_table_offset; ++ ++ uint32_t l1_size; ++ uint16_t id_str_size; ++ uint16_t name_size; ++ ++ uint32_t date_sec; ++ uint32_t date_nsec; ++ ++ uint64_t vm_clock_nsec; ++ ++ uint32_t vm_state_size; ++ uint32_t extra_data_size; /* for extension */ ++ /* extra data follows */ ++ /* id_str follows */ ++ /* name follows */ ++} QCowSnapshotHeader; ++ ++typedef struct QEMU_PACKED QCowSnapshotExtraData { ++ uint64_t vm_state_size_large; ++ uint64_t disk_size; ++ uint64_t icount; ++} QCowSnapshotExtraData; ++ ++ ++typedef struct QCowSnapshot { ++ uint64_t l1_table_offset; ++ uint32_t l1_size; ++ char *id_str; ++ char *name; ++ uint64_t disk_size; ++ uint64_t vm_state_size; ++ uint32_t date_sec; ++ uint32_t date_nsec; ++ uint64_t vm_clock_nsec; ++ /* icount value for the moment when snapshot was taken */ ++ uint64_t icount; ++ /* Size of all extra data, including QCowSnapshotExtraData if available */ ++ uint32_t extra_data_size; ++ /* Data beyond QCowSnapshotExtraData, if any */ ++ void *unknown_extra_data; ++} QCowSnapshot; ++ ++struct Qcow2Cache; ++typedef struct Qcow2Cache Qcow2Cache; ++ ++typedef struct Qcow2CryptoHeaderExtension { ++ uint64_t offset; ++ uint64_t length; ++} QEMU_PACKED Qcow2CryptoHeaderExtension; ++ ++typedef struct Qcow2UnknownHeaderExtension { ++ uint32_t magic; ++ uint32_t len; ++ QLIST_ENTRY(Qcow2UnknownHeaderExtension) next; ++ uint8_t data[]; ++} Qcow2UnknownHeaderExtension; ++ ++enum { ++ QCOW2_FEAT_TYPE_INCOMPATIBLE = 0, ++ QCOW2_FEAT_TYPE_COMPATIBLE = 1, ++ QCOW2_FEAT_TYPE_AUTOCLEAR = 2, ++}; ++ ++/* Incompatible feature bits */ ++enum { ++ QCOW2_INCOMPAT_DIRTY_BITNR = 0, ++ QCOW2_INCOMPAT_CORRUPT_BITNR = 1, ++ QCOW2_INCOMPAT_DATA_FILE_BITNR = 2, ++ QCOW2_INCOMPAT_COMPRESSION_BITNR = 3, ++ QCOW2_INCOMPAT_EXTL2_BITNR = 4, ++ QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR, ++ QCOW2_INCOMPAT_CORRUPT = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR, ++ QCOW2_INCOMPAT_DATA_FILE = 1 << QCOW2_INCOMPAT_DATA_FILE_BITNR, ++ QCOW2_INCOMPAT_COMPRESSION = 1 << QCOW2_INCOMPAT_COMPRESSION_BITNR, ++ QCOW2_INCOMPAT_EXTL2 = 1 << QCOW2_INCOMPAT_EXTL2_BITNR, ++ ++ QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY ++ | QCOW2_INCOMPAT_CORRUPT ++ | QCOW2_INCOMPAT_DATA_FILE ++ | QCOW2_INCOMPAT_COMPRESSION ++ | QCOW2_INCOMPAT_EXTL2, ++}; ++ ++/* Compatible feature bits */ ++enum { ++ QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR = 0, ++ QCOW2_COMPAT_LAZY_REFCOUNTS = 1 << QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, ++ ++ QCOW2_COMPAT_FEAT_MASK = QCOW2_COMPAT_LAZY_REFCOUNTS, ++}; ++ ++/* Autoclear feature bits */ ++enum { ++ QCOW2_AUTOCLEAR_BITMAPS_BITNR = 0, ++ QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR = 1, ++ QCOW2_AUTOCLEAR_BITMAPS = 1 << QCOW2_AUTOCLEAR_BITMAPS_BITNR, ++ QCOW2_AUTOCLEAR_DATA_FILE_RAW = 1 << QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR, ++ ++ QCOW2_AUTOCLEAR_MASK = QCOW2_AUTOCLEAR_BITMAPS ++ | QCOW2_AUTOCLEAR_DATA_FILE_RAW, ++}; ++ ++enum qcow2_discard_type { ++ QCOW2_DISCARD_NEVER = 0, ++ QCOW2_DISCARD_ALWAYS, ++ QCOW2_DISCARD_REQUEST, ++ QCOW2_DISCARD_SNAPSHOT, ++ QCOW2_DISCARD_OTHER, ++ QCOW2_DISCARD_MAX ++}; ++ ++typedef struct Qcow2Feature { ++ uint8_t type; ++ uint8_t bit; ++ char name[46]; ++} QEMU_PACKED Qcow2Feature; ++ ++typedef struct Qcow2DiscardRegion { ++ BlockDriverState *bs; ++ uint64_t offset; ++ uint64_t bytes; ++ QTAILQ_ENTRY(Qcow2DiscardRegion) next; ++} Qcow2DiscardRegion; ++ ++typedef uint64_t Qcow2GetRefcountFunc(const void *refcount_array, ++ uint64_t index); ++typedef void Qcow2SetRefcountFunc(void *refcount_array, ++ uint64_t index, uint64_t value); ++ ++typedef struct Qcow2BitmapHeaderExt { ++ uint32_t nb_bitmaps; ++ uint32_t reserved32; ++ uint64_t bitmap_directory_size; ++ uint64_t bitmap_directory_offset; ++} QEMU_PACKED Qcow2BitmapHeaderExt; ++ ++#define QCOW2_MAX_THREADS 4 ++ ++typedef struct BDRVQcow2State { ++ int cluster_bits; ++ int cluster_size; ++ int l2_slice_size; ++ int subcluster_bits; ++ int subcluster_size; ++ int subclusters_per_cluster; ++ int l2_bits; ++ int l2_size; ++ int l1_size; ++ int l1_vm_state_index; ++ int refcount_block_bits; ++ int refcount_block_size; ++ int csize_shift; ++ int csize_mask; ++ uint64_t cluster_offset_mask; ++ uint64_t l1_table_offset; ++ uint64_t *l1_table; ++ ++ Qcow2Cache *l2_table_cache; ++ Qcow2Cache *refcount_block_cache; ++ QEMUTimer *cache_clean_timer; ++ unsigned cache_clean_interval; ++ ++ QLIST_HEAD(, QCowL2Meta) cluster_allocs; ++ ++ uint64_t *refcount_table; ++ uint64_t refcount_table_offset; ++ uint32_t refcount_table_size; ++ uint32_t max_refcount_table_index; /* Last used entry in refcount_table */ ++ uint64_t free_cluster_index; ++ uint64_t free_byte_offset; ++ ++ CoMutex lock; ++ ++ Qcow2CryptoHeaderExtension crypto_header; /* QCow2 header extension */ ++ QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ ++ QCryptoBlock *crypto; /* Disk encryption format driver */ ++ bool crypt_physical_offset; /* Whether to use virtual or physical offset ++ for encryption initialization vector tweak */ ++ uint32_t crypt_method_header; ++ uint64_t snapshots_offset; ++ int snapshots_size; ++ unsigned int nb_snapshots; ++ QCowSnapshot *snapshots; ++ ++ uint32_t nb_bitmaps; ++ uint64_t bitmap_directory_size; ++ uint64_t bitmap_directory_offset; ++ ++ int flags; ++ int qcow_version; ++ bool use_lazy_refcounts; ++ int refcount_order; ++ int refcount_bits; ++ uint64_t refcount_max; ++ ++ Qcow2GetRefcountFunc *get_refcount; ++ Qcow2SetRefcountFunc *set_refcount; ++ ++ bool discard_passthrough[QCOW2_DISCARD_MAX]; ++ ++ bool discard_no_unref; ++ ++ int overlap_check; /* bitmask of Qcow2MetadataOverlap values */ ++ bool signaled_corruption; ++ ++ uint64_t incompatible_features; ++ uint64_t compatible_features; ++ uint64_t autoclear_features; ++ ++ size_t unknown_header_fields_size; ++ void *unknown_header_fields; ++ QLIST_HEAD(, Qcow2UnknownHeaderExtension) unknown_header_ext; ++ QTAILQ_HEAD (, Qcow2DiscardRegion) discards; ++ bool cache_discards; ++ ++ /* Backing file path and format as stored in the image (this is not the ++ * effective path/format, which may be the result of a runtime option ++ * override) */ ++ char *image_backing_file; ++ char *image_backing_format; ++ char *image_data_file; ++ ++ CoQueue thread_task_queue; ++ int nb_threads; ++ ++ BdrvChild *data_file; ++ ++ bool metadata_preallocation_checked; ++ bool metadata_preallocation; ++ /* ++ * Compression type used for the image. Default: 0 - ZLIB ++ * The image compression type is set on image creation. ++ * For now, the only way to change the compression type ++ * is to convert the image with the desired compression type set. ++ */ ++ Qcow2CompressionType compression_type; ++} BDRVQcow2State; ++ ++typedef struct Qcow2COWRegion { ++ /** ++ * Offset of the COW region in bytes from the start of the first cluster ++ * touched by the request. ++ */ ++ unsigned offset; ++ ++ /** Number of bytes to copy */ ++ unsigned nb_bytes; ++} Qcow2COWRegion; ++ ++/** ++ * Describes an in-flight (part of a) write request that writes to clusters ++ * that need to have their L2 table entries updated (because they are ++ * newly allocated or need changes in their L2 bitmaps) ++ */ ++typedef struct QCowL2Meta ++{ ++ /** Guest offset of the first updated cluster */ ++ uint64_t offset; ++ ++ /** Host offset of the first updated cluster */ ++ uint64_t alloc_offset; ++ ++ /** Number of updated clusters */ ++ int nb_clusters; ++ ++ /** Do not free the old clusters */ ++ bool keep_old_clusters; ++ ++ /** ++ * Requests that overlap with this allocation and wait to be restarted ++ * when the allocating request has completed. ++ */ ++ CoQueue dependent_requests; ++ ++ /** ++ * The COW Region immediately before the area the guest actually ++ * writes to. This (part of the) write request starts at ++ * cow_start.offset + cow_start.nb_bytes. ++ */ ++ Qcow2COWRegion cow_start; ++ ++ /** ++ * The COW Region immediately after the area the guest actually ++ * writes to. This (part of the) write request ends at cow_end.offset ++ * (which must always be set even when cow_end.nb_bytes is 0). ++ */ ++ Qcow2COWRegion cow_end; ++ ++ /* ++ * Indicates that COW regions are already handled and do not require ++ * any more processing. ++ */ ++ bool skip_cow; ++ ++ /** ++ * Indicates that this is not a normal write request but a preallocation. ++ * If the image has extended L2 entries this means that no new individual ++ * subclusters will be marked as allocated in the L2 bitmap (but any ++ * existing contents of that bitmap will be kept). ++ */ ++ bool prealloc; ++ ++ /** ++ * The I/O vector with the data from the actual guest write request. ++ * If non-NULL, this is meant to be merged together with the data ++ * from @cow_start and @cow_end into one single write operation. ++ */ ++ QEMUIOVector *data_qiov; ++ size_t data_qiov_offset; ++ ++ /** Pointer to next L2Meta of the same write request */ ++ struct QCowL2Meta *next; ++ ++ QLIST_ENTRY(QCowL2Meta) next_in_flight; ++} QCowL2Meta; ++ ++/* ++ * In images with standard L2 entries all clusters are treated as if ++ * they had one subcluster so QCow2ClusterType and QCow2SubclusterType ++ * can be mapped to each other and have the exact same meaning ++ * (QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC cannot happen in these images). ++ * ++ * In images with extended L2 entries QCow2ClusterType refers to the ++ * complete cluster and QCow2SubclusterType to each of the individual ++ * subclusters, so there are several possible combinations: ++ * ++ * |--------------+---------------------------| ++ * | Cluster type | Possible subcluster types | ++ * |--------------+---------------------------| ++ * | UNALLOCATED | UNALLOCATED_PLAIN | ++ * | | ZERO_PLAIN | ++ * |--------------+---------------------------| ++ * | NORMAL | UNALLOCATED_ALLOC | ++ * | | ZERO_ALLOC | ++ * | | NORMAL | ++ * |--------------+---------------------------| ++ * | COMPRESSED | COMPRESSED | ++ * |--------------+---------------------------| ++ * ++ * QCOW2_SUBCLUSTER_INVALID means that the L2 entry is incorrect and ++ * the image should be marked corrupt. ++ */ ++ ++typedef enum QCow2ClusterType { ++ QCOW2_CLUSTER_UNALLOCATED, ++ QCOW2_CLUSTER_ZERO_PLAIN, ++ QCOW2_CLUSTER_ZERO_ALLOC, ++ QCOW2_CLUSTER_NORMAL, ++ QCOW2_CLUSTER_COMPRESSED, ++} QCow2ClusterType; ++ ++typedef enum QCow2SubclusterType { ++ QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN, ++ QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC, ++ QCOW2_SUBCLUSTER_ZERO_PLAIN, ++ QCOW2_SUBCLUSTER_ZERO_ALLOC, ++ QCOW2_SUBCLUSTER_NORMAL, ++ QCOW2_SUBCLUSTER_COMPRESSED, ++ QCOW2_SUBCLUSTER_INVALID, ++} QCow2SubclusterType; ++ ++typedef enum QCow2MetadataOverlap { ++ QCOW2_OL_MAIN_HEADER_BITNR = 0, ++ QCOW2_OL_ACTIVE_L1_BITNR = 1, ++ QCOW2_OL_ACTIVE_L2_BITNR = 2, ++ QCOW2_OL_REFCOUNT_TABLE_BITNR = 3, ++ QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4, ++ QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5, ++ QCOW2_OL_INACTIVE_L1_BITNR = 6, ++ QCOW2_OL_INACTIVE_L2_BITNR = 7, ++ QCOW2_OL_BITMAP_DIRECTORY_BITNR = 8, ++ ++ QCOW2_OL_MAX_BITNR = 9, ++ ++ QCOW2_OL_NONE = 0, ++ QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR), ++ QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR), ++ QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR), ++ QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR), ++ QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR), ++ QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR), ++ QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR), ++ /* NOTE: Checking overlaps with inactive L2 tables will result in bdrv ++ * reads. */ ++ QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR), ++ QCOW2_OL_BITMAP_DIRECTORY = (1 << QCOW2_OL_BITMAP_DIRECTORY_BITNR), ++} QCow2MetadataOverlap; ++ ++/* Perform all overlap checks which can be done in constant time */ ++#define QCOW2_OL_CONSTANT \ ++ (QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \ ++ QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_BITMAP_DIRECTORY) ++ ++/* Perform all overlap checks which don't require disk access */ ++#define QCOW2_OL_CACHED \ ++ (QCOW2_OL_CONSTANT | QCOW2_OL_ACTIVE_L2 | QCOW2_OL_REFCOUNT_BLOCK | \ ++ QCOW2_OL_INACTIVE_L1) ++ ++/* Perform all overlap checks */ ++#define QCOW2_OL_ALL \ ++ (QCOW2_OL_CACHED | QCOW2_OL_INACTIVE_L2) ++ ++#define L1E_OFFSET_MASK 0x00fffffffffffe00ULL ++#define L1E_RESERVED_MASK 0x7f000000000001ffULL ++#define L2E_OFFSET_MASK 0x00fffffffffffe00ULL ++#define L2E_STD_RESERVED_MASK 0x3f000000000001feULL ++ ++#define REFT_OFFSET_MASK 0xfffffffffffffe00ULL ++#define REFT_RESERVED_MASK 0x1ffULL ++ ++#define INV_OFFSET (-1ULL) ++ ++static inline bool has_subclusters(BDRVQcow2State *s) ++{ ++ return s->incompatible_features & QCOW2_INCOMPAT_EXTL2; ++} ++ ++static inline size_t l2_entry_size(BDRVQcow2State *s) ++{ ++ return has_subclusters(s) ? L2E_SIZE_EXTENDED : L2E_SIZE_NORMAL; ++} ++ ++static inline uint64_t get_l2_entry(BDRVQcow2State *s, uint64_t *l2_slice, ++ int idx) ++{ ++ idx *= l2_entry_size(s) / sizeof(uint64_t); ++ return be64_to_cpu(l2_slice[idx]); ++} ++ ++static inline uint64_t get_l2_bitmap(BDRVQcow2State *s, uint64_t *l2_slice, ++ int idx) ++{ ++ if (has_subclusters(s)) { ++ idx *= l2_entry_size(s) / sizeof(uint64_t); ++ return be64_to_cpu(l2_slice[idx + 1]); ++ } else { ++ return 0; /* For convenience only; this value has no meaning. */ ++ } ++} ++ ++static inline void set_l2_entry(BDRVQcow2State *s, uint64_t *l2_slice, ++ int idx, uint64_t entry) ++{ ++ idx *= l2_entry_size(s) / sizeof(uint64_t); ++ l2_slice[idx] = cpu_to_be64(entry); ++} ++ ++static inline void set_l2_bitmap(BDRVQcow2State *s, uint64_t *l2_slice, ++ int idx, uint64_t bitmap) ++{ ++ assert(has_subclusters(s)); ++ idx *= l2_entry_size(s) / sizeof(uint64_t); ++ l2_slice[idx + 1] = cpu_to_be64(bitmap); ++} ++ ++static inline bool GRAPH_RDLOCK has_data_file(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ return (s->data_file != bs->file); ++} ++ ++static inline bool data_file_is_raw(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ return !!(s->autoclear_features & QCOW2_AUTOCLEAR_DATA_FILE_RAW); ++} ++ ++static inline int64_t start_of_cluster(BDRVQcow2State *s, int64_t offset) ++{ ++ return offset & ~(s->cluster_size - 1); ++} ++ ++static inline int64_t offset_into_cluster(BDRVQcow2State *s, int64_t offset) ++{ ++ return offset & (s->cluster_size - 1); ++} ++ ++static inline int64_t offset_into_subcluster(BDRVQcow2State *s, int64_t offset) ++{ ++ return offset & (s->subcluster_size - 1); ++} ++ ++static inline uint64_t size_to_clusters(BDRVQcow2State *s, uint64_t size) ++{ ++ return (size + (s->cluster_size - 1)) >> s->cluster_bits; ++} ++ ++static inline uint64_t size_to_subclusters(BDRVQcow2State *s, uint64_t size) ++{ ++ return (size + (s->subcluster_size - 1)) >> s->subcluster_bits; ++} ++ ++static inline int64_t size_to_l1(BDRVQcow2State *s, int64_t size) ++{ ++ int shift = s->cluster_bits + s->l2_bits; ++ return (size + (1ULL << shift) - 1) >> shift; ++} ++ ++static inline int offset_to_l1_index(BDRVQcow2State *s, uint64_t offset) ++{ ++ return offset >> (s->l2_bits + s->cluster_bits); ++} ++ ++static inline int offset_to_l2_index(BDRVQcow2State *s, int64_t offset) ++{ ++ return (offset >> s->cluster_bits) & (s->l2_size - 1); ++} ++ ++static inline int offset_to_l2_slice_index(BDRVQcow2State *s, int64_t offset) ++{ ++ return (offset >> s->cluster_bits) & (s->l2_slice_size - 1); ++} ++ ++static inline int offset_to_sc_index(BDRVQcow2State *s, int64_t offset) ++{ ++ return (offset >> s->subcluster_bits) & (s->subclusters_per_cluster - 1); ++} ++ ++static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s) ++{ ++ return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); ++} ++ ++static inline QCow2ClusterType GRAPH_RDLOCK ++qcow2_get_cluster_type(BlockDriverState *bs, uint64_t l2_entry) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ if (l2_entry & QCOW_OFLAG_COMPRESSED) { ++ return QCOW2_CLUSTER_COMPRESSED; ++ } else if ((l2_entry & QCOW_OFLAG_ZERO) && !has_subclusters(s)) { ++ if (l2_entry & L2E_OFFSET_MASK) { ++ return QCOW2_CLUSTER_ZERO_ALLOC; ++ } ++ return QCOW2_CLUSTER_ZERO_PLAIN; ++ } else if (!(l2_entry & L2E_OFFSET_MASK)) { ++ /* Offset 0 generally means unallocated, but it is ambiguous with ++ * external data files because 0 is a valid offset there. However, all ++ * clusters in external data files always have refcount 1, so we can ++ * rely on QCOW_OFLAG_COPIED to disambiguate. */ ++ if (has_data_file(bs) && (l2_entry & QCOW_OFLAG_COPIED)) { ++ return QCOW2_CLUSTER_NORMAL; ++ } else { ++ return QCOW2_CLUSTER_UNALLOCATED; ++ } ++ } else { ++ return QCOW2_CLUSTER_NORMAL; ++ } ++} ++ ++/* ++ * In an image without subsclusters @l2_bitmap is ignored and ++ * @sc_index must be 0. ++ * Return QCOW2_SUBCLUSTER_INVALID if an invalid l2 entry is detected ++ * (this checks the whole entry and bitmap, not only the bits related ++ * to subcluster @sc_index). ++ */ ++static inline GRAPH_RDLOCK ++QCow2SubclusterType qcow2_get_subcluster_type(BlockDriverState *bs, ++ uint64_t l2_entry, ++ uint64_t l2_bitmap, ++ unsigned sc_index) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCow2ClusterType type = qcow2_get_cluster_type(bs, l2_entry); ++ assert(sc_index < s->subclusters_per_cluster); ++ ++ if (has_subclusters(s)) { ++ switch (type) { ++ case QCOW2_CLUSTER_COMPRESSED: ++ return QCOW2_SUBCLUSTER_COMPRESSED; ++ case QCOW2_CLUSTER_NORMAL: ++ if ((l2_bitmap >> 32) & l2_bitmap) { ++ return QCOW2_SUBCLUSTER_INVALID; ++ } else if (l2_bitmap & QCOW_OFLAG_SUB_ZERO(sc_index)) { ++ return QCOW2_SUBCLUSTER_ZERO_ALLOC; ++ } else if (l2_bitmap & QCOW_OFLAG_SUB_ALLOC(sc_index)) { ++ return QCOW2_SUBCLUSTER_NORMAL; ++ } else { ++ return QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC; ++ } ++ case QCOW2_CLUSTER_UNALLOCATED: ++ if (l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC) { ++ return QCOW2_SUBCLUSTER_INVALID; ++ } else if (l2_bitmap & QCOW_OFLAG_SUB_ZERO(sc_index)) { ++ return QCOW2_SUBCLUSTER_ZERO_PLAIN; ++ } else { ++ return QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN; ++ } ++ default: ++ g_assert_not_reached(); ++ } ++ } else { ++ switch (type) { ++ case QCOW2_CLUSTER_COMPRESSED: ++ return QCOW2_SUBCLUSTER_COMPRESSED; ++ case QCOW2_CLUSTER_ZERO_PLAIN: ++ return QCOW2_SUBCLUSTER_ZERO_PLAIN; ++ case QCOW2_CLUSTER_ZERO_ALLOC: ++ return QCOW2_SUBCLUSTER_ZERO_ALLOC; ++ case QCOW2_CLUSTER_NORMAL: ++ return QCOW2_SUBCLUSTER_NORMAL; ++ case QCOW2_CLUSTER_UNALLOCATED: ++ return QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++} ++ ++static inline bool qcow2_cluster_is_allocated(QCow2ClusterType type) ++{ ++ return (type == QCOW2_CLUSTER_COMPRESSED || type == QCOW2_CLUSTER_NORMAL || ++ type == QCOW2_CLUSTER_ZERO_ALLOC); ++} ++ ++/* Check whether refcounts are eager or lazy */ ++static inline bool qcow2_need_accurate_refcounts(BDRVQcow2State *s) ++{ ++ return !(s->incompatible_features & QCOW2_INCOMPAT_DIRTY); ++} ++ ++static inline uint64_t l2meta_cow_start(QCowL2Meta *m) ++{ ++ return m->offset + m->cow_start.offset; ++} ++ ++static inline uint64_t l2meta_cow_end(QCowL2Meta *m) ++{ ++ return m->offset + m->cow_end.offset + m->cow_end.nb_bytes; ++} ++ ++static inline uint64_t refcount_diff(uint64_t r1, uint64_t r2) ++{ ++ return r1 > r2 ? r1 - r2 : r2 - r1; ++} ++ ++static inline ++uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset) ++{ ++ return offset >> (s->refcount_block_bits + s->cluster_bits); ++} ++ ++/* qcow2.c functions */ ++int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size, ++ int refcount_order, bool generous_increase, ++ uint64_t *refblock_count); ++ ++int GRAPH_RDLOCK qcow2_mark_dirty(BlockDriverState *bs); ++int GRAPH_RDLOCK qcow2_mark_corrupt(BlockDriverState *bs); ++int GRAPH_RDLOCK qcow2_update_header(BlockDriverState *bs); ++ ++void GRAPH_RDLOCK ++qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, ++ int64_t size, const char *message_format, ...) ++ G_GNUC_PRINTF(5, 6); ++ ++int qcow2_validate_table(BlockDriverState *bs, uint64_t offset, ++ uint64_t entries, size_t entry_len, ++ int64_t max_size_bytes, const char *table_name, ++ Error **errp); ++ ++/* qcow2-refcount.c functions */ ++int coroutine_fn GRAPH_RDLOCK qcow2_refcount_init(BlockDriverState *bs); ++void qcow2_refcount_close(BlockDriverState *bs); ++ ++int GRAPH_RDLOCK qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, ++ uint64_t *refcount); ++ ++int GRAPH_RDLOCK ++qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, ++ uint64_t addend, bool decrease, ++ enum qcow2_discard_type type); ++ ++int64_t GRAPH_RDLOCK ++qcow2_refcount_area(BlockDriverState *bs, uint64_t offset, ++ uint64_t additional_clusters, bool exact_size, ++ int new_refblock_index, ++ uint64_t new_refblock_offset); ++ ++int64_t GRAPH_RDLOCK ++qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size); ++ ++int64_t GRAPH_RDLOCK coroutine_fn ++qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, ++ int64_t nb_clusters); ++ ++int64_t coroutine_fn GRAPH_RDLOCK qcow2_alloc_bytes(BlockDriverState *bs, int size); ++void GRAPH_RDLOCK qcow2_free_clusters(BlockDriverState *bs, ++ int64_t offset, int64_t size, ++ enum qcow2_discard_type type); ++void GRAPH_RDLOCK ++qcow2_free_any_cluster(BlockDriverState *bs, uint64_t l2_entry, ++ enum qcow2_discard_type type); ++ ++int GRAPH_RDLOCK ++qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset, ++ int l1_size, int addend); ++ ++int GRAPH_RDLOCK qcow2_flush_caches(BlockDriverState *bs); ++int GRAPH_RDLOCK qcow2_write_caches(BlockDriverState *bs); ++int coroutine_fn qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, ++ BdrvCheckMode fix); ++ ++void GRAPH_RDLOCK qcow2_process_discards(BlockDriverState *bs, int ret); ++ ++int GRAPH_RDLOCK ++qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, ++ int64_t size); ++int GRAPH_RDLOCK ++qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, ++ int64_t size, bool data_file); ++ ++int coroutine_fn qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, ++ int64_t *refcount_table_size, ++ int64_t offset, int64_t size); ++ ++int GRAPH_RDLOCK ++qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, ++ BlockDriverAmendStatusCB *status_cb, ++ void *cb_opaque, Error **errp); ++int coroutine_fn GRAPH_RDLOCK qcow2_shrink_reftable(BlockDriverState *bs); ++ ++int64_t coroutine_fn GRAPH_RDLOCK ++qcow2_get_last_cluster(BlockDriverState *bs, int64_t size); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_detect_metadata_preallocation(BlockDriverState *bs); ++ ++/* qcow2-cluster.c functions */ ++int GRAPH_RDLOCK ++qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, bool exact_size); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size); ++ ++int GRAPH_RDLOCK qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); ++int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, ++ uint8_t *buf, int nb_sectors, bool enc, Error **errp); ++ ++int GRAPH_RDLOCK ++qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, ++ unsigned int *bytes, uint64_t *host_offset, ++ QCow2SubclusterType *subcluster_type); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, ++ unsigned int *bytes, uint64_t *host_offset, ++ QCowL2Meta **m); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, ++ int compressed_size, uint64_t *host_offset); ++void GRAPH_RDLOCK ++qcow2_parse_compressed_l2_entry(BlockDriverState *bs, uint64_t l2_entry, ++ uint64_t *coffset, int *csize); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); ++ ++void coroutine_fn GRAPH_RDLOCK ++qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m); ++ ++int GRAPH_RDLOCK ++qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, uint64_t bytes, ++ enum qcow2_discard_type type, bool full_discard); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_subcluster_zeroize(BlockDriverState *bs, uint64_t offset, uint64_t bytes, ++ int flags); ++ ++int GRAPH_RDLOCK ++qcow2_expand_zero_clusters(BlockDriverState *bs, ++ BlockDriverAmendStatusCB *status_cb, ++ void *cb_opaque); ++ ++/* qcow2-snapshot.c functions */ ++int GRAPH_RDLOCK ++qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); ++ ++int GRAPH_RDLOCK ++qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); ++ ++int GRAPH_RDLOCK ++qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id, ++ const char *name, Error **errp); ++ ++int GRAPH_RDLOCK ++qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); ++ ++int GRAPH_RDLOCK ++qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_id, ++ const char *name, Error **errp); ++ ++void qcow2_free_snapshots(BlockDriverState *bs); ++int coroutine_fn GRAPH_RDLOCK ++qcow2_read_snapshots(BlockDriverState *bs, Error **errp); ++int GRAPH_RDLOCK qcow2_write_snapshots(BlockDriverState *bs); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_check_read_snapshot_table(BlockDriverState *bs, BdrvCheckResult *result, ++ BdrvCheckMode fix); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_check_fix_snapshot_table(BlockDriverState *bs, BdrvCheckResult *result, ++ BdrvCheckMode fix); ++ ++/* qcow2-cache.c functions */ ++Qcow2Cache * GRAPH_RDLOCK ++qcow2_cache_create(BlockDriverState *bs, int num_tables, unsigned table_size); ++ ++int qcow2_cache_destroy(Qcow2Cache *c); ++ ++void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); ++int GRAPH_RDLOCK qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); ++int GRAPH_RDLOCK qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c); ++int GRAPH_RDLOCK qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, ++ Qcow2Cache *dependency); ++void qcow2_cache_depends_on_flush(Qcow2Cache *c); ++ ++void qcow2_cache_clean_unused(Qcow2Cache *c); ++int GRAPH_RDLOCK qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c); ++ ++int GRAPH_RDLOCK ++qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, ++ void **table); ++ ++int GRAPH_RDLOCK ++qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, ++ void **table); ++ ++void qcow2_cache_put(Qcow2Cache *c, void **table); ++void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset); ++void qcow2_cache_discard(Qcow2Cache *c, void *table); ++ ++/* qcow2-bitmap.c functions */ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, ++ int64_t *refcount_table_size); ++ ++bool coroutine_fn GRAPH_RDLOCK ++qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, ++ Error **errp); ++ ++bool GRAPH_RDLOCK ++qcow2_get_bitmap_info_list(BlockDriverState *bs, ++ Qcow2BitmapInfoList **info_list, Error **errp); ++ ++int GRAPH_RDLOCK qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); ++int GRAPH_RDLOCK qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp); ++ ++bool GRAPH_RDLOCK ++qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, bool release_stored, ++ Error **errp); ++ ++bool coroutine_fn GRAPH_RDLOCK ++qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, ++ uint32_t granularity, Error **errp); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, ++ Error **errp); ++ ++bool qcow2_supports_persistent_dirty_bitmap(BlockDriverState *bs); ++uint64_t qcow2_get_persistent_dirty_bitmap_size(BlockDriverState *bs, ++ uint32_t cluster_size); ++ ++ssize_t coroutine_fn ++qcow2_co_compress(BlockDriverState *bs, void *dest, size_t dest_size, ++ const void *src, size_t src_size); ++ssize_t coroutine_fn ++qcow2_co_decompress(BlockDriverState *bs, void *dest, size_t dest_size, ++ const void *src, size_t src_size); ++int coroutine_fn ++qcow2_co_encrypt(BlockDriverState *bs, uint64_t host_offset, ++ uint64_t guest_offset, void *buf, size_t len); ++int coroutine_fn ++qcow2_co_decrypt(BlockDriverState *bs, uint64_t host_offset, ++ uint64_t guest_offset, void *buf, size_t len); ++ ++#endif +diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h +new file mode 100644 +index 00000000..7a3f2e65 +--- /dev/null ++++ b/include/qemu/atomic.h +@@ -0,0 +1,284 @@ ++/* ++ * Simple interface for atomic operations. ++ * ++ * Copyright (C) 2013 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ * See docs/devel/atomics.rst for discussion about the guarantees each ++ * atomic primitive is meant to provide. ++ */ ++ ++#ifndef QEMU_ATOMIC_H ++#define QEMU_ATOMIC_H ++ ++#include "compiler.h" ++ ++/* Compiler barrier */ ++#define barrier() ({ asm volatile("" ::: "memory"); (void)0; }) ++ ++#ifndef __ATOMIC_RELAXED ++#error "Expecting C11 atomic ops" ++#endif ++ ++/* Manual memory barriers ++ * ++ *__atomic_thread_fence does not include a compiler barrier; instead, ++ * the barrier is part of __atomic_load/__atomic_store's "volatile-like" ++ * semantics. If smp_wmb() is a no-op, absence of the barrier means that ++ * the compiler is free to reorder stores on each side of the barrier. ++ * Add one here, and similarly in smp_rmb() and smp_read_barrier_depends(). ++ */ ++ ++#define smp_mb() ({ barrier(); __atomic_thread_fence(__ATOMIC_SEQ_CST); }) ++#define smp_mb_release() ({ barrier(); __atomic_thread_fence(__ATOMIC_RELEASE); }) ++#define smp_mb_acquire() ({ barrier(); __atomic_thread_fence(__ATOMIC_ACQUIRE); }) ++ ++/* Most compilers currently treat consume and acquire the same, but really ++ * no processors except Alpha need a barrier here. Leave it in if ++ * using Thread Sanitizer to avoid warnings, otherwise optimize it away. ++ */ ++#ifdef QEMU_SANITIZE_THREAD ++#define smp_read_barrier_depends() ({ barrier(); __atomic_thread_fence(__ATOMIC_CONSUME); }) ++#elif defined(__alpha__) ++#define smp_read_barrier_depends() asm volatile("mb":::"memory") ++#else ++#define smp_read_barrier_depends() barrier() ++#endif ++ ++/* ++ * A signal barrier forces all pending local memory ops to be observed before ++ * a SIGSEGV is delivered to the *same* thread. In practice this is exactly ++ * the same as barrier(), but since we have the correct builtin, use it. ++ */ ++#define signal_barrier() __atomic_signal_fence(__ATOMIC_SEQ_CST) ++ ++/* Sanity check that the size of an atomic operation isn't "overly large". ++ * Despite the fact that e.g. i686 has 64-bit atomic operations, we do not ++ * want to use them because we ought not need them, and this lets us do a ++ * bit of sanity checking that other 32-bit hosts might build. ++ * ++ * That said, we have a problem on 64-bit ILP32 hosts in that in order to ++ * sync with TCG_OVERSIZED_GUEST, this must match TCG_TARGET_REG_BITS. ++ * We'd prefer not want to pull in everything else TCG related, so handle ++ * those few cases by hand. ++ * ++ * Note that x32 is fully detected with __x86_64__ + _ILP32, and that for ++ * Sparc we always force the use of sparcv9 in configure. MIPS n32 (ILP32) & ++ * n64 (LP64) ABIs are both detected using __mips64. ++ */ ++#if defined(__x86_64__) || defined(__sparc__) || defined(__mips64) ++# define ATOMIC_REG_SIZE 8 ++#else ++# define ATOMIC_REG_SIZE sizeof(void *) ++#endif ++ ++/* Weak atomic operations prevent the compiler moving other ++ * loads/stores past the atomic operation load/store. However there is ++ * no explicit memory barrier for the processor. ++ * ++ * The C11 memory model says that variables that are accessed from ++ * different threads should at least be done with __ATOMIC_RELAXED ++ * primitives or the result is undefined. Generally this has little to ++ * no effect on the generated code but not using the atomic primitives ++ * will get flagged by sanitizers as a violation. ++ */ ++#define qatomic_read__nocheck(ptr) \ ++ __atomic_load_n(ptr, __ATOMIC_RELAXED) ++ ++#define qatomic_read(ptr) \ ++ ({ \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ qatomic_read__nocheck(ptr); \ ++ }) ++ ++#define qatomic_set__nocheck(ptr, i) \ ++ __atomic_store_n(ptr, i, __ATOMIC_RELAXED) ++ ++#define qatomic_set(ptr, i) do { \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ qatomic_set__nocheck(ptr, i); \ ++} while(0) ++ ++/* See above: most compilers currently treat consume and acquire the ++ * same, but this slows down qatomic_rcu_read unnecessarily. ++ */ ++#ifdef QEMU_SANITIZE_THREAD ++#define qatomic_rcu_read__nocheck(ptr, valptr) \ ++ __atomic_load(ptr, valptr, __ATOMIC_CONSUME); ++#else ++#define qatomic_rcu_read__nocheck(ptr, valptr) \ ++ __atomic_load(ptr, valptr, __ATOMIC_RELAXED); \ ++ smp_read_barrier_depends(); ++#endif ++ ++/* ++ * Preprocessor sorcery ahead: use a different identifier for the ++ * local variable in each expansion, so we can nest macro calls ++ * without shadowing variables. ++ */ ++#define qatomic_rcu_read_internal(ptr, _val) \ ++ ({ \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ typeof_strip_qual(*ptr) _val; \ ++ qatomic_rcu_read__nocheck(ptr, &_val); \ ++ _val; \ ++ }) ++#define qatomic_rcu_read(ptr) \ ++ qatomic_rcu_read_internal((ptr), MAKE_IDENTIFIER(_val)) ++ ++#define qatomic_rcu_set(ptr, i) do { \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ __atomic_store_n(ptr, i, __ATOMIC_RELEASE); \ ++} while(0) ++ ++#define qatomic_load_acquire(ptr) \ ++ ({ \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ typeof_strip_qual(*ptr) _val; \ ++ __atomic_load(ptr, &_val, __ATOMIC_ACQUIRE); \ ++ _val; \ ++ }) ++ ++#define qatomic_store_release(ptr, i) do { \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ __atomic_store_n(ptr, i, __ATOMIC_RELEASE); \ ++} while(0) ++ ++ ++/* All the remaining operations are fully sequentially consistent */ ++ ++#define qatomic_xchg__nocheck(ptr, i) ({ \ ++ __atomic_exchange_n(ptr, (i), __ATOMIC_SEQ_CST); \ ++}) ++ ++#define qatomic_xchg(ptr, i) ({ \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ qatomic_xchg__nocheck(ptr, i); \ ++}) ++ ++/* Returns the old value of '*ptr' (whether the cmpxchg failed or not) */ ++#define qatomic_cmpxchg__nocheck(ptr, old, new) ({ \ ++ typeof_strip_qual(*ptr) _old = (old); \ ++ (void)__atomic_compare_exchange_n(ptr, &_old, new, false, \ ++ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \ ++ _old; \ ++}) ++ ++#define qatomic_cmpxchg(ptr, old, new) ({ \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ qatomic_cmpxchg__nocheck(ptr, old, new); \ ++}) ++ ++/* Provide shorter names for GCC atomic builtins, return old value */ ++#define qatomic_fetch_inc(ptr) __atomic_fetch_add(ptr, 1, __ATOMIC_SEQ_CST) ++#define qatomic_fetch_dec(ptr) __atomic_fetch_sub(ptr, 1, __ATOMIC_SEQ_CST) ++ ++#define qatomic_fetch_add(ptr, n) __atomic_fetch_add(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_fetch_sub(ptr, n) __atomic_fetch_sub(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_fetch_and(ptr, n) __atomic_fetch_and(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_fetch_or(ptr, n) __atomic_fetch_or(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_fetch_xor(ptr, n) __atomic_fetch_xor(ptr, n, __ATOMIC_SEQ_CST) ++ ++#define qatomic_inc_fetch(ptr) __atomic_add_fetch(ptr, 1, __ATOMIC_SEQ_CST) ++#define qatomic_dec_fetch(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_SEQ_CST) ++#define qatomic_add_fetch(ptr, n) __atomic_add_fetch(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_sub_fetch(ptr, n) __atomic_sub_fetch(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_and_fetch(ptr, n) __atomic_and_fetch(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_or_fetch(ptr, n) __atomic_or_fetch(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_xor_fetch(ptr, n) __atomic_xor_fetch(ptr, n, __ATOMIC_SEQ_CST) ++ ++/* And even shorter names that return void. */ ++#define qatomic_inc(ptr) \ ++ ((void) __atomic_fetch_add(ptr, 1, __ATOMIC_SEQ_CST)) ++#define qatomic_dec(ptr) \ ++ ((void) __atomic_fetch_sub(ptr, 1, __ATOMIC_SEQ_CST)) ++#define qatomic_add(ptr, n) \ ++ ((void) __atomic_fetch_add(ptr, n, __ATOMIC_SEQ_CST)) ++#define qatomic_sub(ptr, n) \ ++ ((void) __atomic_fetch_sub(ptr, n, __ATOMIC_SEQ_CST)) ++#define qatomic_and(ptr, n) \ ++ ((void) __atomic_fetch_and(ptr, n, __ATOMIC_SEQ_CST)) ++#define qatomic_or(ptr, n) \ ++ ((void) __atomic_fetch_or(ptr, n, __ATOMIC_SEQ_CST)) ++#define qatomic_xor(ptr, n) \ ++ ((void) __atomic_fetch_xor(ptr, n, __ATOMIC_SEQ_CST)) ++ ++#define smp_wmb() smp_mb_release() ++#define smp_rmb() smp_mb_acquire() ++ ++/* ++ * SEQ_CST is weaker than the older __sync_* builtins and Linux ++ * kernel read-modify-write atomics. Provide a macro to obtain ++ * the same semantics. ++ */ ++#if !defined(QEMU_SANITIZE_THREAD) && \ ++ (defined(__i386__) || defined(__x86_64__) || defined(__s390x__)) ++# define smp_mb__before_rmw() signal_barrier() ++# define smp_mb__after_rmw() signal_barrier() ++#else ++# define smp_mb__before_rmw() smp_mb() ++# define smp_mb__after_rmw() smp_mb() ++#endif ++ ++/* ++ * On some architectures, qatomic_set_mb is more efficient than a store ++ * plus a fence. ++ */ ++ ++#if !defined(QEMU_SANITIZE_THREAD) && \ ++ (defined(__i386__) || defined(__x86_64__) || defined(__s390x__)) ++# define qatomic_set_mb(ptr, i) \ ++ ({ (void)qatomic_xchg(ptr, i); smp_mb__after_rmw(); }) ++#else ++# define qatomic_set_mb(ptr, i) \ ++ ({ qatomic_store_release(ptr, i); smp_mb(); }) ++#endif ++ ++#define qatomic_fetch_inc_nonzero(ptr) ({ \ ++ typeof_strip_qual(*ptr) _oldn = qatomic_read(ptr); \ ++ while (_oldn && qatomic_cmpxchg(ptr, _oldn, _oldn + 1) != _oldn) { \ ++ _oldn = qatomic_read(ptr); \ ++ } \ ++ _oldn; \ ++}) ++ ++/* ++ * Abstractions to access atomically (i.e. "once") i64/u64 variables. ++ * ++ * The i386 abi is odd in that by default members are only aligned to ++ * 4 bytes, which means that 8-byte types can wind up mis-aligned. ++ * Clang will then warn about this, and emit a call into libatomic. ++ * ++ * Use of these types in structures when they will be used with atomic ++ * operations can avoid this. ++ */ ++typedef int64_t aligned_int64_t __attribute__((aligned(8))); ++typedef uint64_t aligned_uint64_t __attribute__((aligned(8))); ++ ++#ifdef CONFIG_ATOMIC64 ++/* Use __nocheck because sizeof(void *) might be < sizeof(u64) */ ++#define qatomic_read_i64(P) \ ++ _Generic(*(P), int64_t: qatomic_read__nocheck(P)) ++#define qatomic_read_u64(P) \ ++ _Generic(*(P), uint64_t: qatomic_read__nocheck(P)) ++#define qatomic_set_i64(P, V) \ ++ _Generic(*(P), int64_t: qatomic_set__nocheck(P, V)) ++#define qatomic_set_u64(P, V) \ ++ _Generic(*(P), uint64_t: qatomic_set__nocheck(P, V)) ++ ++static inline void qatomic64_init(void) ++{ ++} ++#else /* !CONFIG_ATOMIC64 */ ++int64_t qatomic_read_i64(const int64_t *ptr); ++uint64_t qatomic_read_u64(const uint64_t *ptr); ++void qatomic_set_i64(int64_t *ptr, int64_t val); ++void qatomic_set_u64(uint64_t *ptr, uint64_t val); ++void qatomic64_init(void); ++#endif /* !CONFIG_ATOMIC64 */ ++ ++#endif /* QEMU_ATOMIC_H */ +diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h +new file mode 100644 +index 00000000..1cf28844 +--- /dev/null ++++ b/include/qemu/bitmap.h +@@ -0,0 +1,281 @@ ++/* ++ * Bitmap Module ++ * ++ * Copyright (C) 2010 Corentin Chary ++ * ++ * Mostly inspired by (stolen from) linux/bitmap.h and linux/bitops.h ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BITMAP_H ++#define BITMAP_H ++ ++ ++#include "qemu/bitops.h" ++ ++/* ++ * The available bitmap operations and their rough meaning in the ++ * case that the bitmap is a single unsigned long are thus: ++ * ++ * Note that nbits should be always a compile time evaluable constant. ++ * Otherwise many inlines will generate horrible code. ++ * ++ * bitmap_zero(dst, nbits) *dst = 0UL ++ * bitmap_fill(dst, nbits) *dst = ~0UL ++ * bitmap_copy(dst, src, nbits) *dst = *src ++ * bitmap_and(dst, src1, src2, nbits) *dst = *src1 & *src2 ++ * bitmap_or(dst, src1, src2, nbits) *dst = *src1 | *src2 ++ * bitmap_xor(dst, src1, src2, nbits) *dst = *src1 ^ *src2 ++ * bitmap_andnot(dst, src1, src2, nbits) *dst = *src1 & ~(*src2) ++ * bitmap_complement(dst, src, nbits) *dst = ~(*src) ++ * bitmap_equal(src1, src2, nbits) Are *src1 and *src2 equal? ++ * bitmap_intersects(src1, src2, nbits) Do *src1 and *src2 overlap? ++ * bitmap_empty(src, nbits) Are all bits zero in *src? ++ * bitmap_full(src, nbits) Are all bits set in *src? ++ * bitmap_set(dst, pos, nbits) Set specified bit area ++ * bitmap_set_atomic(dst, pos, nbits) Set specified bit area with atomic ops ++ * bitmap_clear(dst, pos, nbits) Clear specified bit area ++ * bitmap_test_and_clear_atomic(dst, pos, nbits) Test and clear area ++ * bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area ++ * bitmap_to_le(dst, src, nbits) Convert bitmap to little endian ++ * bitmap_from_le(dst, src, nbits) Convert bitmap from little endian ++ * bitmap_copy_with_src_offset(dst, src, offset, nbits) ++ * *dst = *src (with an offset into src) ++ * bitmap_copy_with_dst_offset(dst, src, offset, nbits) ++ * *dst = *src (with an offset into dst) ++ */ ++ ++/* ++ * Also the following operations apply to bitmaps. ++ * ++ * set_bit(bit, addr) *addr |= bit ++ * clear_bit(bit, addr) *addr &= ~bit ++ * change_bit(bit, addr) *addr ^= bit ++ * test_bit(bit, addr) Is bit set in *addr? ++ * test_and_set_bit(bit, addr) Set bit and return old value ++ * test_and_clear_bit(bit, addr) Clear bit and return old value ++ * test_and_change_bit(bit, addr) Change bit and return old value ++ * find_first_zero_bit(addr, nbits) Position first zero bit in *addr ++ * find_first_bit(addr, nbits) Position first set bit in *addr ++ * find_next_zero_bit(addr, nbits, bit) Position next zero bit in *addr >= bit ++ * find_next_bit(addr, nbits, bit) Position next set bit in *addr >= bit ++ */ ++ ++#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1))) ++#define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1))) ++ ++#define DECLARE_BITMAP(name,bits) \ ++ unsigned long name[BITS_TO_LONGS(bits)] ++ ++#define small_nbits(nbits) \ ++ ((nbits) <= BITS_PER_LONG) ++ ++int slow_bitmap_empty(const unsigned long *bitmap, long bits); ++int slow_bitmap_full(const unsigned long *bitmap, long bits); ++int slow_bitmap_equal(const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits); ++void slow_bitmap_complement(unsigned long *dst, const unsigned long *src, ++ long bits); ++int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits); ++void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits); ++void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits); ++int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits); ++int slow_bitmap_intersects(const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits); ++long slow_bitmap_count_one(const unsigned long *bitmap, long nbits); ++ ++static inline unsigned long *bitmap_try_new(long nbits) ++{ ++ long nelem = BITS_TO_LONGS(nbits); ++ return g_try_new0(unsigned long, nelem); ++} ++ ++static inline unsigned long *bitmap_new(long nbits) ++{ ++ long nelem = BITS_TO_LONGS(nbits); ++ return g_new0(unsigned long, nelem); ++} ++ ++static inline void bitmap_zero(unsigned long *dst, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ *dst = 0UL; ++ } else { ++ long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); ++ memset(dst, 0, len); ++ } ++} ++ ++static inline void bitmap_fill(unsigned long *dst, long nbits) ++{ ++ size_t nlongs = BITS_TO_LONGS(nbits); ++ if (!small_nbits(nbits)) { ++ long len = (nlongs - 1) * sizeof(unsigned long); ++ memset(dst, 0xff, len); ++ } ++ dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits); ++} ++ ++static inline void bitmap_copy(unsigned long *dst, const unsigned long *src, ++ long nbits) ++{ ++ if (small_nbits(nbits)) { ++ *dst = *src; ++ } else { ++ long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); ++ memcpy(dst, src, len); ++ } ++} ++ ++static inline int bitmap_and(unsigned long *dst, const unsigned long *src1, ++ const unsigned long *src2, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ return (*dst = *src1 & *src2) != 0; ++ } ++ return slow_bitmap_and(dst, src1, src2, nbits); ++} ++ ++static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, ++ const unsigned long *src2, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ *dst = *src1 | *src2; ++ } else { ++ slow_bitmap_or(dst, src1, src2, nbits); ++ } ++} ++ ++static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1, ++ const unsigned long *src2, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ *dst = *src1 ^ *src2; ++ } else { ++ slow_bitmap_xor(dst, src1, src2, nbits); ++ } ++} ++ ++static inline int bitmap_andnot(unsigned long *dst, const unsigned long *src1, ++ const unsigned long *src2, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ return (*dst = *src1 & ~(*src2)) != 0; ++ } ++ return slow_bitmap_andnot(dst, src1, src2, nbits); ++} ++ ++static inline void bitmap_complement(unsigned long *dst, ++ const unsigned long *src, ++ long nbits) ++{ ++ if (small_nbits(nbits)) { ++ *dst = ~(*src) & BITMAP_LAST_WORD_MASK(nbits); ++ } else { ++ slow_bitmap_complement(dst, src, nbits); ++ } ++} ++ ++static inline int bitmap_equal(const unsigned long *src1, ++ const unsigned long *src2, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ return ! ((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits)); ++ } else { ++ return slow_bitmap_equal(src1, src2, nbits); ++ } ++} ++ ++static inline int bitmap_empty(const unsigned long *src, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ return ! (*src & BITMAP_LAST_WORD_MASK(nbits)); ++ } else { ++ return slow_bitmap_empty(src, nbits); ++ } ++} ++ ++static inline int bitmap_full(const unsigned long *src, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits)); ++ } else { ++ return slow_bitmap_full(src, nbits); ++ } ++} ++ ++static inline int bitmap_intersects(const unsigned long *src1, ++ const unsigned long *src2, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ return ((*src1 & *src2) & BITMAP_LAST_WORD_MASK(nbits)) != 0; ++ } else { ++ return slow_bitmap_intersects(src1, src2, nbits); ++ } ++} ++ ++static inline long bitmap_count_one(const unsigned long *bitmap, long nbits) ++{ ++ if (unlikely(!nbits)) { ++ return 0; ++ } ++ ++ if (small_nbits(nbits)) { ++ return ctpopl(*bitmap & BITMAP_LAST_WORD_MASK(nbits)); ++ } else { ++ return slow_bitmap_count_one(bitmap, nbits); ++ } ++} ++ ++static inline long bitmap_count_one_with_offset(const unsigned long *bitmap, ++ long offset, long nbits) ++{ ++ long aligned_offset = QEMU_ALIGN_DOWN(offset, BITS_PER_LONG); ++ long redundant_bits = offset - aligned_offset; ++ long bits_to_count = nbits + redundant_bits; ++ const unsigned long *bitmap_start = bitmap + ++ aligned_offset / BITS_PER_LONG; ++ ++ return bitmap_count_one(bitmap_start, bits_to_count) - ++ bitmap_count_one(bitmap_start, redundant_bits); ++} ++ ++void bitmap_set(unsigned long *map, long i, long len); ++void bitmap_set_atomic(unsigned long *map, long i, long len); ++void bitmap_clear(unsigned long *map, long start, long nr); ++bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr); ++bool bitmap_test_and_clear(unsigned long *map, long start, long nr); ++void bitmap_copy_and_clear_atomic(unsigned long *dst, unsigned long *src, ++ long nr); ++unsigned long bitmap_find_next_zero_area(unsigned long *map, ++ unsigned long size, ++ unsigned long start, ++ unsigned long nr, ++ unsigned long align_mask); ++ ++static inline unsigned long *bitmap_zero_extend(unsigned long *old, ++ long old_nbits, long new_nbits) ++{ ++ long new_nelem = BITS_TO_LONGS(new_nbits); ++ unsigned long *ptr = g_renew(unsigned long, old, new_nelem); ++ bitmap_clear(ptr, old_nbits, new_nbits - old_nbits); ++ return ptr; ++} ++ ++void bitmap_to_le(unsigned long *dst, const unsigned long *src, ++ long nbits); ++void bitmap_from_le(unsigned long *dst, const unsigned long *src, ++ long nbits); ++ ++void bitmap_copy_with_src_offset(unsigned long *dst, const unsigned long *src, ++ unsigned long offset, unsigned long nbits); ++void bitmap_copy_with_dst_offset(unsigned long *dst, const unsigned long *src, ++ unsigned long shift, unsigned long nbits); ++ ++#endif /* BITMAP_H */ +diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h +new file mode 100644 +index 00000000..2c0a2fe7 +--- /dev/null ++++ b/include/qemu/bitops.h +@@ -0,0 +1,634 @@ ++/* ++ * Bitops Module ++ * ++ * Copyright (C) 2010 Corentin Chary ++ * ++ * Mostly inspired by (stolen from) linux/bitmap.h and linux/bitops.h ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BITOPS_H ++#define BITOPS_H ++ ++ ++#include "host-utils.h" ++#include "atomic.h" ++ ++#define BITS_PER_BYTE CHAR_BIT ++#define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE) ++ ++#define BIT(nr) (1UL << (nr)) ++#define BIT_ULL(nr) (1ULL << (nr)) ++#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) ++#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) ++#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) ++ ++#define MAKE_64BIT_MASK(shift, length) \ ++ (((~0ULL) >> (64 - (length))) << (shift)) ++ ++/** ++ * set_bit - Set a bit in memory ++ * @nr: the bit to set ++ * @addr: the address to start counting from ++ */ ++static inline void set_bit(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ ++ *p |= mask; ++} ++ ++/** ++ * set_bit_atomic - Set a bit in memory atomically ++ * @nr: the bit to set ++ * @addr: the address to start counting from ++ */ ++static inline void set_bit_atomic(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ ++ qatomic_or(p, mask); ++} ++ ++/** ++ * clear_bit - Clears a bit in memory ++ * @nr: Bit to clear ++ * @addr: Address to start counting from ++ */ ++static inline void clear_bit(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ ++ *p &= ~mask; ++} ++ ++/** ++ * clear_bit_atomic - Clears a bit in memory atomically ++ * @nr: Bit to clear ++ * @addr: Address to start counting from ++ */ ++static inline void clear_bit_atomic(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ ++ return qatomic_and(p, ~mask); ++} ++ ++/** ++ * change_bit - Toggle a bit in memory ++ * @nr: Bit to change ++ * @addr: Address to start counting from ++ */ ++static inline void change_bit(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ ++ *p ^= mask; ++} ++ ++/** ++ * test_and_set_bit - Set a bit and return its old value ++ * @nr: Bit to set ++ * @addr: Address to count from ++ */ ++static inline int test_and_set_bit(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ unsigned long old = *p; ++ ++ *p = old | mask; ++ return (old & mask) != 0; ++} ++ ++/** ++ * test_and_clear_bit - Clear a bit and return its old value ++ * @nr: Bit to clear ++ * @addr: Address to count from ++ */ ++static inline int test_and_clear_bit(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ unsigned long old = *p; ++ ++ *p = old & ~mask; ++ return (old & mask) != 0; ++} ++ ++/** ++ * test_and_change_bit - Change a bit and return its old value ++ * @nr: Bit to change ++ * @addr: Address to count from ++ */ ++static inline int test_and_change_bit(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ unsigned long old = *p; ++ ++ *p = old ^ mask; ++ return (old & mask) != 0; ++} ++ ++/** ++ * test_bit - Determine whether a bit is set ++ * @nr: bit number to test ++ * @addr: Address to start counting from ++ */ ++static inline int test_bit(long nr, const unsigned long *addr) ++{ ++ return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); ++} ++ ++/** ++ * find_last_bit - find the last set bit in a memory region ++ * @addr: The address to start the search at ++ * @size: The maximum size to search ++ * ++ * Returns the bit number of the last set bit, ++ * or @size if there is no set bit in the bitmap. ++ */ ++unsigned long find_last_bit(const unsigned long *addr, ++ unsigned long size); ++ ++/** ++ * find_next_bit - find the next set bit in a memory region ++ * @addr: The address to base the search on ++ * @offset: The bitnumber to start searching at ++ * @size: The bitmap size in bits ++ * ++ * Returns the bit number of the next set bit, ++ * or @size if there are no further set bits in the bitmap. ++ */ ++unsigned long find_next_bit(const unsigned long *addr, ++ unsigned long size, ++ unsigned long offset); ++ ++/** ++ * find_next_zero_bit - find the next cleared bit in a memory region ++ * @addr: The address to base the search on ++ * @offset: The bitnumber to start searching at ++ * @size: The bitmap size in bits ++ * ++ * Returns the bit number of the next cleared bit, ++ * or @size if there are no further clear bits in the bitmap. ++ */ ++ ++unsigned long find_next_zero_bit(const unsigned long *addr, ++ unsigned long size, ++ unsigned long offset); ++ ++/** ++ * find_first_bit - find the first set bit in a memory region ++ * @addr: The address to start the search at ++ * @size: The maximum size to search ++ * ++ * Returns the bit number of the first set bit, ++ * or @size if there is no set bit in the bitmap. ++ */ ++static inline unsigned long find_first_bit(const unsigned long *addr, ++ unsigned long size) ++{ ++ unsigned long result, tmp; ++ ++ for (result = 0; result < size; result += BITS_PER_LONG) { ++ tmp = *addr++; ++ if (tmp) { ++ result += ctzl(tmp); ++ return result < size ? result : size; ++ } ++ } ++ /* Not found */ ++ return size; ++} ++ ++/** ++ * find_first_zero_bit - find the first cleared bit in a memory region ++ * @addr: The address to start the search at ++ * @size: The maximum size to search ++ * ++ * Returns the bit number of the first cleared bit, ++ * or @size if there is no clear bit in the bitmap. ++ */ ++static inline unsigned long find_first_zero_bit(const unsigned long *addr, ++ unsigned long size) ++{ ++ return find_next_zero_bit(addr, size, 0); ++} ++ ++/** ++ * rol8 - rotate an 8-bit value left ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint8_t rol8(uint8_t word, unsigned int shift) ++{ ++ return (word << (shift & 7)) | (word >> (-shift & 7)); ++} ++ ++/** ++ * ror8 - rotate an 8-bit value right ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint8_t ror8(uint8_t word, unsigned int shift) ++{ ++ return (word >> (shift & 7)) | (word << (-shift & 7)); ++} ++ ++/** ++ * rol16 - rotate a 16-bit value left ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint16_t rol16(uint16_t word, unsigned int shift) ++{ ++ return (word << (shift & 15)) | (word >> (-shift & 15)); ++} ++ ++/** ++ * ror16 - rotate a 16-bit value right ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint16_t ror16(uint16_t word, unsigned int shift) ++{ ++ return (word >> (shift & 15)) | (word << (-shift & 15)); ++} ++ ++/** ++ * rol32 - rotate a 32-bit value left ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint32_t rol32(uint32_t word, unsigned int shift) ++{ ++ return (word << (shift & 31)) | (word >> (-shift & 31)); ++} ++ ++/** ++ * ror32 - rotate a 32-bit value right ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint32_t ror32(uint32_t word, unsigned int shift) ++{ ++ return (word >> (shift & 31)) | (word << (-shift & 31)); ++} ++ ++/** ++ * rol64 - rotate a 64-bit value left ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint64_t rol64(uint64_t word, unsigned int shift) ++{ ++ return (word << (shift & 63)) | (word >> (-shift & 63)); ++} ++ ++/** ++ * ror64 - rotate a 64-bit value right ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint64_t ror64(uint64_t word, unsigned int shift) ++{ ++ return (word >> (shift & 63)) | (word << (-shift & 63)); ++} ++ ++/** ++ * hswap32 - swap 16-bit halfwords within a 32-bit value ++ * @h: value to swap ++ */ ++static inline uint32_t hswap32(uint32_t h) ++{ ++ return rol32(h, 16); ++} ++ ++/** ++ * hswap64 - swap 16-bit halfwords within a 64-bit value ++ * @h: value to swap ++ */ ++static inline uint64_t hswap64(uint64_t h) ++{ ++ uint64_t m = 0x0000ffff0000ffffull; ++ h = rol64(h, 32); ++ return ((h & m) << 16) | ((h >> 16) & m); ++} ++ ++/** ++ * wswap64 - swap 32-bit words within a 64-bit value ++ * @h: value to swap ++ */ ++static inline uint64_t wswap64(uint64_t h) ++{ ++ return rol64(h, 32); ++} ++ ++/** ++ * extract32: ++ * @value: the value to extract the bit field from ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * ++ * Extract from the 32 bit input @value the bit field specified by the ++ * @start and @length parameters, and return it. The bit field must ++ * lie entirely within the 32 bit word. It is valid to request that ++ * all 32 bits are returned (ie @length 32 and @start 0). ++ * ++ * Returns: the value of the bit field extracted from the input value. ++ */ ++static inline uint32_t extract32(uint32_t value, int start, int length) ++{ ++ assert(start >= 0 && length > 0 && length <= 32 - start); ++ return (value >> start) & (~0U >> (32 - length)); ++} ++ ++/** ++ * extract8: ++ * @value: the value to extract the bit field from ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * ++ * Extract from the 8 bit input @value the bit field specified by the ++ * @start and @length parameters, and return it. The bit field must ++ * lie entirely within the 8 bit word. It is valid to request that ++ * all 8 bits are returned (ie @length 8 and @start 0). ++ * ++ * Returns: the value of the bit field extracted from the input value. ++ */ ++static inline uint8_t extract8(uint8_t value, int start, int length) ++{ ++ assert(start >= 0 && length > 0 && length <= 8 - start); ++ return extract32(value, start, length); ++} ++ ++/** ++ * extract16: ++ * @value: the value to extract the bit field from ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * ++ * Extract from the 16 bit input @value the bit field specified by the ++ * @start and @length parameters, and return it. The bit field must ++ * lie entirely within the 16 bit word. It is valid to request that ++ * all 16 bits are returned (ie @length 16 and @start 0). ++ * ++ * Returns: the value of the bit field extracted from the input value. ++ */ ++static inline uint16_t extract16(uint16_t value, int start, int length) ++{ ++ assert(start >= 0 && length > 0 && length <= 16 - start); ++ return extract32(value, start, length); ++} ++ ++/** ++ * extract64: ++ * @value: the value to extract the bit field from ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * ++ * Extract from the 64 bit input @value the bit field specified by the ++ * @start and @length parameters, and return it. The bit field must ++ * lie entirely within the 64 bit word. It is valid to request that ++ * all 64 bits are returned (ie @length 64 and @start 0). ++ * ++ * Returns: the value of the bit field extracted from the input value. ++ */ ++static inline uint64_t extract64(uint64_t value, int start, int length) ++{ ++ assert(start >= 0 && length > 0 && length <= 64 - start); ++ return (value >> start) & (~0ULL >> (64 - length)); ++} ++ ++/** ++ * sextract32: ++ * @value: the value to extract the bit field from ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * ++ * Extract from the 32 bit input @value the bit field specified by the ++ * @start and @length parameters, and return it, sign extended to ++ * an int32_t (ie with the most significant bit of the field propagated ++ * to all the upper bits of the return value). The bit field must lie ++ * entirely within the 32 bit word. It is valid to request that ++ * all 32 bits are returned (ie @length 32 and @start 0). ++ * ++ * Returns: the sign extended value of the bit field extracted from the ++ * input value. ++ */ ++static inline int32_t sextract32(uint32_t value, int start, int length) ++{ ++ assert(start >= 0 && length > 0 && length <= 32 - start); ++ /* Note that this implementation relies on right shift of signed ++ * integers being an arithmetic shift. ++ */ ++ return ((int32_t)(value << (32 - length - start))) >> (32 - length); ++} ++ ++/** ++ * sextract64: ++ * @value: the value to extract the bit field from ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * ++ * Extract from the 64 bit input @value the bit field specified by the ++ * @start and @length parameters, and return it, sign extended to ++ * an int64_t (ie with the most significant bit of the field propagated ++ * to all the upper bits of the return value). The bit field must lie ++ * entirely within the 64 bit word. It is valid to request that ++ * all 64 bits are returned (ie @length 64 and @start 0). ++ * ++ * Returns: the sign extended value of the bit field extracted from the ++ * input value. ++ */ ++static inline int64_t sextract64(uint64_t value, int start, int length) ++{ ++ assert(start >= 0 && length > 0 && length <= 64 - start); ++ /* Note that this implementation relies on right shift of signed ++ * integers being an arithmetic shift. ++ */ ++ return ((int64_t)(value << (64 - length - start))) >> (64 - length); ++} ++ ++/** ++ * deposit32: ++ * @value: initial value to insert bit field into ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * @fieldval: the value to insert into the bit field ++ * ++ * Deposit @fieldval into the 32 bit @value at the bit field specified ++ * by the @start and @length parameters, and return the modified ++ * @value. Bits of @value outside the bit field are not modified. ++ * Bits of @fieldval above the least significant @length bits are ++ * ignored. The bit field must lie entirely within the 32 bit word. ++ * It is valid to request that all 32 bits are modified (ie @length ++ * 32 and @start 0). ++ * ++ * Returns: the modified @value. ++ */ ++static inline uint32_t deposit32(uint32_t value, int start, int length, ++ uint32_t fieldval) ++{ ++ uint32_t mask; ++ assert(start >= 0 && length > 0 && length <= 32 - start); ++ mask = (~0U >> (32 - length)) << start; ++ return (value & ~mask) | ((fieldval << start) & mask); ++} ++ ++/** ++ * deposit64: ++ * @value: initial value to insert bit field into ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * @fieldval: the value to insert into the bit field ++ * ++ * Deposit @fieldval into the 64 bit @value at the bit field specified ++ * by the @start and @length parameters, and return the modified ++ * @value. Bits of @value outside the bit field are not modified. ++ * Bits of @fieldval above the least significant @length bits are ++ * ignored. The bit field must lie entirely within the 64 bit word. ++ * It is valid to request that all 64 bits are modified (ie @length ++ * 64 and @start 0). ++ * ++ * Returns: the modified @value. ++ */ ++static inline uint64_t deposit64(uint64_t value, int start, int length, ++ uint64_t fieldval) ++{ ++ uint64_t mask; ++ assert(start >= 0 && length > 0 && length <= 64 - start); ++ mask = (~0ULL >> (64 - length)) << start; ++ return (value & ~mask) | ((fieldval << start) & mask); ++} ++ ++/** ++ * half_shuffle32: ++ * @x: 32-bit value (of which only the bottom 16 bits are of interest) ++ * ++ * Given an input value:: ++ * ++ * xxxx xxxx xxxx xxxx ABCD EFGH IJKL MNOP ++ * ++ * return the value where the bottom 16 bits are spread out into ++ * the odd bits in the word, and the even bits are zeroed:: ++ * ++ * 0A0B 0C0D 0E0F 0G0H 0I0J 0K0L 0M0N 0O0P ++ * ++ * Any bits set in the top half of the input are ignored. ++ * ++ * Returns: the shuffled bits. ++ */ ++static inline uint32_t half_shuffle32(uint32_t x) ++{ ++ /* This algorithm is from _Hacker's Delight_ section 7-2 "Shuffling Bits". ++ * It ignores any bits set in the top half of the input. ++ */ ++ x = ((x & 0xFF00) << 8) | (x & 0x00FF); ++ x = ((x << 4) | x) & 0x0F0F0F0F; ++ x = ((x << 2) | x) & 0x33333333; ++ x = ((x << 1) | x) & 0x55555555; ++ return x; ++} ++ ++/** ++ * half_shuffle64: ++ * @x: 64-bit value (of which only the bottom 32 bits are of interest) ++ * ++ * Given an input value:: ++ * ++ * xxxx xxxx xxxx .... xxxx xxxx ABCD EFGH IJKL MNOP QRST UVWX YZab cdef ++ * ++ * return the value where the bottom 32 bits are spread out into ++ * the odd bits in the word, and the even bits are zeroed:: ++ * ++ * 0A0B 0C0D 0E0F 0G0H 0I0J 0K0L 0M0N .... 0U0V 0W0X 0Y0Z 0a0b 0c0d 0e0f ++ * ++ * Any bits set in the top half of the input are ignored. ++ * ++ * Returns: the shuffled bits. ++ */ ++static inline uint64_t half_shuffle64(uint64_t x) ++{ ++ /* This algorithm is from _Hacker's Delight_ section 7-2 "Shuffling Bits". ++ * It ignores any bits set in the top half of the input. ++ */ ++ x = ((x & 0xFFFF0000ULL) << 16) | (x & 0xFFFF); ++ x = ((x << 8) | x) & 0x00FF00FF00FF00FFULL; ++ x = ((x << 4) | x) & 0x0F0F0F0F0F0F0F0FULL; ++ x = ((x << 2) | x) & 0x3333333333333333ULL; ++ x = ((x << 1) | x) & 0x5555555555555555ULL; ++ return x; ++} ++ ++/** ++ * half_unshuffle32: ++ * @x: 32-bit value (of which only the odd bits are of interest) ++ * ++ * Given an input value:: ++ * ++ * xAxB xCxD xExF xGxH xIxJ xKxL xMxN xOxP ++ * ++ * return the value where all the odd bits are compressed down ++ * into the low half of the word, and the high half is zeroed:: ++ * ++ * 0000 0000 0000 0000 ABCD EFGH IJKL MNOP ++ * ++ * Any even bits set in the input are ignored. ++ * ++ * Returns: the unshuffled bits. ++ */ ++static inline uint32_t half_unshuffle32(uint32_t x) ++{ ++ /* This algorithm is from _Hacker's Delight_ section 7-2 "Shuffling Bits". ++ * where it is called an inverse half shuffle. ++ */ ++ x &= 0x55555555; ++ x = ((x >> 1) | x) & 0x33333333; ++ x = ((x >> 2) | x) & 0x0F0F0F0F; ++ x = ((x >> 4) | x) & 0x00FF00FF; ++ x = ((x >> 8) | x) & 0x0000FFFF; ++ return x; ++} ++ ++/** ++ * half_unshuffle64: ++ * @x: 64-bit value (of which only the odd bits are of interest) ++ * ++ * Given an input value:: ++ * ++ * xAxB xCxD xExF xGxH xIxJ xKxL xMxN .... xUxV xWxX xYxZ xaxb xcxd xexf ++ * ++ * return the value where all the odd bits are compressed down ++ * into the low half of the word, and the high half is zeroed:: ++ * ++ * 0000 0000 0000 .... 0000 0000 ABCD EFGH IJKL MNOP QRST UVWX YZab cdef ++ * ++ * Any even bits set in the input are ignored. ++ * ++ * Returns: the unshuffled bits. ++ */ ++static inline uint64_t half_unshuffle64(uint64_t x) ++{ ++ /* This algorithm is from _Hacker's Delight_ section 7-2 "Shuffling Bits". ++ * where it is called an inverse half shuffle. ++ */ ++ x &= 0x5555555555555555ULL; ++ x = ((x >> 1) | x) & 0x3333333333333333ULL; ++ x = ((x >> 2) | x) & 0x0F0F0F0F0F0F0F0FULL; ++ x = ((x >> 4) | x) & 0x00FF00FF00FF00FFULL; ++ x = ((x >> 8) | x) & 0x0000FFFF0000FFFFULL; ++ x = ((x >> 16) | x) & 0x00000000FFFFFFFFULL; ++ return x; ++} ++ ++#endif +diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h +new file mode 100644 +index 00000000..ad22910a +--- /dev/null ++++ b/include/qemu/bswap.h +@@ -0,0 +1,436 @@ ++#ifndef BSWAP_H ++#define BSWAP_H ++ ++#undef bswap16 ++#define bswap16(_x) __builtin_bswap16(_x) ++#undef bswap32 ++#define bswap32(_x) __builtin_bswap32(_x) ++#undef bswap64 ++#define bswap64(_x) __builtin_bswap64(_x) ++ ++static inline uint32_t bswap24(uint32_t x) ++{ ++ return (((x & 0x000000ffU) << 16) | ++ ((x & 0x0000ff00U) << 0) | ++ ((x & 0x00ff0000U) >> 16)); ++} ++ ++static inline void bswap16s(uint16_t *s) ++{ ++ *s = __builtin_bswap16(*s); ++} ++ ++static inline void bswap24s(uint32_t *s) ++{ ++ *s = bswap24(*s & 0x00ffffffU); ++} ++ ++static inline void bswap32s(uint32_t *s) ++{ ++ *s = __builtin_bswap32(*s); ++} ++ ++static inline void bswap64s(uint64_t *s) ++{ ++ *s = __builtin_bswap64(*s); ++} ++ ++#if HOST_BIG_ENDIAN ++#define be_bswap(v, size) (v) ++#define le_bswap(v, size) glue(__builtin_bswap, size)(v) ++#define be_bswap24(v) (v) ++#define le_bswap24(v) bswap24(v) ++#define be_bswaps(v, size) ++#define le_bswaps(p, size) \ ++ do { *p = glue(__builtin_bswap, size)(*p); } while (0) ++#else ++#define le_bswap(v, size) (v) ++#define be_bswap24(v) bswap24(v) ++#define le_bswap24(v) (v) ++#define be_bswap(v, size) glue(__builtin_bswap, size)(v) ++#define le_bswaps(v, size) ++#define be_bswaps(p, size) \ ++ do { *p = glue(__builtin_bswap, size)(*p); } while (0) ++#endif ++ ++/** ++ * Endianness conversion functions between host cpu and specified endianness. ++ * (We list the complete set of prototypes produced by the macros below ++ * to assist people who search the headers to find their definitions.) ++ * ++ * uint16_t le16_to_cpu(uint16_t v); ++ * uint32_t le32_to_cpu(uint32_t v); ++ * uint64_t le64_to_cpu(uint64_t v); ++ * uint16_t be16_to_cpu(uint16_t v); ++ * uint32_t be32_to_cpu(uint32_t v); ++ * uint64_t be64_to_cpu(uint64_t v); ++ * ++ * Convert the value @v from the specified format to the native ++ * endianness of the host CPU by byteswapping if necessary, and ++ * return the converted value. ++ * ++ * uint16_t cpu_to_le16(uint16_t v); ++ * uint32_t cpu_to_le32(uint32_t v); ++ * uint64_t cpu_to_le64(uint64_t v); ++ * uint16_t cpu_to_be16(uint16_t v); ++ * uint32_t cpu_to_be32(uint32_t v); ++ * uint64_t cpu_to_be64(uint64_t v); ++ * ++ * Convert the value @v from the native endianness of the host CPU to ++ * the specified format by byteswapping if necessary, and return ++ * the converted value. ++ * ++ * void le16_to_cpus(uint16_t *v); ++ * void le32_to_cpus(uint32_t *v); ++ * void le64_to_cpus(uint64_t *v); ++ * void be16_to_cpus(uint16_t *v); ++ * void be32_to_cpus(uint32_t *v); ++ * void be64_to_cpus(uint64_t *v); ++ * ++ * Do an in-place conversion of the value pointed to by @v from the ++ * specified format to the native endianness of the host CPU. ++ * ++ * void cpu_to_le16s(uint16_t *v); ++ * void cpu_to_le32s(uint32_t *v); ++ * void cpu_to_le64s(uint64_t *v); ++ * void cpu_to_be16s(uint16_t *v); ++ * void cpu_to_be32s(uint32_t *v); ++ * void cpu_to_be64s(uint64_t *v); ++ * ++ * Do an in-place conversion of the value pointed to by @v from the ++ * native endianness of the host CPU to the specified format. ++ * ++ * Both X_to_cpu() and cpu_to_X() perform the same operation; you ++ * should use whichever one is better documenting of the function your ++ * code is performing. ++ * ++ * Do not use these functions for conversion of values which are in guest ++ * memory, since the data may not be sufficiently aligned for the host CPU's ++ * load and store instructions. Instead you should use the ld*_p() and ++ * st*_p() functions, which perform loads and stores of data of any ++ * required size and endianness and handle possible misalignment. ++ */ ++ ++#define CPU_CONVERT(endian, size, type)\ ++static inline type endian ## size ## _to_cpu(type v)\ ++{\ ++ return glue(endian, _bswap)(v, size);\ ++}\ ++\ ++static inline type cpu_to_ ## endian ## size(type v)\ ++{\ ++ return glue(endian, _bswap)(v, size);\ ++}\ ++\ ++static inline void endian ## size ## _to_cpus(type *p)\ ++{\ ++ glue(endian, _bswaps)(p, size);\ ++}\ ++\ ++static inline void cpu_to_ ## endian ## size ## s(type *p)\ ++{\ ++ glue(endian, _bswaps)(p, size);\ ++} ++ ++CPU_CONVERT(be, 16, uint16_t) ++CPU_CONVERT(be, 32, uint32_t) ++CPU_CONVERT(be, 64, uint64_t) ++ ++CPU_CONVERT(le, 16, uint16_t) ++CPU_CONVERT(le, 32, uint32_t) ++CPU_CONVERT(le, 64, uint64_t) ++ ++/* ++ * Same as cpu_to_le{16,32,64}, except that gcc will figure the result is ++ * a compile-time constant if you pass in a constant. So this can be ++ * used to initialize static variables. ++ */ ++#if HOST_BIG_ENDIAN ++# define const_le64(_x) \ ++ ((((_x) & 0x00000000000000ffULL) << 56) | \ ++ (((_x) & 0x000000000000ff00ULL) << 40) | \ ++ (((_x) & 0x0000000000ff0000ULL) << 24) | \ ++ (((_x) & 0x00000000ff000000ULL) << 8) | \ ++ (((_x) & 0x000000ff00000000ULL) >> 8) | \ ++ (((_x) & 0x0000ff0000000000ULL) >> 24) | \ ++ (((_x) & 0x00ff000000000000ULL) >> 40) | \ ++ (((_x) & 0xff00000000000000ULL) >> 56)) ++# define const_le32(_x) \ ++ ((((_x) & 0x000000ffU) << 24) | \ ++ (((_x) & 0x0000ff00U) << 8) | \ ++ (((_x) & 0x00ff0000U) >> 8) | \ ++ (((_x) & 0xff000000U) >> 24)) ++# define const_le16(_x) \ ++ ((((_x) & 0x00ff) << 8) | \ ++ (((_x) & 0xff00) >> 8)) ++#else ++# define const_le64(_x) (_x) ++# define const_le32(_x) (_x) ++# define const_le16(_x) (_x) ++#endif ++ ++/* unaligned/endian-independent pointer access */ ++ ++/* ++ * the generic syntax is: ++ * ++ * load: ld{type}{sign}{size}_{endian}_p(ptr) ++ * ++ * store: st{type}{size}_{endian}_p(ptr, val) ++ * ++ * Note there are small differences with the softmmu access API! ++ * ++ * type is: ++ * (empty): integer access ++ * f : float access ++ * ++ * sign is: ++ * (empty): for 32 or 64 bit sizes (including floats and doubles) ++ * u : unsigned ++ * s : signed ++ * ++ * size is: ++ * b: 8 bits ++ * w: 16 bits ++ * 24: 24 bits ++ * l: 32 bits ++ * q: 64 bits ++ * ++ * endian is: ++ * he : host endian ++ * be : big endian ++ * le : little endian ++ * te : target endian ++ * (except for byte accesses, which have no endian infix). ++ * ++ * The target endian accessors are obviously only available to source ++ * files which are built per-target; they are defined in cpu-all.h. ++ * ++ * In all cases these functions take a host pointer. ++ * For accessors that take a guest address rather than a ++ * host address, see the cpu_{ld,st}_* accessors defined in ++ * cpu_ldst.h. ++ * ++ * For cases where the size to be used is not fixed at compile time, ++ * there are ++ * stn_{endian}_p(ptr, sz, val) ++ * which stores @val to @ptr as an @endian-order number @sz bytes in size ++ * and ++ * ldn_{endian}_p(ptr, sz) ++ * which loads @sz bytes from @ptr as an unsigned @endian-order number ++ * and returns it in a uint64_t. ++ */ ++ ++static inline int ldub_p(const void *ptr) ++{ ++ return *(uint8_t *)ptr; ++} ++ ++static inline int ldsb_p(const void *ptr) ++{ ++ return *(int8_t *)ptr; ++} ++ ++static inline void stb_p(void *ptr, uint8_t v) ++{ ++ *(uint8_t *)ptr = v; ++} ++ ++/* ++ * Any compiler worth its salt will turn these memcpy into native unaligned ++ * operations. Thus we don't need to play games with packed attributes, or ++ * inline byte-by-byte stores. ++ * Some compilation environments (eg some fortify-source implementations) ++ * may intercept memcpy() in a way that defeats the compiler optimization, ++ * though, so we use __builtin_memcpy() to give ourselves the best chance ++ * of good performance. ++ */ ++ ++static inline int lduw_he_p(const void *ptr) ++{ ++ uint16_t r; ++ __builtin_memcpy(&r, ptr, sizeof(r)); ++ return r; ++} ++ ++static inline int ldsw_he_p(const void *ptr) ++{ ++ int16_t r; ++ __builtin_memcpy(&r, ptr, sizeof(r)); ++ return r; ++} ++ ++static inline void stw_he_p(void *ptr, uint16_t v) ++{ ++ __builtin_memcpy(ptr, &v, sizeof(v)); ++} ++ ++static inline void st24_he_p(void *ptr, uint32_t v) ++{ ++ __builtin_memcpy(ptr, &v, 3); ++} ++ ++static inline int ldl_he_p(const void *ptr) ++{ ++ int32_t r; ++ __builtin_memcpy(&r, ptr, sizeof(r)); ++ return r; ++} ++ ++static inline void stl_he_p(void *ptr, uint32_t v) ++{ ++ __builtin_memcpy(ptr, &v, sizeof(v)); ++} ++ ++static inline uint64_t ldq_he_p(const void *ptr) ++{ ++ uint64_t r; ++ __builtin_memcpy(&r, ptr, sizeof(r)); ++ return r; ++} ++ ++static inline void stq_he_p(void *ptr, uint64_t v) ++{ ++ __builtin_memcpy(ptr, &v, sizeof(v)); ++} ++ ++static inline int lduw_le_p(const void *ptr) ++{ ++ return (uint16_t)le_bswap(lduw_he_p(ptr), 16); ++} ++ ++static inline int ldsw_le_p(const void *ptr) ++{ ++ return (int16_t)le_bswap(lduw_he_p(ptr), 16); ++} ++ ++static inline int ldl_le_p(const void *ptr) ++{ ++ return le_bswap(ldl_he_p(ptr), 32); ++} ++ ++static inline uint64_t ldq_le_p(const void *ptr) ++{ ++ return le_bswap(ldq_he_p(ptr), 64); ++} ++ ++static inline void stw_le_p(void *ptr, uint16_t v) ++{ ++ stw_he_p(ptr, le_bswap(v, 16)); ++} ++ ++static inline void st24_le_p(void *ptr, uint32_t v) ++{ ++ st24_he_p(ptr, le_bswap24(v)); ++} ++ ++static inline void stl_le_p(void *ptr, uint32_t v) ++{ ++ stl_he_p(ptr, le_bswap(v, 32)); ++} ++ ++static inline void stq_le_p(void *ptr, uint64_t v) ++{ ++ stq_he_p(ptr, le_bswap(v, 64)); ++} ++ ++static inline int lduw_be_p(const void *ptr) ++{ ++ return (uint16_t)be_bswap(lduw_he_p(ptr), 16); ++} ++ ++static inline int ldsw_be_p(const void *ptr) ++{ ++ return (int16_t)be_bswap(lduw_he_p(ptr), 16); ++} ++ ++static inline int ldl_be_p(const void *ptr) ++{ ++ return be_bswap(ldl_he_p(ptr), 32); ++} ++ ++static inline uint64_t ldq_be_p(const void *ptr) ++{ ++ return be_bswap(ldq_he_p(ptr), 64); ++} ++ ++static inline void stw_be_p(void *ptr, uint16_t v) ++{ ++ stw_he_p(ptr, be_bswap(v, 16)); ++} ++ ++static inline void st24_be_p(void *ptr, uint32_t v) ++{ ++ st24_he_p(ptr, be_bswap24(v)); ++} ++ ++static inline void stl_be_p(void *ptr, uint32_t v) ++{ ++ stl_he_p(ptr, be_bswap(v, 32)); ++} ++ ++static inline void stq_be_p(void *ptr, uint64_t v) ++{ ++ stq_he_p(ptr, be_bswap(v, 64)); ++} ++ ++static inline unsigned long leul_to_cpu(unsigned long v) ++{ ++#if HOST_LONG_BITS == 32 ++ return le_bswap(v, 32); ++#elif HOST_LONG_BITS == 64 ++ return le_bswap(v, 64); ++#else ++# error Unknown sizeof long ++#endif ++} ++ ++/* Store v to p as a sz byte value in host order */ ++#define DO_STN_LDN_P(END) \ ++ static inline void stn_## END ## _p(void *ptr, int sz, uint64_t v) \ ++ { \ ++ switch (sz) { \ ++ case 1: \ ++ stb_p(ptr, v); \ ++ break; \ ++ case 2: \ ++ stw_ ## END ## _p(ptr, v); \ ++ break; \ ++ case 4: \ ++ stl_ ## END ## _p(ptr, v); \ ++ break; \ ++ case 8: \ ++ stq_ ## END ## _p(ptr, v); \ ++ break; \ ++ default: \ ++ g_assert_not_reached(); \ ++ } \ ++ } \ ++ static inline uint64_t ldn_## END ## _p(const void *ptr, int sz) \ ++ { \ ++ switch (sz) { \ ++ case 1: \ ++ return ldub_p(ptr); \ ++ case 2: \ ++ return lduw_ ## END ## _p(ptr); \ ++ case 4: \ ++ return (uint32_t)ldl_ ## END ## _p(ptr); \ ++ case 8: \ ++ return ldq_ ## END ## _p(ptr); \ ++ default: \ ++ g_assert_not_reached(); \ ++ } \ ++ } ++ ++DO_STN_LDN_P(he) ++DO_STN_LDN_P(le) ++DO_STN_LDN_P(be) ++ ++#undef DO_STN_LDN_P ++ ++#undef le_bswap ++#undef be_bswap ++#undef le_bswaps ++#undef be_bswaps ++ ++#endif /* BSWAP_H */ +diff --git a/include/qemu/clang-tsa.h b/include/qemu/clang-tsa.h +new file mode 100644 +index 00000000..ba06fb8c +--- /dev/null ++++ b/include/qemu/clang-tsa.h +@@ -0,0 +1,114 @@ ++#ifndef CLANG_TSA_H ++#define CLANG_TSA_H ++ ++/* ++ * Copyright 2018 Jarkko Hietaniemi ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining ++ * a copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without ++ * limitation the rights to use, copy, modify, merge, publish, ++ * distribute, sublicense, and/or sell copies of the Software, and to ++ * permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ++ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ++ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ++ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ++ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++ ++/* http://clang.llvm.org/docs/ThreadSafetyAnalysis.html ++ * ++ * TSA is available since clang 3.6-ish. ++ */ ++#ifdef __clang__ ++# define TSA(x) __attribute__((x)) ++#else ++# define TSA(x) /* No TSA, make TSA attributes no-ops. */ ++#endif ++ ++/* TSA_CAPABILITY() is used to annotate typedefs: ++ * ++ * typedef pthread_mutex_t TSA_CAPABILITY("mutex") tsa_mutex; ++ */ ++#define TSA_CAPABILITY(x) TSA(capability(x)) ++ ++/* TSA_GUARDED_BY() is used to annotate global variables, ++ * the data is guarded: ++ * ++ * Foo foo TSA_GUARDED_BY(mutex); ++ */ ++#define TSA_GUARDED_BY(x) TSA(guarded_by(x)) ++ ++/* TSA_PT_GUARDED_BY() is used to annotate global pointers, the data ++ * behind the pointer is guarded. ++ * ++ * Foo* ptr TSA_PT_GUARDED_BY(mutex); ++ */ ++#define TSA_PT_GUARDED_BY(x) TSA(pt_guarded_by(x)) ++ ++/* The TSA_REQUIRES() is used to annotate functions: the caller of the ++ * function MUST hold the resource, the function will NOT release it. ++ * ++ * More than one mutex may be specified, comma-separated. ++ * ++ * void Foo(void) TSA_REQUIRES(mutex); ++ */ ++#define TSA_REQUIRES(...) TSA(requires_capability(__VA_ARGS__)) ++#define TSA_REQUIRES_SHARED(...) TSA(requires_shared_capability(__VA_ARGS__)) ++ ++/* TSA_EXCLUDES() is used to annotate functions: the caller of the ++ * function MUST NOT hold resource, the function first acquires the ++ * resource, and then releases it. ++ * ++ * More than one mutex may be specified, comma-separated. ++ * ++ * void Foo(void) TSA_EXCLUDES(mutex); ++ */ ++#define TSA_EXCLUDES(...) TSA(locks_excluded(__VA_ARGS__)) ++ ++/* TSA_ACQUIRE() is used to annotate functions: the caller of the ++ * function MUST NOT hold the resource, the function will acquire the ++ * resource, but NOT release it. ++ * ++ * More than one mutex may be specified, comma-separated. ++ * ++ * void Foo(void) TSA_ACQUIRE(mutex); ++ */ ++#define TSA_ACQUIRE(...) TSA(acquire_capability(__VA_ARGS__)) ++#define TSA_ACQUIRE_SHARED(...) TSA(acquire_shared_capability(__VA_ARGS__)) ++ ++/* TSA_RELEASE() is used to annotate functions: the caller of the ++ * function MUST hold the resource, but the function will then release it. ++ * ++ * More than one mutex may be specified, comma-separated. ++ * ++ * void Foo(void) TSA_RELEASE(mutex); ++ */ ++#define TSA_RELEASE(...) TSA(release_capability(__VA_ARGS__)) ++#define TSA_RELEASE_SHARED(...) TSA(release_shared_capability(__VA_ARGS__)) ++ ++/* TSA_NO_TSA is used to annotate functions. Use only when you need to. ++ * ++ * void Foo(void) TSA_NO_TSA; ++ */ ++#define TSA_NO_TSA TSA(no_thread_safety_analysis) ++ ++/* ++ * TSA_ASSERT() is used to annotate functions: This function will assert that ++ * the lock is held. When it returns, the caller of the function is assumed to ++ * already hold the resource. ++ * ++ * More than one mutex may be specified, comma-separated. ++ */ ++#define TSA_ASSERT(...) TSA(assert_capability(__VA_ARGS__)) ++#define TSA_ASSERT_SHARED(...) TSA(assert_shared_capability(__VA_ARGS__)) ++ ++#endif /* #ifndef CLANG_TSA_H */ +diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h +new file mode 100644 +index 00000000..c06954cc +--- /dev/null ++++ b/include/qemu/compiler.h +@@ -0,0 +1,276 @@ ++/* compiler.h: macros to abstract away compiler specifics ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef COMPILER_H ++#define COMPILER_H ++ ++#define HOST_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) ++ ++/* HOST_LONG_BITS is the size of a native pointer in bits. */ ++#define HOST_LONG_BITS (__SIZEOF_POINTER__ * 8) ++ ++#if defined __clang_analyzer__ || defined __COVERITY__ ++#define QEMU_STATIC_ANALYSIS 1 ++#endif ++ ++#ifdef __cplusplus ++#define QEMU_EXTERN_C extern "C" ++#else ++#define QEMU_EXTERN_C extern ++#endif ++ ++#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) ++# define QEMU_PACKED __attribute__((gcc_struct, packed)) ++#else ++# define QEMU_PACKED __attribute__((packed)) ++#endif ++ ++#define QEMU_ALIGNED(X) __attribute__((aligned(X))) ++ ++#ifndef glue ++#define xglue(x, y) x ## y ++#define glue(x, y) xglue(x, y) ++#define stringify(s) tostring(s) ++#define tostring(s) #s ++#endif ++ ++/* Expands into an identifier stemN, where N is another number each time */ ++#define MAKE_IDENTIFIER(stem) glue(stem, __COUNTER__) ++ ++#ifndef likely ++#define likely(x) __builtin_expect(!!(x), 1) ++#define unlikely(x) __builtin_expect(!!(x), 0) ++#endif ++ ++#ifndef container_of ++#define container_of(ptr, type, member) ({ \ ++ const typeof(((type *) 0)->member) *__mptr = (ptr); \ ++ (type *) ((char *) __mptr - offsetof(type, member));}) ++#endif ++ ++#define sizeof_field(type, field) sizeof(((type *)0)->field) ++ ++/* ++ * Calculate the number of bytes up to and including the given 'field' of ++ * 'container'. ++ */ ++#define endof(container, field) \ ++ (offsetof(container, field) + sizeof_field(container, field)) ++ ++/* Convert from a base type to a parent type, with compile time checking. */ ++#define DO_UPCAST(type, field, dev) ( __extension__ ( { \ ++ char __attribute__((unused)) offset_must_be_zero[ \ ++ -offsetof(type, field)]; \ ++ container_of(dev, type, field);})) ++ ++#define typeof_field(type, field) typeof(((type *)0)->field) ++#define type_check(t1,t2) ((t1*)0 - (t2*)0) ++ ++#define QEMU_BUILD_BUG_ON_STRUCT(x) \ ++ struct { \ ++ int:(x) ? -1 : 1; \ ++ } ++ ++#define QEMU_BUILD_BUG_MSG(x, msg) _Static_assert(!(x), msg) ++ ++#define QEMU_BUILD_BUG_ON(x) QEMU_BUILD_BUG_MSG(x, "not expecting: " #x) ++ ++#define QEMU_BUILD_BUG_ON_ZERO(x) (sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)) - \ ++ sizeof(QEMU_BUILD_BUG_ON_STRUCT(x))) ++ ++#if !defined(__clang__) && defined(_WIN32) ++/* ++ * Map __printf__ to __gnu_printf__ because we want standard format strings even ++ * when MinGW or GLib include files use __printf__. ++ */ ++# define __printf__ __gnu_printf__ ++#endif ++ ++#ifndef __has_warning ++#define __has_warning(x) 0 /* compatibility with non-clang compilers */ ++#endif ++ ++#ifndef __has_feature ++#define __has_feature(x) 0 /* compatibility with non-clang compilers */ ++#endif ++ ++#ifndef __has_builtin ++#define __has_builtin(x) 0 /* compatibility with non-clang compilers */ ++#endif ++ ++#if __has_builtin(__builtin_assume_aligned) || !defined(__clang__) ++#define HAS_ASSUME_ALIGNED ++#endif ++ ++#ifndef __has_attribute ++#define __has_attribute(x) 0 /* compatibility with older GCC */ ++#endif ++ ++#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) ++# define QEMU_SANITIZE_ADDRESS 1 ++#endif ++ ++#if defined(__SANITIZE_THREAD__) || __has_feature(thread_sanitizer) ++# define QEMU_SANITIZE_THREAD 1 ++#endif ++ ++/* ++ * GCC doesn't provide __has_attribute() until GCC 5, but we know all the GCC ++ * versions we support have the "flatten" attribute. Clang may not have the ++ * "flatten" attribute but always has __has_attribute() to check for it. ++ */ ++#if __has_attribute(flatten) || !defined(__clang__) ++# define QEMU_FLATTEN __attribute__((flatten)) ++#else ++# define QEMU_FLATTEN ++#endif ++ ++/* ++ * If __attribute__((error)) is present, use it to produce an error at ++ * compile time. Otherwise, one must wait for the linker to diagnose ++ * the missing symbol. ++ */ ++#if __has_attribute(error) ++# define QEMU_ERROR(X) __attribute__((error(X))) ++#else ++# define QEMU_ERROR(X) ++#endif ++ ++/* ++ * The nonstring variable attribute specifies that an object or member ++ * declaration with type array of char or pointer to char is intended ++ * to store character arrays that do not necessarily contain a terminating ++ * NUL character. This is useful in detecting uses of such arrays or pointers ++ * with functions that expect NUL-terminated strings, and to avoid warnings ++ * when such an array or pointer is used as an argument to a bounded string ++ * manipulation function such as strncpy. ++ */ ++#if __has_attribute(nonstring) ++# define QEMU_NONSTRING __attribute__((nonstring)) ++#else ++# define QEMU_NONSTRING ++#endif ++ ++/* ++ * Forced inlining may be desired to encourage constant propagation ++ * of function parameters. However, it can also make debugging harder, ++ * so disable it for a non-optimizing build. ++ */ ++#if defined(__OPTIMIZE__) ++#define QEMU_ALWAYS_INLINE __attribute__((always_inline)) ++#else ++#define QEMU_ALWAYS_INLINE ++#endif ++ ++/** ++ * In most cases, normal "fallthrough" comments are good enough for ++ * switch-case statements, but sometimes the compiler has problems ++ * with those. In that case you can use QEMU_FALLTHROUGH instead. ++ */ ++#if __has_attribute(fallthrough) ++# define QEMU_FALLTHROUGH __attribute__((fallthrough)) ++#else ++# define QEMU_FALLTHROUGH do {} while (0) /* fallthrough */ ++#endif ++ ++#ifdef CONFIG_CFI ++/* ++ * If CFI is enabled, use an attribute to disable cfi-icall on the following ++ * function ++ */ ++#define QEMU_DISABLE_CFI __attribute__((no_sanitize("cfi-icall"))) ++#else ++/* If CFI is not enabled, use an empty define to not change the behavior */ ++#define QEMU_DISABLE_CFI ++#endif ++ ++/* ++ * Apple clang version 14 has a bug in its __builtin_subcll(); define ++ * BUILTIN_SUBCLL_BROKEN for the offending versions so we can avoid it. ++ * When a version of Apple clang which has this bug fixed is released ++ * we can add an upper bound to this check. ++ * See https://gitlab.com/qemu-project/qemu/-/issues/1631 ++ * and https://gitlab.com/qemu-project/qemu/-/issues/1659 for details. ++ * The bug never made it into any upstream LLVM releases, only Apple ones. ++ */ ++#if defined(__apple_build_version__) && __clang_major__ >= 14 ++#define BUILTIN_SUBCLL_BROKEN ++#endif ++ ++#if __has_attribute(annotate) ++#define QEMU_ANNOTATE(x) __attribute__((annotate(x))) ++#else ++#define QEMU_ANNOTATE(x) ++#endif ++ ++#if __has_attribute(used) ++# define QEMU_USED __attribute__((used)) ++#else ++# define QEMU_USED ++#endif ++ ++/* ++ * Ugly CPP trick that is like "defined FOO", but also works in C ++ * code. Useful to replace #ifdef with "if" statements; assumes ++ * the symbol was defined with Meson's "config.set()", so it is empty ++ * if defined. ++ */ ++#define IS_ENABLED(x) IS_EMPTY(x) ++ ++#define IS_EMPTY_JUNK_ junk, ++#define IS_EMPTY(value) IS_EMPTY_(IS_EMPTY_JUNK_##value) ++ ++/* Expands to either SECOND_ARG(junk, 1, 0) or SECOND_ARG(IS_EMPTY_JUNK_CONFIG_FOO 1, 0) */ ++#define SECOND_ARG(first, second, ...) second ++#define IS_EMPTY_(junk_maybecomma) SECOND_ARG(junk_maybecomma 1, 0) ++ ++#ifndef __cplusplus ++/* ++ * Useful in macros that need to declare temporary variables. For example, ++ * the variable that receives the old value of an atomically-accessed ++ * variable must be non-qualified, because atomic builtins return values ++ * through a pointer-type argument as in __atomic_load(&var, &old, MODEL). ++ * ++ * This macro has to handle types smaller than int manually, because of ++ * implicit promotion. int and larger types, as well as pointers, can be ++ * converted to a non-qualified type just by applying a binary operator. ++ */ ++#define typeof_strip_qual(expr) \ ++ typeof( \ ++ __builtin_choose_expr( \ ++ __builtin_types_compatible_p(typeof(expr), bool) || \ ++ __builtin_types_compatible_p(typeof(expr), const bool) || \ ++ __builtin_types_compatible_p(typeof(expr), volatile bool) || \ ++ __builtin_types_compatible_p(typeof(expr), const volatile bool), \ ++ (bool)1, \ ++ __builtin_choose_expr( \ ++ __builtin_types_compatible_p(typeof(expr), signed char) || \ ++ __builtin_types_compatible_p(typeof(expr), const signed char) || \ ++ __builtin_types_compatible_p(typeof(expr), volatile signed char) || \ ++ __builtin_types_compatible_p(typeof(expr), const volatile signed char), \ ++ (signed char)1, \ ++ __builtin_choose_expr( \ ++ __builtin_types_compatible_p(typeof(expr), unsigned char) || \ ++ __builtin_types_compatible_p(typeof(expr), const unsigned char) || \ ++ __builtin_types_compatible_p(typeof(expr), volatile unsigned char) || \ ++ __builtin_types_compatible_p(typeof(expr), const volatile unsigned char), \ ++ (unsigned char)1, \ ++ __builtin_choose_expr( \ ++ __builtin_types_compatible_p(typeof(expr), signed short) || \ ++ __builtin_types_compatible_p(typeof(expr), const signed short) || \ ++ __builtin_types_compatible_p(typeof(expr), volatile signed short) || \ ++ __builtin_types_compatible_p(typeof(expr), const volatile signed short), \ ++ (signed short)1, \ ++ __builtin_choose_expr( \ ++ __builtin_types_compatible_p(typeof(expr), unsigned short) || \ ++ __builtin_types_compatible_p(typeof(expr), const unsigned short) || \ ++ __builtin_types_compatible_p(typeof(expr), volatile unsigned short) || \ ++ __builtin_types_compatible_p(typeof(expr), const volatile unsigned short), \ ++ (unsigned short)1, \ ++ (expr)+0)))))) ++#endif ++ ++#endif /* COMPILER_H */ +diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h +new file mode 100644 +index 00000000..51b310fa +--- /dev/null ++++ b/include/qemu/config-file.h +@@ -0,0 +1,31 @@ ++#ifndef QEMU_CONFIG_FILE_H ++#define QEMU_CONFIG_FILE_H ++ ++typedef void QEMUConfigCB(const char *group, QDict *qdict, void *opaque, Error **errp); ++ ++void qemu_load_module_for_opts(const char *group); ++QemuOptsList *qemu_find_opts(const char *group); ++QemuOptsList *qemu_find_opts_err(const char *group, Error **errp); ++QemuOpts *qemu_find_opts_singleton(const char *group); ++ ++extern QemuOptsList *vm_config_groups[]; ++extern QemuOptsList *drive_config_groups[]; ++ ++void qemu_add_opts(QemuOptsList *list); ++void qemu_add_drive_opts(QemuOptsList *list); ++int qemu_global_option(const char *str); ++ ++int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, ++ Error **errp); ++ ++/* A default callback for qemu_read_config_file(). */ ++void qemu_config_do_parse(const char *group, QDict *qdict, void *opaque, Error **errp); ++ ++int qemu_read_config_file(const char *filename, QEMUConfigCB *f, Error **errp); ++ ++/* Parse QDict options as a replacement for a config file (allowing multiple ++ enumerated (0..(n-1)) configuration "sections") */ ++bool qemu_config_parse_qdict(QDict *options, QemuOptsList **lists, ++ Error **errp); ++ ++#endif /* QEMU_CONFIG_FILE_H */ +diff --git a/include/qemu/coroutine-core.h b/include/qemu/coroutine-core.h +new file mode 100644 +index 00000000..503bad6e +--- /dev/null ++++ b/include/qemu/coroutine-core.h +@@ -0,0 +1,154 @@ ++/* ++ * QEMU coroutine implementation ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Stefan Hajnoczi ++ * Kevin Wolf ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_COROUTINE_CORE_H ++#define QEMU_COROUTINE_CORE_H ++ ++/** ++ * Coroutines are a mechanism for stack switching and can be used for ++ * cooperative userspace threading. These functions provide a simple but ++ * useful flavor of coroutines that is suitable for writing sequential code, ++ * rather than callbacks, for operations that need to give up control while ++ * waiting for events to complete. ++ * ++ * These functions are re-entrant and may be used outside the BQL. ++ * ++ * Functions that execute in coroutine context cannot be called ++ * directly from normal functions. Use @coroutine_fn to mark such ++ * functions. For example: ++ * ++ * static void coroutine_fn foo(void) { ++ * .... ++ * } ++ * ++ * In the future it would be nice to have the compiler or a static ++ * checker catch misuse of such functions. This annotation might make ++ * it possible and in the meantime it serves as documentation. ++ */ ++ ++/** ++ * Mark a function that executes in coroutine context ++ * ++ * ++ * Functions that execute in coroutine context cannot be called ++ * directly from normal functions. Use @coroutine_fn to mark such ++ * functions. For example: ++ * ++ * static void coroutine_fn foo(void) { ++ * .... ++ * } ++ * ++ * In the future it would be nice to have the compiler or a static ++ * checker catch misuse of such functions. This annotation might make ++ * it possible and in the meantime it serves as documentation. ++ */ ++ ++typedef struct Coroutine Coroutine; ++typedef struct CoMutex CoMutex; ++ ++/** ++ * Coroutine entry point ++ * ++ * When the coroutine is entered for the first time, opaque is passed in as an ++ * argument. ++ * ++ * When this function returns, the coroutine is destroyed automatically and ++ * execution continues in the caller who last entered the coroutine. ++ */ ++typedef void coroutine_fn CoroutineEntry(void *opaque); ++ ++/** ++ * Create a new coroutine ++ * ++ * Use qemu_coroutine_enter() to actually transfer control to the coroutine. ++ * The opaque argument is passed as the argument to the entry point. ++ */ ++Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque); ++ ++/** ++ * Transfer control to a coroutine ++ */ ++void qemu_coroutine_enter(Coroutine *coroutine); ++ ++/** ++ * Transfer control to a coroutine if it's not active (i.e. part of the call ++ * stack of the running coroutine). Otherwise, do nothing. ++ */ ++void qemu_coroutine_enter_if_inactive(Coroutine *co); ++ ++/** ++ * Transfer control to a coroutine and associate it with ctx ++ */ ++void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co); ++ ++/** ++ * Transfer control back to a coroutine's caller ++ * ++ * This function does not return until the coroutine is re-entered using ++ * qemu_coroutine_enter(). ++ */ ++void coroutine_fn qemu_coroutine_yield(void); ++ ++/** ++ * Get the AioContext of the given coroutine ++ */ ++AioContext *qemu_coroutine_get_aio_context(Coroutine *co); ++ ++/** ++ * Get the currently executing coroutine ++ */ ++Coroutine *qemu_coroutine_self(void); ++ ++/** ++ * Return whether or not currently inside a coroutine ++ * ++ * This can be used to write functions that work both when in coroutine context ++ * and when not in coroutine context. Note that such functions cannot use the ++ * coroutine_fn annotation since they work outside coroutine context. ++ */ ++bool qemu_in_coroutine(void); ++ ++/** ++ * Return true if the coroutine is currently entered ++ * ++ * A coroutine is "entered" if it has not yielded from the current ++ * qemu_coroutine_enter() call used to run it. This does not mean that the ++ * coroutine is currently executing code since it may have transferred control ++ * to another coroutine using qemu_coroutine_enter(). ++ * ++ * When several coroutines enter each other there may be no way to know which ++ * ones have already been entered. In such situations this function can be ++ * used to avoid recursively entering coroutines. ++ */ ++bool qemu_coroutine_entered(Coroutine *co); ++ ++/** ++ * Initialises a CoMutex. This must be called before any other operation is used ++ * on the CoMutex. ++ */ ++void qemu_co_mutex_init(CoMutex *mutex); ++ ++/** ++ * Locks the mutex. If the lock cannot be taken immediately, control is ++ * transferred to the caller of the current coroutine. ++ */ ++void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex); ++ ++/** ++ * Unlocks the mutex and schedules the next coroutine that was waiting for this ++ * lock to be run. ++ */ ++void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex); ++ ++#endif +diff --git a/include/qemu/coroutine-tls.h b/include/qemu/coroutine-tls.h +new file mode 100644 +index 00000000..1558a826 +--- /dev/null ++++ b/include/qemu/coroutine-tls.h +@@ -0,0 +1,165 @@ ++/* ++ * QEMU Thread Local Storage for coroutines ++ * ++ * Copyright Red Hat ++ * ++ * SPDX-License-Identifier: LGPL-2.1-or-later ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ * It is forbidden to access Thread Local Storage in coroutines because ++ * compiler optimizations may cause values to be cached across coroutine ++ * re-entry. Coroutines can run in more than one thread through the course of ++ * their life, leading bugs when stale TLS values from the wrong thread are ++ * used as a result of compiler optimization. ++ * ++ * An example is: ++ * ++ * ..code-block:: c ++ * :caption: A coroutine that may see the wrong TLS value ++ * ++ * static __thread AioContext *current_aio_context; ++ * ... ++ * static void coroutine_fn foo(void) ++ * { ++ * aio_notify(current_aio_context); ++ * qemu_coroutine_yield(); ++ * aio_notify(current_aio_context); // <-- may be stale after yielding! ++ * } ++ * ++ * This header provides macros for safely defining variables in Thread Local ++ * Storage: ++ * ++ * ..code-block:: c ++ * :caption: A coroutine that safely uses TLS ++ * ++ * QEMU_DEFINE_STATIC_CO_TLS(AioContext *, current_aio_context) ++ * ... ++ * static void coroutine_fn foo(void) ++ * { ++ * aio_notify(get_current_aio_context()); ++ * qemu_coroutine_yield(); ++ * aio_notify(get_current_aio_context()); // <-- safe ++ * } ++ */ ++ ++#ifndef QEMU_COROUTINE_TLS_H ++#define QEMU_COROUTINE_TLS_H ++ ++/* ++ * To stop the compiler from caching TLS values we define accessor functions ++ * with __attribute__((noinline)) plus asm volatile("") to prevent ++ * optimizations that override noinline. ++ * ++ * The compiler can still analyze noinline code and make optimizations based on ++ * that knowledge, so an inline asm output operand is used to prevent ++ * optimizations that make assumptions about the address of the TLS variable. ++ * ++ * This is fragile and ultimately needs to be solved by a mechanism that is ++ * guaranteed to work by the compiler (e.g. stackless coroutines), but for now ++ * we use this approach to prevent issues. ++ */ ++ ++/** ++ * QEMU_DECLARE_CO_TLS: ++ * @type: the variable's C type ++ * @var: the variable name ++ * ++ * Declare an extern variable in Thread Local Storage from a header file: ++ * ++ * .. code-block:: c ++ * :caption: Declaring an extern variable in Thread Local Storage ++ * ++ * QEMU_DECLARE_CO_TLS(int, my_count) ++ * ... ++ * int c = get_my_count(); ++ * set_my_count(c + 1); ++ * *get_ptr_my_count() = 0; ++ * ++ * This is a coroutine-safe replacement for the __thread keyword and is ++ * equivalent to the following code: ++ * ++ * .. code-block:: c ++ * :caption: Declaring a TLS variable using __thread ++ * ++ * extern __thread int my_count; ++ * ... ++ * int c = my_count; ++ * my_count = c + 1; ++ * *(&my_count) = 0; ++ */ ++#define QEMU_DECLARE_CO_TLS(type, var) \ ++ __attribute__((noinline)) type get_##var(void); \ ++ __attribute__((noinline)) void set_##var(type v); \ ++ __attribute__((noinline)) type *get_ptr_##var(void); ++ ++/** ++ * QEMU_DEFINE_CO_TLS: ++ * @type: the variable's C type ++ * @var: the variable name ++ * ++ * Define a variable in Thread Local Storage that was previously declared from ++ * a header file with QEMU_DECLARE_CO_TLS(): ++ * ++ * .. code-block:: c ++ * :caption: Defining a variable in Thread Local Storage ++ * ++ * QEMU_DEFINE_CO_TLS(int, my_count) ++ * ++ * This is a coroutine-safe replacement for the __thread keyword and is ++ * equivalent to the following code: ++ * ++ * .. code-block:: c ++ * :caption: Defining a TLS variable using __thread ++ * ++ * __thread int my_count; ++ */ ++#define QEMU_DEFINE_CO_TLS(type, var) \ ++ static __thread type co_tls_##var; \ ++ type get_##var(void) { asm volatile(""); return co_tls_##var; } \ ++ void set_##var(type v) { asm volatile(""); co_tls_##var = v; } \ ++ type *get_ptr_##var(void) \ ++ { type *ptr = &co_tls_##var; asm volatile("" : "+rm" (ptr)); return ptr; } ++ ++/** ++ * QEMU_DEFINE_STATIC_CO_TLS: ++ * @type: the variable's C type ++ * @var: the variable name ++ * ++ * Define a static variable in Thread Local Storage: ++ * ++ * .. code-block:: c ++ * :caption: Defining a static variable in Thread Local Storage ++ * ++ * QEMU_DEFINE_STATIC_CO_TLS(int, my_count) ++ * ... ++ * int c = get_my_count(); ++ * set_my_count(c + 1); ++ * *get_ptr_my_count() = 0; ++ * ++ * This is a coroutine-safe replacement for the __thread keyword and is ++ * equivalent to the following code: ++ * ++ * .. code-block:: c ++ * :caption: Defining a static TLS variable using __thread ++ * ++ * static __thread int my_count; ++ * ... ++ * int c = my_count; ++ * my_count = c + 1; ++ * *(&my_count) = 0; ++ */ ++#define QEMU_DEFINE_STATIC_CO_TLS(type, var) \ ++ static __thread type co_tls_##var; \ ++ static __attribute__((noinline, unused)) \ ++ type get_##var(void) \ ++ { asm volatile(""); return co_tls_##var; } \ ++ static __attribute__((noinline, unused)) \ ++ void set_##var(type v) \ ++ { asm volatile(""); co_tls_##var = v; } \ ++ static __attribute__((noinline, unused)) \ ++ type *get_ptr_##var(void) \ ++ { type *ptr = &co_tls_##var; asm volatile("" : "+rm" (ptr)); return ptr; } ++ ++#endif /* QEMU_COROUTINE_TLS_H */ +diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h +new file mode 100644 +index 00000000..ff308453 +--- /dev/null ++++ b/include/qemu/coroutine.h +@@ -0,0 +1,311 @@ ++/* ++ * QEMU coroutine implementation ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Stefan Hajnoczi ++ * Kevin Wolf ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_COROUTINE_H ++#define QEMU_COROUTINE_H ++ ++#include "qemu/coroutine-core.h" ++#include "qemu/queue.h" ++#include "qemu/timer.h" ++ ++/** ++ * Coroutines are a mechanism for stack switching and can be used for ++ * cooperative userspace threading. These functions provide a simple but ++ * useful flavor of coroutines that is suitable for writing sequential code, ++ * rather than callbacks, for operations that need to give up control while ++ * waiting for events to complete. ++ * ++ * These functions are re-entrant and may be used outside the BQL. ++ * ++ * Functions that execute in coroutine context cannot be called ++ * directly from normal functions. Use @coroutine_fn to mark such ++ * functions. For example: ++ * ++ * static void coroutine_fn foo(void) { ++ * .... ++ * } ++ * ++ * In the future it would be nice to have the compiler or a static ++ * checker catch misuse of such functions. This annotation might make ++ * it possible and in the meantime it serves as documentation. ++ */ ++ ++/** ++ * Provides a mutex that can be used to synchronise coroutines ++ */ ++struct CoWaitRecord; ++struct CoMutex { ++ /* Count of pending lockers; 0 for a free mutex, 1 for an ++ * uncontended mutex. ++ */ ++ unsigned locked; ++ ++ /* Context that is holding the lock. Useful to avoid spinning ++ * when two coroutines on the same AioContext try to get the lock. :) ++ */ ++ AioContext *ctx; ++ ++ /* A queue of waiters. Elements are added atomically in front of ++ * from_push. to_pop is only populated, and popped from, by whoever ++ * is in charge of the next wakeup. This can be an unlocker or, ++ * through the handoff protocol, a locker that is about to go to sleep. ++ */ ++ QSLIST_HEAD(, CoWaitRecord) from_push, to_pop; ++ ++ unsigned handoff, sequence; ++ ++ Coroutine *holder; ++}; ++ ++/** ++ * Assert that the current coroutine holds @mutex. ++ */ ++static inline coroutine_fn void qemu_co_mutex_assert_locked(CoMutex *mutex) ++{ ++ /* ++ * mutex->holder doesn't need any synchronisation if the assertion holds ++ * true because the mutex protects it. If it doesn't hold true, we still ++ * don't mind if another thread takes or releases mutex behind our back, ++ * because the condition will be false no matter whether we read NULL or ++ * the pointer for any other coroutine. ++ */ ++ assert(qatomic_read(&mutex->locked) && ++ mutex->holder == qemu_coroutine_self()); ++} ++ ++#include "qemu/lockable.h" ++ ++/** ++ * CoQueues are a mechanism to queue coroutines in order to continue executing ++ * them later. They are similar to condition variables, but they need help ++ * from an external mutex in order to maintain thread-safety. ++ */ ++typedef struct CoQueue { ++ QSIMPLEQ_HEAD(, Coroutine) entries; ++} CoQueue; ++ ++/** ++ * Initialise a CoQueue. This must be called before any other operation is used ++ * on the CoQueue. ++ */ ++void qemu_co_queue_init(CoQueue *queue); ++ ++typedef enum { ++ /* ++ * Enqueue at front instead of back. Use this to re-queue a request when ++ * its wait condition is not satisfied after being woken up. ++ */ ++ CO_QUEUE_WAIT_FRONT = 0x1, ++} CoQueueWaitFlags; ++ ++/** ++ * Adds the current coroutine to the CoQueue and transfers control to the ++ * caller of the coroutine. The mutex is unlocked during the wait and ++ * locked again afterwards. ++ */ ++#define qemu_co_queue_wait(queue, lock) \ ++ qemu_co_queue_wait_impl(queue, QEMU_MAKE_LOCKABLE(lock), 0) ++#define qemu_co_queue_wait_flags(queue, lock, flags) \ ++ qemu_co_queue_wait_impl(queue, QEMU_MAKE_LOCKABLE(lock), (flags)) ++void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock, ++ CoQueueWaitFlags flags); ++ ++/** ++ * Removes the next coroutine from the CoQueue, and queue it to run after ++ * the currently-running coroutine yields. ++ * Returns true if a coroutine was removed, false if the queue is empty. ++ * Used from coroutine context, use qemu_co_enter_next outside. ++ */ ++bool coroutine_fn qemu_co_queue_next(CoQueue *queue); ++ ++/** ++ * Empties the CoQueue and queues the coroutine to run after ++ * the currently-running coroutine yields. ++ * Used from coroutine context, use qemu_co_enter_all outside. ++ */ ++void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue); ++ ++/** ++ * Removes the next coroutine from the CoQueue, and wake it up. Unlike ++ * qemu_co_queue_next, this function releases the lock during aio_co_wake ++ * because it is meant to be used outside coroutine context; in that case, the ++ * coroutine is entered immediately, before qemu_co_enter_next returns. ++ * ++ * If used in coroutine context, qemu_co_enter_next is equivalent to ++ * qemu_co_queue_next. ++ */ ++#define qemu_co_enter_next(queue, lock) \ ++ qemu_co_enter_next_impl(queue, QEMU_MAKE_LOCKABLE(lock)) ++bool qemu_co_enter_next_impl(CoQueue *queue, QemuLockable *lock); ++ ++/** ++ * Empties the CoQueue, waking the waiting coroutine one at a time. Unlike ++ * qemu_co_queue_all, this function releases the lock during aio_co_wake ++ * because it is meant to be used outside coroutine context; in that case, the ++ * coroutine is entered immediately, before qemu_co_enter_all returns. ++ * ++ * If used in coroutine context, qemu_co_enter_all is equivalent to ++ * qemu_co_queue_all. ++ */ ++#define qemu_co_enter_all(queue, lock) \ ++ qemu_co_enter_all_impl(queue, QEMU_MAKE_LOCKABLE(lock)) ++void qemu_co_enter_all_impl(CoQueue *queue, QemuLockable *lock); ++ ++/** ++ * Checks if the CoQueue is empty. ++ */ ++bool qemu_co_queue_empty(CoQueue *queue); ++ ++ ++typedef struct CoRwTicket CoRwTicket; ++typedef struct CoRwlock { ++ CoMutex mutex; ++ ++ /* Number of readers, or -1 if owned for writing. */ ++ int owners; ++ ++ /* Waiting coroutines. */ ++ QSIMPLEQ_HEAD(, CoRwTicket) tickets; ++} CoRwlock; ++ ++/** ++ * Initialises a CoRwlock. This must be called before any other operation ++ * is used on the CoRwlock ++ */ ++void qemu_co_rwlock_init(CoRwlock *lock); ++ ++/** ++ * Read locks the CoRwlock. If the lock cannot be taken immediately because ++ * of a parallel writer, control is transferred to the caller of the current ++ * coroutine. ++ */ ++void coroutine_fn qemu_co_rwlock_rdlock(CoRwlock *lock); ++ ++/** ++ * Write Locks the CoRwlock from a reader. This is a bit more efficient than ++ * @qemu_co_rwlock_unlock followed by a separate @qemu_co_rwlock_wrlock. ++ * Note that if the lock cannot be upgraded immediately, control is transferred ++ * to the caller of the current coroutine; another writer might run while ++ * @qemu_co_rwlock_upgrade blocks. ++ */ ++void coroutine_fn qemu_co_rwlock_upgrade(CoRwlock *lock); ++ ++/** ++ * Downgrades a write-side critical section to a reader. Downgrading with ++ * @qemu_co_rwlock_downgrade never blocks, unlike @qemu_co_rwlock_unlock ++ * followed by @qemu_co_rwlock_rdlock. This makes it more efficient, but ++ * may also sometimes be necessary for correctness. ++ */ ++void coroutine_fn qemu_co_rwlock_downgrade(CoRwlock *lock); ++ ++/** ++ * Write Locks the mutex. If the lock cannot be taken immediately because ++ * of a parallel reader, control is transferred to the caller of the current ++ * coroutine. ++ */ ++void coroutine_fn qemu_co_rwlock_wrlock(CoRwlock *lock); ++ ++/** ++ * Unlocks the read/write lock and schedules the next coroutine that was ++ * waiting for this lock to be run. ++ */ ++void coroutine_fn qemu_co_rwlock_unlock(CoRwlock *lock); ++ ++typedef struct QemuCoSleep { ++ Coroutine *to_wake; ++} QemuCoSleep; ++ ++/** ++ * Yield the coroutine for a given duration. Initializes @w so that, ++ * during this yield, it can be passed to qemu_co_sleep_wake() to ++ * terminate the sleep. ++ */ ++void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w, ++ QEMUClockType type, int64_t ns); ++ ++/** ++ * Yield the coroutine until the next call to qemu_co_sleep_wake. ++ */ ++void coroutine_fn qemu_co_sleep(QemuCoSleep *w); ++ ++static inline void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns) ++{ ++ QemuCoSleep w = { 0 }; ++ qemu_co_sleep_ns_wakeable(&w, type, ns); ++} ++ ++typedef void CleanupFunc(void *opaque); ++/** ++ * Run entry in a coroutine and start timer. Wait for entry to finish or for ++ * timer to elapse, what happen first. If entry finished, return 0, if timer ++ * elapsed earlier, return -ETIMEDOUT. ++ * ++ * Be careful, entry execution is not canceled, user should handle it somehow. ++ * If @clean is provided, it's called after coroutine finish if timeout ++ * happened. ++ */ ++int coroutine_fn qemu_co_timeout(CoroutineEntry *entry, void *opaque, ++ uint64_t timeout_ns, CleanupFunc clean); ++ ++/** ++ * Wake a coroutine if it is sleeping in qemu_co_sleep_ns. The timer will be ++ * deleted. @sleep_state must be the variable whose address was given to ++ * qemu_co_sleep_ns() and should be checked to be non-NULL before calling ++ * qemu_co_sleep_wake(). ++ */ ++void qemu_co_sleep_wake(QemuCoSleep *w); ++ ++/** ++ * Yield until a file descriptor becomes readable ++ * ++ * Note that this function clobbers the handlers for the file descriptor. ++ */ ++void coroutine_fn yield_until_fd_readable(int fd); ++ ++/** ++ * Increase coroutine pool size ++ */ ++void qemu_coroutine_inc_pool_size(unsigned int additional_pool_size); ++ ++/** ++ * Decrease coroutine pool size ++ */ ++void qemu_coroutine_dec_pool_size(unsigned int additional_pool_size); ++ ++/** ++ * Sends a (part of) iovec down a socket, yielding when the socket is full, or ++ * Receives data into a (part of) iovec from a socket, ++ * yielding when there is no data in the socket. ++ * The same interface as qemu_sendv_recvv(), with added yielding. ++ * XXX should mark these as coroutine_fn ++ */ ++ssize_t coroutine_fn qemu_co_sendv_recvv(int sockfd, struct iovec *iov, ++ unsigned iov_cnt, size_t offset, ++ size_t bytes, bool do_send); ++#define qemu_co_recvv(sockfd, iov, iov_cnt, offset, bytes) \ ++ qemu_co_sendv_recvv(sockfd, iov, iov_cnt, offset, bytes, false) ++#define qemu_co_sendv(sockfd, iov, iov_cnt, offset, bytes) \ ++ qemu_co_sendv_recvv(sockfd, iov, iov_cnt, offset, bytes, true) ++ ++/** ++ * The same as above, but with just a single buffer ++ */ ++ssize_t coroutine_fn qemu_co_send_recv(int sockfd, void *buf, size_t bytes, ++ bool do_send); ++#define qemu_co_recv(sockfd, buf, bytes) \ ++ qemu_co_send_recv(sockfd, buf, bytes, false) ++#define qemu_co_send(sockfd, buf, bytes) \ ++ qemu_co_send_recv(sockfd, buf, bytes, true) ++ ++#endif /* QEMU_COROUTINE_H */ +diff --git a/include/qemu/coroutine_int.h b/include/qemu/coroutine_int.h +new file mode 100644 +index 00000000..1da14855 +--- /dev/null ++++ b/include/qemu/coroutine_int.h +@@ -0,0 +1,77 @@ ++/* ++ * Coroutine internals ++ * ++ * Copyright (c) 2011 Kevin Wolf ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_COROUTINE_INT_H ++#define QEMU_COROUTINE_INT_H ++ ++#include "qemu/queue.h" ++#include "qemu/coroutine.h" ++ ++#ifdef CONFIG_SAFESTACK ++/* Pointer to the unsafe stack, defined by the compiler */ ++extern __thread void *__safestack_unsafe_stack_ptr; ++#endif ++ ++#define COROUTINE_STACK_SIZE (1 << 20) ++ ++typedef enum { ++ COROUTINE_YIELD = 1, ++ COROUTINE_TERMINATE = 2, ++ COROUTINE_ENTER = 3, ++} CoroutineAction; ++ ++struct Coroutine { ++ CoroutineEntry *entry; ++ void *entry_arg; ++ Coroutine *caller; ++ ++ /* Only used when the coroutine has terminated. */ ++ QSLIST_ENTRY(Coroutine) pool_next; ++ ++ size_t locks_held; ++ ++ /* Only used when the coroutine has yielded. */ ++ AioContext *ctx; ++ ++ /* Used to catch and abort on illegal co-routine entry. ++ * Will contain the name of the function that had first ++ * scheduled the coroutine. */ ++ const char *scheduled; ++ ++ QSIMPLEQ_ENTRY(Coroutine) co_queue_next; ++ ++ /* Coroutines that should be woken up when we yield or terminate. ++ * Only used when the coroutine is running. ++ */ ++ QSIMPLEQ_HEAD(, Coroutine) co_queue_wakeup; ++ ++ QSLIST_ENTRY(Coroutine) co_scheduled_next; ++}; ++ ++Coroutine *qemu_coroutine_new(void); ++void qemu_coroutine_delete(Coroutine *co); ++CoroutineAction qemu_coroutine_switch(Coroutine *from, Coroutine *to, ++ CoroutineAction action); ++ ++#endif +diff --git a/include/qemu/ctype.h b/include/qemu/ctype.h +new file mode 100644 +index 00000000..3691f098 +--- /dev/null ++++ b/include/qemu/ctype.h +@@ -0,0 +1,27 @@ ++/* ++ * QEMU TCG support ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_CTYPE_H ++#define QEMU_CTYPE_H ++ ++#define qemu_isalnum(c) isalnum((unsigned char)(c)) ++#define qemu_isalpha(c) isalpha((unsigned char)(c)) ++#define qemu_iscntrl(c) iscntrl((unsigned char)(c)) ++#define qemu_isdigit(c) isdigit((unsigned char)(c)) ++#define qemu_isgraph(c) isgraph((unsigned char)(c)) ++#define qemu_islower(c) islower((unsigned char)(c)) ++#define qemu_isprint(c) isprint((unsigned char)(c)) ++#define qemu_ispunct(c) ispunct((unsigned char)(c)) ++#define qemu_isspace(c) isspace((unsigned char)(c)) ++#define qemu_isupper(c) isupper((unsigned char)(c)) ++#define qemu_isxdigit(c) isxdigit((unsigned char)(c)) ++#define qemu_tolower(c) tolower((unsigned char)(c)) ++#define qemu_toupper(c) toupper((unsigned char)(c)) ++#define qemu_isascii(c) isascii((unsigned char)(c)) ++#define qemu_toascii(c) toascii((unsigned char)(c)) ++ ++#endif +diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h +new file mode 100644 +index 00000000..da15547b +--- /dev/null ++++ b/include/qemu/cutils.h +@@ -0,0 +1,308 @@ ++#ifndef QEMU_CUTILS_H ++#define QEMU_CUTILS_H ++ ++/* ++ * si_prefix: ++ * @exp10: exponent of 10, a multiple of 3 between -18 and 18 inclusive. ++ * ++ * Return a SI prefix (n, u, m, K, M, etc.) corresponding ++ * to the given exponent of 10. ++ */ ++const char *si_prefix(unsigned int exp10); ++ ++/* ++ * iec_binary_prefix: ++ * @exp2: exponent of 2, a multiple of 10 between 0 and 60 inclusive. ++ * ++ * Return an IEC binary prefix (Ki, Mi, etc.) corresponding ++ * to the given exponent of 2. ++ */ ++const char *iec_binary_prefix(unsigned int exp2); ++ ++/** ++ * pstrcpy: ++ * @buf: buffer to copy string into ++ * @buf_size: size of @buf in bytes ++ * @str: string to copy ++ * ++ * Copy @str into @buf, including the trailing NUL, but do not ++ * write more than @buf_size bytes. The resulting buffer is ++ * always NUL terminated (even if the source string was too long). ++ * If @buf_size is zero or negative then no bytes are copied. ++ * ++ * This function is similar to strncpy(), but avoids two of that ++ * function's problems: ++ * * if @str fits in the buffer, pstrcpy() does not zero-fill the ++ * remaining space at the end of @buf ++ * * if @str is too long, pstrcpy() will copy the first @buf_size-1 ++ * bytes and then add a NUL ++ */ ++void pstrcpy(char *buf, int buf_size, const char *str); ++/** ++ * strpadcpy: ++ * @buf: buffer to copy string into ++ * @buf_size: size of @buf in bytes ++ * @str: string to copy ++ * @pad: character to pad the remainder of @buf with ++ * ++ * Copy @str into @buf (but *not* its trailing NUL!), and then pad the ++ * rest of the buffer with the @pad character. If @str is too large ++ * for the buffer then it is truncated, so that @buf contains the ++ * first @buf_size characters of @str, with no terminator. ++ */ ++void strpadcpy(char *buf, int buf_size, const char *str, char pad); ++/** ++ * pstrcat: ++ * @buf: buffer containing existing string ++ * @buf_size: size of @buf in bytes ++ * @s: string to concatenate to @buf ++ * ++ * Append a copy of @s to the string already in @buf, but do not ++ * allow the buffer to overflow. If the existing contents of @buf ++ * plus @str would total more than @buf_size bytes, then write ++ * as much of @str as will fit followed by a NUL terminator. ++ * ++ * @buf must already contain a NUL-terminated string, or the ++ * behaviour is undefined. ++ * ++ * Returns: @buf. ++ */ ++char *pstrcat(char *buf, int buf_size, const char *s); ++/** ++ * strstart: ++ * @str: string to test ++ * @val: prefix string to look for ++ * @ptr: NULL, or pointer to be written to indicate start of ++ * the remainder of the string ++ * ++ * Test whether @str starts with the prefix @val. ++ * If it does (including the degenerate case where @str and @val ++ * are equal) then return true. If @ptr is not NULL then a ++ * pointer to the first character following the prefix is written ++ * to it. If @val is not a prefix of @str then return false (and ++ * @ptr is not written to). ++ * ++ * Returns: true if @str starts with prefix @val, false otherwise. ++ */ ++int strstart(const char *str, const char *val, const char **ptr); ++/** ++ * stristart: ++ * @str: string to test ++ * @val: prefix string to look for ++ * @ptr: NULL, or pointer to be written to indicate start of ++ * the remainder of the string ++ * ++ * Test whether @str starts with the case-insensitive prefix @val. ++ * This function behaves identically to strstart(), except that the ++ * comparison is made after calling qemu_toupper() on each pair of ++ * characters. ++ * ++ * Returns: true if @str starts with case-insensitive prefix @val, ++ * false otherwise. ++ */ ++int stristart(const char *str, const char *val, const char **ptr); ++/** ++ * qemu_strnlen: ++ * @s: string ++ * @max_len: maximum number of bytes in @s to scan ++ * ++ * Return the length of the string @s, like strlen(), but do not ++ * examine more than @max_len bytes of the memory pointed to by @s. ++ * If no NUL terminator is found within @max_len bytes, then return ++ * @max_len instead. ++ * ++ * This function has the same behaviour as the POSIX strnlen() ++ * function. ++ * ++ * Returns: length of @s in bytes, or @max_len, whichever is smaller. ++ */ ++int qemu_strnlen(const char *s, int max_len); ++/** ++ * qemu_strsep: ++ * @input: pointer to string to parse ++ * @delim: string containing delimiter characters to search for ++ * ++ * Locate the first occurrence of any character in @delim within ++ * the string referenced by @input, and replace it with a NUL. ++ * The location of the next character after the delimiter character ++ * is stored into @input. ++ * If the end of the string was reached without finding a delimiter ++ * character, then NULL is stored into @input. ++ * If @input points to a NULL pointer on entry, return NULL. ++ * The return value is always the original value of *@input (and ++ * so now points to a NUL-terminated string corresponding to the ++ * part of the input up to the first delimiter). ++ * ++ * This function has the same behaviour as the BSD strsep() function. ++ * ++ * Returns: the pointer originally in @input. ++ */ ++char *qemu_strsep(char **input, const char *delim); ++#ifdef HAVE_STRCHRNUL ++static inline const char *qemu_strchrnul(const char *s, int c) ++{ ++ return strchrnul(s, c); ++} ++#else ++const char *qemu_strchrnul(const char *s, int c); ++#endif ++time_t mktimegm(struct tm *tm); ++int qemu_parse_fd(const char *param); ++int qemu_strtoi(const char *nptr, const char **endptr, int base, ++ int *result); ++int qemu_strtoui(const char *nptr, const char **endptr, int base, ++ unsigned int *result); ++int qemu_strtol(const char *nptr, const char **endptr, int base, ++ long *result); ++int qemu_strtoul(const char *nptr, const char **endptr, int base, ++ unsigned long *result); ++int qemu_strtoi64(const char *nptr, const char **endptr, int base, ++ int64_t *result); ++int qemu_strtou64(const char *nptr, const char **endptr, int base, ++ uint64_t *result); ++int qemu_strtod(const char *nptr, const char **endptr, double *result); ++int qemu_strtod_finite(const char *nptr, const char **endptr, double *result); ++ ++int parse_uint(const char *s, const char **endptr, int base, uint64_t *value); ++int parse_uint_full(const char *s, int base, uint64_t *value); ++ ++int qemu_strtosz(const char *nptr, const char **end, uint64_t *result); ++int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result); ++int qemu_strtosz_metric(const char *nptr, const char **end, uint64_t *result); ++ ++char *size_to_str(uint64_t val); ++ ++/** ++ * freq_to_str: ++ * @freq_hz: frequency to stringify ++ * ++ * Return human readable string for frequency @freq_hz. ++ * Use SI units like KHz, MHz, and so forth. ++ * ++ * The caller is responsible for releasing the value returned ++ * with g_free() after use. ++ */ ++char *freq_to_str(uint64_t freq_hz); ++ ++/* used to print char* safely */ ++#define STR_OR_NULL(str) ((str) ? (str) : "null") ++ ++/* ++ * Check if a buffer is all zeroes. ++ */ ++ ++bool buffer_is_zero_ool(const void *vbuf, size_t len); ++bool buffer_is_zero_ge256(const void *vbuf, size_t len); ++bool test_buffer_is_zero_next_accel(void); ++ ++static inline bool buffer_is_zero_sample3(const char *buf, size_t len) ++{ ++ /* ++ * For any reasonably sized buffer, these three samples come from ++ * three different cachelines. In qemu-img usage, we find that ++ * each byte eliminates more than half of all buffer testing. ++ * It is therefore critical to performance that the byte tests ++ * short-circuit, so that we do not pull in additional cache lines. ++ * Do not "optimize" this to !(a | b | c). ++ */ ++ return !buf[0] && !buf[len - 1] && !buf[len / 2]; ++} ++ ++#ifdef __OPTIMIZE__ ++static inline bool buffer_is_zero(const void *buf, size_t len) ++{ ++ return (__builtin_constant_p(len) && len >= 256 ++ ? buffer_is_zero_sample3(buf, len) && ++ buffer_is_zero_ge256(buf, len) ++ : buffer_is_zero_ool(buf, len)); ++} ++#else ++#define buffer_is_zero buffer_is_zero_ool ++#endif ++ ++/* ++ * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128) ++ * Input is limited to 14-bit numbers ++ */ ++ ++int uleb128_encode_small(uint8_t *out, uint32_t n); ++int uleb128_decode_small(const uint8_t *in, uint32_t *n); ++ ++/** ++ * qemu_pstrcmp0: ++ * @str1: a non-NULL pointer to a C string (*str1 can be NULL) ++ * @str2: a non-NULL pointer to a C string (*str2 can be NULL) ++ * ++ * Compares *str1 and *str2 with g_strcmp0(). ++ * ++ * Returns: an integer less than, equal to, or greater than zero, if ++ * *str1 is <, == or > than *str2. ++ */ ++int qemu_pstrcmp0(const char **str1, const char **str2); ++ ++/* Find program directory, and save it for later usage with ++ * qemu_get_exec_dir(). ++ * Try OS specific API first, if not working, parse from argv0. */ ++void qemu_init_exec_dir(const char *argv0); ++ ++/* Get the saved exec dir. */ ++const char *qemu_get_exec_dir(void); ++ ++/** ++ * get_relocated_path: ++ * @dir: the directory (typically a `CONFIG_*DIR` variable) to be relocated. ++ * ++ * Returns a path for @dir that uses the directory of the running executable ++ * as the prefix. ++ * ++ * When a directory named `qemu-bundle` exists in the directory of the running ++ * executable, the path to the directory will be prepended to @dir. For ++ * example, if the directory of the running executable is `/qemu/build` @dir ++ * is `/usr/share/qemu`, the result will be ++ * `/qemu/build/qemu-bundle/usr/share/qemu`. The directory is expected to exist ++ * in the build tree. ++ * ++ * Otherwise, the directory of the running executable will be used as the ++ * prefix and it appends the relative path from `bindir` to @dir. For example, ++ * if the directory of the running executable is `/opt/qemu/bin`, `bindir` is ++ * `/usr/bin` and @dir is `/usr/share/qemu`, the result will be ++ * `/opt/qemu/bin/../share/qemu`. ++ * ++ * The returned string should be freed by the caller. ++ */ ++char *get_relocated_path(const char *dir); ++ ++static inline const char *yes_no(bool b) ++{ ++ return b ? "yes" : "no"; ++} ++ ++/* ++ * helper to parse debug environment variables ++ */ ++int parse_debug_env(const char *name, int max, int initial); ++ ++/** ++ * qemu_hexdump_line: ++ * @str: GString into which to append ++ * @buf: buffer to dump ++ * @len: number of bytes to dump ++ * @unit_len: add a space between every @unit_len bytes ++ * @block_len: add an extra space between every @block_len bytes ++ * ++ * Append @len bytes of @buf as hexadecimal into @str. ++ * Add spaces between every @unit_len and @block_len bytes. ++ * If @str is NULL, allocate a new string and return it; ++ * otherwise return @str. ++ */ ++GString *qemu_hexdump_line(GString *str, const void *buf, size_t len, ++ size_t unit_len, size_t block_len); ++ ++/* ++ * Hexdump a buffer to a file. An optional string prefix is added to every line ++ */ ++ ++void qemu_hexdump(FILE *fp, const char *prefix, ++ const void *bufptr, size_t size); ++ ++#endif +diff --git a/include/qemu/defer-call.h b/include/qemu/defer-call.h +new file mode 100644 +index 00000000..e2c1d245 +--- /dev/null ++++ b/include/qemu/defer-call.h +@@ -0,0 +1,16 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Deferred calls ++ * ++ * Copyright Red Hat. ++ */ ++ ++#ifndef QEMU_DEFER_CALL_H ++#define QEMU_DEFER_CALL_H ++ ++/* See documentation in util/defer-call.c */ ++void defer_call_begin(void); ++void defer_call_end(void); ++void defer_call(void (*fn)(void *), void *opaque); ++ ++#endif /* QEMU_DEFER_CALL_H */ +diff --git a/include/qemu/error-report.h b/include/qemu/error-report.h +new file mode 100644 +index 00000000..3ae2357f +--- /dev/null ++++ b/include/qemu/error-report.h +@@ -0,0 +1,77 @@ ++/* ++ * Error reporting ++ * ++ * Copyright (C) 2010 Red Hat Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_ERROR_REPORT_H ++#define QEMU_ERROR_REPORT_H ++ ++typedef struct Location { ++ /* all members are private to qemu-error.c */ ++ enum { LOC_NONE, LOC_CMDLINE, LOC_FILE } kind; ++ int num; ++ const void *ptr; ++ struct Location *prev; ++} Location; ++ ++Location *loc_push_restore(Location *loc); ++Location *loc_push_none(Location *loc); ++Location *loc_pop(Location *loc); ++Location *loc_save(Location *loc); ++void loc_restore(Location *loc); ++void loc_set_none(void); ++void loc_set_cmdline(char **argv, int idx, int cnt); ++void loc_set_file(const char *fname, int lno); ++ ++int error_vprintf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); ++int error_printf(const char *fmt, ...) G_GNUC_PRINTF(1, 2); ++ ++void error_vreport(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); ++void warn_vreport(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); ++void info_vreport(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); ++ ++void error_report(const char *fmt, ...) G_GNUC_PRINTF(1, 2); ++void warn_report(const char *fmt, ...) G_GNUC_PRINTF(1, 2); ++void info_report(const char *fmt, ...) G_GNUC_PRINTF(1, 2); ++ ++bool error_report_once_cond(bool *printed, const char *fmt, ...) ++ G_GNUC_PRINTF(2, 3); ++bool warn_report_once_cond(bool *printed, const char *fmt, ...) ++ G_GNUC_PRINTF(2, 3); ++ ++void error_init(const char *argv0); ++ ++/* ++ * Similar to error_report(), except it prints the message just once. ++ * Return true when it prints, false otherwise. ++ */ ++#define error_report_once(fmt, ...) \ ++ ({ \ ++ static bool print_once_; \ ++ error_report_once_cond(&print_once_, \ ++ fmt, ##__VA_ARGS__); \ ++ }) ++ ++/* ++ * Similar to warn_report(), except it prints the message just once. ++ * Return true when it prints, false otherwise. ++ */ ++#define warn_report_once(fmt, ...) \ ++ ({ \ ++ static bool print_once_; \ ++ warn_report_once_cond(&print_once_, \ ++ fmt, ##__VA_ARGS__); \ ++ }) ++ ++extern bool message_with_timestamp; ++extern bool error_with_guestname; ++extern const char *error_guest_name; ++ ++#endif +diff --git a/include/qemu/event_notifier.h b/include/qemu/event_notifier.h +new file mode 100644 +index 00000000..8a4ff308 +--- /dev/null ++++ b/include/qemu/event_notifier.h +@@ -0,0 +1,46 @@ ++/* ++ * event notifier support ++ * ++ * Copyright Red Hat, Inc. 2010 ++ * ++ * Authors: ++ * Michael S. Tsirkin ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_EVENT_NOTIFIER_H ++#define QEMU_EVENT_NOTIFIER_H ++ ++ ++#ifdef _WIN32 ++#include ++#endif ++ ++struct EventNotifier { ++#ifdef _WIN32 ++ HANDLE event; ++#else ++ int rfd; ++ int wfd; ++ bool initialized; ++#endif ++}; ++ ++typedef void EventNotifierHandler(EventNotifier *); ++ ++int event_notifier_init(EventNotifier *, int active); ++void event_notifier_cleanup(EventNotifier *); ++int event_notifier_set(EventNotifier *); ++int event_notifier_test_and_clear(EventNotifier *); ++ ++#ifdef CONFIG_POSIX ++void event_notifier_init_fd(EventNotifier *, int fd); ++int event_notifier_get_fd(const EventNotifier *); ++int event_notifier_get_wfd(const EventNotifier *); ++#else ++HANDLE event_notifier_get_handle(EventNotifier *); ++#endif ++ ++#endif +diff --git a/include/qemu/futex.h b/include/qemu/futex.h +new file mode 100644 +index 00000000..91ae8896 +--- /dev/null ++++ b/include/qemu/futex.h +@@ -0,0 +1,41 @@ ++/* ++ * Wrappers around Linux futex syscall ++ * ++ * Copyright Red Hat, Inc. 2017 ++ * ++ * Author: ++ * Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_FUTEX_H ++#define QEMU_FUTEX_H ++ ++#include ++#include ++ ++#define qemu_futex(...) syscall(__NR_futex, __VA_ARGS__) ++ ++static inline void qemu_futex_wake(void *f, int n) ++{ ++ qemu_futex(f, FUTEX_WAKE, n, NULL, NULL, 0); ++} ++ ++static inline void qemu_futex_wait(void *f, unsigned val) ++{ ++ while (qemu_futex(f, FUTEX_WAIT, (int) val, NULL, NULL, 0)) { ++ switch (errno) { ++ case EWOULDBLOCK: ++ return; ++ case EINTR: ++ break; /* get out of switch and retry */ ++ default: ++ abort(); ++ } ++ } ++} ++ ++#endif /* QEMU_FUTEX_H */ +diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h +new file mode 100644 +index 00000000..8136e336 +--- /dev/null ++++ b/include/qemu/hbitmap.h +@@ -0,0 +1,353 @@ ++/* ++ * Hierarchical Bitmap Data Type ++ * ++ * Copyright Red Hat, Inc., 2012 ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef HBITMAP_H ++#define HBITMAP_H ++ ++#include "bitops.h" ++#include "host-utils.h" ++ ++typedef struct HBitmap HBitmap; ++typedef struct HBitmapIter HBitmapIter; ++ ++#define BITS_PER_LEVEL (BITS_PER_LONG == 32 ? 5 : 6) ++ ++/* For 32-bit, the largest that fits in a 4 GiB address space. ++ * For 64-bit, the number of sectors in 1 PiB. Good luck, in ++ * either case... :) ++ */ ++#define HBITMAP_LOG_MAX_SIZE (BITS_PER_LONG == 32 ? 34 : 41) ++ ++/* We need to place a sentinel in level 0 to speed up iteration. Thus, ++ * we do this instead of HBITMAP_LOG_MAX_SIZE / BITS_PER_LEVEL. The ++ * difference is that it allocates an extra level when HBITMAP_LOG_MAX_SIZE ++ * is an exact multiple of BITS_PER_LEVEL. ++ */ ++#define HBITMAP_LEVELS ((HBITMAP_LOG_MAX_SIZE / BITS_PER_LEVEL) + 1) ++ ++struct HBitmapIter { ++ const HBitmap *hb; ++ ++ /* Copied from hb for access in the inline functions (hb is opaque). */ ++ int granularity; ++ ++ /* Entry offset into the last-level array of longs. */ ++ size_t pos; ++ ++ /* The currently-active path in the tree. Each item of cur[i] stores ++ * the bits (i.e. the subtrees) yet to be processed under that node. ++ */ ++ unsigned long cur[HBITMAP_LEVELS]; ++}; ++ ++/** ++ * hbitmap_alloc: ++ * @size: Number of bits in the bitmap. ++ * @granularity: Granularity of the bitmap. Aligned groups of 2^@granularity ++ * bits will be represented by a single bit. Each operation on a ++ * range of bits first rounds the bits to determine which group they land ++ * in, and then affect the entire set; iteration will only visit the first ++ * bit of each group. ++ * ++ * Allocate a new HBitmap. ++ */ ++HBitmap *hbitmap_alloc(uint64_t size, int granularity); ++ ++/** ++ * hbitmap_truncate: ++ * @hb: The bitmap to change the size of. ++ * @size: The number of elements to change the bitmap to accommodate. ++ * ++ * truncate or grow an existing bitmap to accommodate a new number of elements. ++ * This may invalidate existing HBitmapIterators. ++ */ ++void hbitmap_truncate(HBitmap *hb, uint64_t size); ++ ++/** ++ * hbitmap_merge: ++ * ++ * Store result of merging @a and @b into @result. ++ * @result is allowed to be equal to @a or @b. ++ * All bitmaps must have same size. ++ */ ++void hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result); ++ ++/** ++ * hbitmap_empty: ++ * @hb: HBitmap to operate on. ++ * ++ * Return whether the bitmap is empty. ++ */ ++bool hbitmap_empty(const HBitmap *hb); ++ ++/** ++ * hbitmap_granularity: ++ * @hb: HBitmap to operate on. ++ * ++ * Return the granularity of the HBitmap. ++ */ ++int hbitmap_granularity(const HBitmap *hb); ++ ++/** ++ * hbitmap_count: ++ * @hb: HBitmap to operate on. ++ * ++ * Return the number of bits set in the HBitmap. ++ */ ++uint64_t hbitmap_count(const HBitmap *hb); ++ ++/** ++ * hbitmap_set: ++ * @hb: HBitmap to operate on. ++ * @start: First bit to set (0-based). ++ * @count: Number of bits to set. ++ * ++ * Set a consecutive range of bits in an HBitmap. ++ */ ++void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count); ++ ++/** ++ * hbitmap_reset: ++ * @hb: HBitmap to operate on. ++ * @start: First bit to reset (0-based). ++ * @count: Number of bits to reset. ++ * ++ * Reset a consecutive range of bits in an HBitmap. ++ * @start and @count must be aligned to bitmap granularity. The only exception ++ * is resetting the tail of the bitmap: @count may be equal to hb->orig_size - ++ * @start, in this case @count may be not aligned. The sum of @start + @count is ++ * allowed to be greater than hb->orig_size, but only if @start < hb->orig_size ++ * and @start + @count = ALIGN_UP(hb->orig_size, granularity). ++ */ ++void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count); ++ ++/** ++ * hbitmap_reset_all: ++ * @hb: HBitmap to operate on. ++ * ++ * Reset all bits in an HBitmap. ++ */ ++void hbitmap_reset_all(HBitmap *hb); ++ ++/** ++ * hbitmap_get: ++ * @hb: HBitmap to operate on. ++ * @item: Bit to query (0-based). ++ * ++ * Return whether the @item-th bit in an HBitmap is set. ++ */ ++bool hbitmap_get(const HBitmap *hb, uint64_t item); ++ ++/** ++ * hbitmap_is_serializable: ++ * @hb: HBitmap which should be (de-)serialized. ++ * ++ * Returns whether the bitmap can actually be (de-)serialized. Other ++ * (de-)serialization functions may only be invoked if this function returns ++ * true. ++ * ++ * Calling (de-)serialization functions does not affect a bitmap's ++ * (de-)serializability. ++ */ ++bool hbitmap_is_serializable(const HBitmap *hb); ++ ++/** ++ * hbitmap_serialization_align: ++ * @hb: HBitmap to operate on. ++ * ++ * Required alignment of serialization chunks, used by other serialization ++ * functions. For every chunk: ++ * 1. Chunk start should be aligned to this granularity. ++ * 2. Chunk size should be aligned too, except for last chunk (for which ++ * start + count == hb->size) ++ */ ++uint64_t hbitmap_serialization_align(const HBitmap *hb); ++ ++/** ++ * hbitmap_serialization_size: ++ * @hb: HBitmap to operate on. ++ * @start: Starting bit ++ * @count: Number of bits ++ * ++ * Return number of bytes hbitmap_(de)serialize_part needs ++ */ ++uint64_t hbitmap_serialization_size(const HBitmap *hb, ++ uint64_t start, uint64_t count); ++ ++/** ++ * hbitmap_serialize_part ++ * @hb: HBitmap to operate on. ++ * @buf: Buffer to store serialized bitmap. ++ * @start: First bit to store. ++ * @count: Number of bits to store. ++ * ++ * Stores HBitmap data corresponding to given region. The format of saved data ++ * is linear sequence of bits, so it can be used by hbitmap_deserialize_part ++ * independently of endianness and size of HBitmap level array elements ++ */ ++void hbitmap_serialize_part(const HBitmap *hb, uint8_t *buf, ++ uint64_t start, uint64_t count); ++ ++/** ++ * hbitmap_deserialize_part ++ * @hb: HBitmap to operate on. ++ * @buf: Buffer to restore bitmap data from. ++ * @start: First bit to restore. ++ * @count: Number of bits to restore. ++ * @finish: Whether to call hbitmap_deserialize_finish automatically. ++ * ++ * Restores HBitmap data corresponding to given region. The format is the same ++ * as for hbitmap_serialize_part. ++ * ++ * If @finish is false, caller must call hbitmap_serialize_finish before using ++ * the bitmap. ++ */ ++void hbitmap_deserialize_part(HBitmap *hb, uint8_t *buf, ++ uint64_t start, uint64_t count, ++ bool finish); ++ ++/** ++ * hbitmap_deserialize_zeroes ++ * @hb: HBitmap to operate on. ++ * @start: First bit to restore. ++ * @count: Number of bits to restore. ++ * @finish: Whether to call hbitmap_deserialize_finish automatically. ++ * ++ * Fills the bitmap with zeroes. ++ * ++ * If @finish is false, caller must call hbitmap_serialize_finish before using ++ * the bitmap. ++ */ ++void hbitmap_deserialize_zeroes(HBitmap *hb, uint64_t start, uint64_t count, ++ bool finish); ++ ++/** ++ * hbitmap_deserialize_ones ++ * @hb: HBitmap to operate on. ++ * @start: First bit to restore. ++ * @count: Number of bits to restore. ++ * @finish: Whether to call hbitmap_deserialize_finish automatically. ++ * ++ * Fills the bitmap with ones. ++ * ++ * If @finish is false, caller must call hbitmap_serialize_finish before using ++ * the bitmap. ++ */ ++void hbitmap_deserialize_ones(HBitmap *hb, uint64_t start, uint64_t count, ++ bool finish); ++ ++/** ++ * hbitmap_deserialize_finish ++ * @hb: HBitmap to operate on. ++ * ++ * Repair HBitmap after calling hbitmap_deserialize_data. Actually, all HBitmap ++ * layers are restored here. ++ */ ++void hbitmap_deserialize_finish(HBitmap *hb); ++ ++/** ++ * hbitmap_sha256: ++ * @bitmap: HBitmap to operate on. ++ * ++ * Returns SHA256 hash of the last level. ++ */ ++char *hbitmap_sha256(const HBitmap *bitmap, Error **errp); ++ ++/** ++ * hbitmap_free: ++ * @hb: HBitmap to operate on. ++ * ++ * Free an HBitmap and all of its associated memory. ++ */ ++void hbitmap_free(HBitmap *hb); ++ ++/** ++ * hbitmap_iter_init: ++ * @hbi: HBitmapIter to initialize. ++ * @hb: HBitmap to iterate on. ++ * @first: First bit to visit (0-based, must be strictly less than the ++ * size of the bitmap). ++ * ++ * Set up @hbi to iterate on the HBitmap @hb. hbitmap_iter_next will return ++ * the lowest-numbered bit that is set in @hb, starting at @first. ++ * ++ * Concurrent setting of bits is acceptable, and will at worst cause the ++ * iteration to miss some of those bits. ++ * ++ * The concurrent resetting of bits is OK. ++ */ ++void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first); ++ ++/* ++ * hbitmap_next_dirty: ++ * ++ * Find next dirty bit within selected range. If not found, return -1. ++ * ++ * @hb: The HBitmap to operate on ++ * @start: The bit to start from. ++ * @count: Number of bits to proceed. If @start+@count > bitmap size, the whole ++ * bitmap is looked through. You can use INT64_MAX as @count to search up to ++ * the bitmap end. ++ */ ++int64_t hbitmap_next_dirty(const HBitmap *hb, int64_t start, int64_t count); ++ ++/* hbitmap_next_zero: ++ * ++ * Find next not dirty bit within selected range. If not found, return -1. ++ * ++ * @hb: The HBitmap to operate on ++ * @start: The bit to start from. ++ * @count: Number of bits to proceed. If @start+@count > bitmap size, the whole ++ * bitmap is looked through. You can use INT64_MAX as @count to search up to ++ * the bitmap end. ++ */ ++int64_t hbitmap_next_zero(const HBitmap *hb, int64_t start, int64_t count); ++ ++/* hbitmap_next_dirty_area: ++ * @hb: The HBitmap to operate on ++ * @start: the offset to start from ++ * @end: end of requested area ++ * @max_dirty_count: limit for out parameter dirty_count ++ * @dirty_start: on success: start of found area ++ * @dirty_count: on success: length of found area ++ * ++ * If dirty area found within [@start, @end), returns true and sets ++ * @dirty_start and @dirty_count appropriately. @dirty_count will not exceed ++ * @max_dirty_count. ++ * If dirty area was not found, returns false and leaves @dirty_start and ++ * @dirty_count unchanged. ++ */ ++bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end, ++ int64_t max_dirty_count, ++ int64_t *dirty_start, int64_t *dirty_count); ++ ++/* ++ * hbitmap_status: ++ * @hb: The HBitmap to operate on ++ * @start: The bit to start from ++ * @count: Number of bits to proceed ++ * @pnum: Out-parameter. How many bits has same value starting from @start ++ * ++ * Returns true if bitmap is dirty at @start, false otherwise. ++ */ ++bool hbitmap_status(const HBitmap *hb, int64_t start, int64_t count, ++ int64_t *pnum); ++ ++/** ++ * hbitmap_iter_next: ++ * @hbi: HBitmapIter to operate on. ++ * ++ * Return the next bit that is set in @hbi's associated HBitmap, ++ * or -1 if all remaining bits are zero. ++ */ ++int64_t hbitmap_iter_next(HBitmapIter *hbi); ++ ++#endif +diff --git a/include/qemu/help_option.h b/include/qemu/help_option.h +new file mode 100644 +index 00000000..ca6389a1 +--- /dev/null ++++ b/include/qemu/help_option.h +@@ -0,0 +1,33 @@ ++#ifndef QEMU_HELP_OPTION_H ++#define QEMU_HELP_OPTION_H ++ ++/** ++ * is_help_option: ++ * @s: string to test ++ * ++ * Check whether @s is one of the standard strings which indicate ++ * that the user is asking for a list of the valid values for a ++ * command option like -cpu or -M. The current accepted strings ++ * are 'help' and '?'. '?' is deprecated (it is a shell wildcard ++ * which makes it annoying to use in a reliable way) but provided ++ * for backwards compatibility. ++ * ++ * Returns: true if @s is a request for a list. ++ */ ++static inline bool is_help_option(const char *s) ++{ ++ return !strcmp(s, "?") || !strcmp(s, "help"); ++} ++ ++static inline int starts_with_help_option(const char *s) ++{ ++ if (*s == '?') { ++ return 1; ++ } ++ if (g_str_has_prefix(s, "help")) { ++ return 4; ++ } ++ return 0; ++} ++ ++#endif +diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h +new file mode 100644 +index 00000000..ead97d35 +--- /dev/null ++++ b/include/qemu/host-utils.h +@@ -0,0 +1,861 @@ ++/* ++ * Utility compute operations used by translated code. ++ * ++ * Copyright (c) 2007 Thiemo Seufer ++ * Copyright (c) 2007 Jocelyn Mayer ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++/* Portions of this work are licensed under the terms of the GNU GPL, ++ * version 2 or later. See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef HOST_UTILS_H ++#define HOST_UTILS_H ++ ++#include "qemu/bswap.h" ++#include "qemu/int128.h" ++ ++#ifdef CONFIG_INT128 ++static inline void mulu64(uint64_t *plow, uint64_t *phigh, ++ uint64_t a, uint64_t b) ++{ ++ __uint128_t r = (__uint128_t)a * b; ++ *plow = r; ++ *phigh = r >> 64; ++} ++ ++static inline void muls64(uint64_t *plow, uint64_t *phigh, ++ int64_t a, int64_t b) ++{ ++ __int128_t r = (__int128_t)a * b; ++ *plow = r; ++ *phigh = r >> 64; ++} ++ ++/* compute with 96 bit intermediate result: (a*b)/c */ ++static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) ++{ ++ return (__int128_t)a * b / c; ++} ++ ++static inline uint64_t muldiv64_round_up(uint64_t a, uint32_t b, uint32_t c) ++{ ++ return ((__int128_t)a * b + c - 1) / c; ++} ++ ++static inline uint64_t divu128(uint64_t *plow, uint64_t *phigh, ++ uint64_t divisor) ++{ ++ __uint128_t dividend = ((__uint128_t)*phigh << 64) | *plow; ++ __uint128_t result = dividend / divisor; ++ ++ *plow = result; ++ *phigh = result >> 64; ++ return dividend % divisor; ++} ++ ++static inline int64_t divs128(uint64_t *plow, int64_t *phigh, ++ int64_t divisor) ++{ ++ __int128_t dividend = ((__int128_t)*phigh << 64) | *plow; ++ __int128_t result = dividend / divisor; ++ ++ *plow = result; ++ *phigh = result >> 64; ++ return dividend % divisor; ++} ++#else ++void muls64(uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b); ++void mulu64(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b); ++uint64_t divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor); ++int64_t divs128(uint64_t *plow, int64_t *phigh, int64_t divisor); ++ ++static inline uint64_t muldiv64_rounding(uint64_t a, uint32_t b, uint32_t c, ++ bool round_up) ++{ ++ union { ++ uint64_t ll; ++ struct { ++#if HOST_BIG_ENDIAN ++ uint32_t high, low; ++#else ++ uint32_t low, high; ++#endif ++ } l; ++ } u, res; ++ uint64_t rl, rh; ++ ++ u.ll = a; ++ rl = (uint64_t)u.l.low * (uint64_t)b; ++ if (round_up) { ++ rl += c - 1; ++ } ++ rh = (uint64_t)u.l.high * (uint64_t)b; ++ rh += (rl >> 32); ++ res.l.high = rh / c; ++ res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; ++ return res.ll; ++} ++ ++static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) ++{ ++ return muldiv64_rounding(a, b, c, false); ++} ++ ++static inline uint64_t muldiv64_round_up(uint64_t a, uint32_t b, uint32_t c) ++{ ++ return muldiv64_rounding(a, b, c, true); ++} ++#endif ++ ++/** ++ * clz8 - count leading zeros in a 8-bit value. ++ * @val: The value to search ++ * ++ * Returns 8 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ * ++ * Note that the GCC builtin will upcast its argument to an `unsigned int` ++ * so this function subtracts off the number of prepended zeroes. ++ */ ++static inline int clz8(uint8_t val) ++{ ++ return val ? __builtin_clz(val) - 24 : 8; ++} ++ ++/** ++ * clz16 - count leading zeros in a 16-bit value. ++ * @val: The value to search ++ * ++ * Returns 16 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ * ++ * Note that the GCC builtin will upcast its argument to an `unsigned int` ++ * so this function subtracts off the number of prepended zeroes. ++ */ ++static inline int clz16(uint16_t val) ++{ ++ return val ? __builtin_clz(val) - 16 : 16; ++} ++ ++/** ++ * clz32 - count leading zeros in a 32-bit value. ++ * @val: The value to search ++ * ++ * Returns 32 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ */ ++static inline int clz32(uint32_t val) ++{ ++ return val ? __builtin_clz(val) : 32; ++} ++ ++/** ++ * clo32 - count leading ones in a 32-bit value. ++ * @val: The value to search ++ * ++ * Returns 32 if the value is -1. ++ */ ++static inline int clo32(uint32_t val) ++{ ++ return clz32(~val); ++} ++ ++/** ++ * clz64 - count leading zeros in a 64-bit value. ++ * @val: The value to search ++ * ++ * Returns 64 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ */ ++static inline int clz64(uint64_t val) ++{ ++ return val ? __builtin_clzll(val) : 64; ++} ++ ++/** ++ * clo64 - count leading ones in a 64-bit value. ++ * @val: The value to search ++ * ++ * Returns 64 if the value is -1. ++ */ ++static inline int clo64(uint64_t val) ++{ ++ return clz64(~val); ++} ++ ++/** ++ * ctz8 - count trailing zeros in a 8-bit value. ++ * @val: The value to search ++ * ++ * Returns 8 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ */ ++static inline int ctz8(uint8_t val) ++{ ++ return val ? __builtin_ctz(val) : 8; ++} ++ ++/** ++ * ctz16 - count trailing zeros in a 16-bit value. ++ * @val: The value to search ++ * ++ * Returns 16 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ */ ++static inline int ctz16(uint16_t val) ++{ ++ return val ? __builtin_ctz(val) : 16; ++} ++ ++/** ++ * ctz32 - count trailing zeros in a 32-bit value. ++ * @val: The value to search ++ * ++ * Returns 32 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ */ ++static inline int ctz32(uint32_t val) ++{ ++ return val ? __builtin_ctz(val) : 32; ++} ++ ++/** ++ * cto32 - count trailing ones in a 32-bit value. ++ * @val: The value to search ++ * ++ * Returns 32 if the value is -1. ++ */ ++static inline int cto32(uint32_t val) ++{ ++ return ctz32(~val); ++} ++ ++/** ++ * ctz64 - count trailing zeros in a 64-bit value. ++ * @val: The value to search ++ * ++ * Returns 64 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ */ ++static inline int ctz64(uint64_t val) ++{ ++ return val ? __builtin_ctzll(val) : 64; ++} ++ ++/** ++ * cto64 - count trailing ones in a 64-bit value. ++ * @val: The value to search ++ * ++ * Returns 64 if the value is -1. ++ */ ++static inline int cto64(uint64_t val) ++{ ++ return ctz64(~val); ++} ++ ++/** ++ * clrsb32 - count leading redundant sign bits in a 32-bit value. ++ * @val: The value to search ++ * ++ * Returns the number of bits following the sign bit that are equal to it. ++ * No special cases; output range is [0-31]. ++ */ ++static inline int clrsb32(uint32_t val) ++{ ++#if __has_builtin(__builtin_clrsb) || !defined(__clang__) ++ return __builtin_clrsb(val); ++#else ++ return clz32(val ^ ((int32_t)val >> 1)) - 1; ++#endif ++} ++ ++/** ++ * clrsb64 - count leading redundant sign bits in a 64-bit value. ++ * @val: The value to search ++ * ++ * Returns the number of bits following the sign bit that are equal to it. ++ * No special cases; output range is [0-63]. ++ */ ++static inline int clrsb64(uint64_t val) ++{ ++#if __has_builtin(__builtin_clrsbll) || !defined(__clang__) ++ return __builtin_clrsbll(val); ++#else ++ return clz64(val ^ ((int64_t)val >> 1)) - 1; ++#endif ++} ++ ++/** ++ * ctpop8 - count the population of one bits in an 8-bit value. ++ * @val: The value to search ++ */ ++static inline int ctpop8(uint8_t val) ++{ ++ return __builtin_popcount(val); ++} ++ ++/** ++ * ctpop16 - count the population of one bits in a 16-bit value. ++ * @val: The value to search ++ */ ++static inline int ctpop16(uint16_t val) ++{ ++ return __builtin_popcount(val); ++} ++ ++/** ++ * ctpop32 - count the population of one bits in a 32-bit value. ++ * @val: The value to search ++ */ ++static inline int ctpop32(uint32_t val) ++{ ++ return __builtin_popcount(val); ++} ++ ++/** ++ * ctpop64 - count the population of one bits in a 64-bit value. ++ * @val: The value to search ++ */ ++static inline int ctpop64(uint64_t val) ++{ ++ return __builtin_popcountll(val); ++} ++ ++/** ++ * revbit8 - reverse the bits in an 8-bit value. ++ * @x: The value to modify. ++ */ ++static inline uint8_t revbit8(uint8_t x) ++{ ++#if __has_builtin(__builtin_bitreverse8) ++ return __builtin_bitreverse8(x); ++#else ++ /* Assign the correct nibble position. */ ++ x = ((x & 0xf0) >> 4) ++ | ((x & 0x0f) << 4); ++ /* Assign the correct bit position. */ ++ x = ((x & 0x88) >> 3) ++ | ((x & 0x44) >> 1) ++ | ((x & 0x22) << 1) ++ | ((x & 0x11) << 3); ++ return x; ++#endif ++} ++ ++/** ++ * revbit16 - reverse the bits in a 16-bit value. ++ * @x: The value to modify. ++ */ ++static inline uint16_t revbit16(uint16_t x) ++{ ++#if __has_builtin(__builtin_bitreverse16) ++ return __builtin_bitreverse16(x); ++#else ++ /* Assign the correct byte position. */ ++ x = bswap16(x); ++ /* Assign the correct nibble position. */ ++ x = ((x & 0xf0f0) >> 4) ++ | ((x & 0x0f0f) << 4); ++ /* Assign the correct bit position. */ ++ x = ((x & 0x8888) >> 3) ++ | ((x & 0x4444) >> 1) ++ | ((x & 0x2222) << 1) ++ | ((x & 0x1111) << 3); ++ return x; ++#endif ++} ++ ++/** ++ * revbit32 - reverse the bits in a 32-bit value. ++ * @x: The value to modify. ++ */ ++static inline uint32_t revbit32(uint32_t x) ++{ ++#if __has_builtin(__builtin_bitreverse32) ++ return __builtin_bitreverse32(x); ++#else ++ /* Assign the correct byte position. */ ++ x = bswap32(x); ++ /* Assign the correct nibble position. */ ++ x = ((x & 0xf0f0f0f0u) >> 4) ++ | ((x & 0x0f0f0f0fu) << 4); ++ /* Assign the correct bit position. */ ++ x = ((x & 0x88888888u) >> 3) ++ | ((x & 0x44444444u) >> 1) ++ | ((x & 0x22222222u) << 1) ++ | ((x & 0x11111111u) << 3); ++ return x; ++#endif ++} ++ ++/** ++ * revbit64 - reverse the bits in a 64-bit value. ++ * @x: The value to modify. ++ */ ++static inline uint64_t revbit64(uint64_t x) ++{ ++#if __has_builtin(__builtin_bitreverse64) ++ return __builtin_bitreverse64(x); ++#else ++ /* Assign the correct byte position. */ ++ x = bswap64(x); ++ /* Assign the correct nibble position. */ ++ x = ((x & 0xf0f0f0f0f0f0f0f0ull) >> 4) ++ | ((x & 0x0f0f0f0f0f0f0f0full) << 4); ++ /* Assign the correct bit position. */ ++ x = ((x & 0x8888888888888888ull) >> 3) ++ | ((x & 0x4444444444444444ull) >> 1) ++ | ((x & 0x2222222222222222ull) << 1) ++ | ((x & 0x1111111111111111ull) << 3); ++ return x; ++#endif ++} ++ ++/** ++ * Return the absolute value of a 64-bit integer as an unsigned 64-bit value ++ */ ++static inline uint64_t uabs64(int64_t v) ++{ ++ return v < 0 ? -v : v; ++} ++ ++/** ++ * sadd32_overflow - addition with overflow indication ++ * @x, @y: addends ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x + @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool sadd32_overflow(int32_t x, int32_t y, int32_t *ret) ++{ ++ return __builtin_add_overflow(x, y, ret); ++} ++ ++/** ++ * sadd64_overflow - addition with overflow indication ++ * @x, @y: addends ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x + @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool sadd64_overflow(int64_t x, int64_t y, int64_t *ret) ++{ ++ return __builtin_add_overflow(x, y, ret); ++} ++ ++/** ++ * uadd32_overflow - addition with overflow indication ++ * @x, @y: addends ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x + @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool uadd32_overflow(uint32_t x, uint32_t y, uint32_t *ret) ++{ ++ return __builtin_add_overflow(x, y, ret); ++} ++ ++/** ++ * uadd64_overflow - addition with overflow indication ++ * @x, @y: addends ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x + @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool uadd64_overflow(uint64_t x, uint64_t y, uint64_t *ret) ++{ ++ return __builtin_add_overflow(x, y, ret); ++} ++ ++/** ++ * ssub32_overflow - subtraction with overflow indication ++ * @x: Minuend ++ * @y: Subtrahend ++ * @ret: Output for difference ++ * ++ * Computes *@ret = @x - @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool ssub32_overflow(int32_t x, int32_t y, int32_t *ret) ++{ ++ return __builtin_sub_overflow(x, y, ret); ++} ++ ++/** ++ * ssub64_overflow - subtraction with overflow indication ++ * @x: Minuend ++ * @y: Subtrahend ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x - @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool ssub64_overflow(int64_t x, int64_t y, int64_t *ret) ++{ ++ return __builtin_sub_overflow(x, y, ret); ++} ++ ++/** ++ * usub32_overflow - subtraction with overflow indication ++ * @x: Minuend ++ * @y: Subtrahend ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x - @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool usub32_overflow(uint32_t x, uint32_t y, uint32_t *ret) ++{ ++ return __builtin_sub_overflow(x, y, ret); ++} ++ ++/** ++ * usub64_overflow - subtraction with overflow indication ++ * @x: Minuend ++ * @y: Subtrahend ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x - @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool usub64_overflow(uint64_t x, uint64_t y, uint64_t *ret) ++{ ++ return __builtin_sub_overflow(x, y, ret); ++} ++ ++/** ++ * smul32_overflow - multiplication with overflow indication ++ * @x, @y: Input multipliers ++ * @ret: Output for product ++ * ++ * Computes *@ret = @x * @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool smul32_overflow(int32_t x, int32_t y, int32_t *ret) ++{ ++ return __builtin_mul_overflow(x, y, ret); ++} ++ ++/** ++ * smul64_overflow - multiplication with overflow indication ++ * @x, @y: Input multipliers ++ * @ret: Output for product ++ * ++ * Computes *@ret = @x * @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool smul64_overflow(int64_t x, int64_t y, int64_t *ret) ++{ ++ return __builtin_mul_overflow(x, y, ret); ++} ++ ++/** ++ * umul32_overflow - multiplication with overflow indication ++ * @x, @y: Input multipliers ++ * @ret: Output for product ++ * ++ * Computes *@ret = @x * @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool umul32_overflow(uint32_t x, uint32_t y, uint32_t *ret) ++{ ++ return __builtin_mul_overflow(x, y, ret); ++} ++ ++/** ++ * umul64_overflow - multiplication with overflow indication ++ * @x, @y: Input multipliers ++ * @ret: Output for product ++ * ++ * Computes *@ret = @x * @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool umul64_overflow(uint64_t x, uint64_t y, uint64_t *ret) ++{ ++ return __builtin_mul_overflow(x, y, ret); ++} ++ ++/* ++ * Unsigned 128x64 multiplication. ++ * Returns true if the result got truncated to 128 bits. ++ * Otherwise, returns false and the multiplication result via plow and phigh. ++ */ ++static inline bool mulu128(uint64_t *plow, uint64_t *phigh, uint64_t factor) ++{ ++#if defined(CONFIG_INT128) ++ bool res; ++ __uint128_t r; ++ __uint128_t f = ((__uint128_t)*phigh << 64) | *plow; ++ res = __builtin_mul_overflow(f, factor, &r); ++ ++ *plow = r; ++ *phigh = r >> 64; ++ ++ return res; ++#else ++ uint64_t dhi = *phigh; ++ uint64_t dlo = *plow; ++ uint64_t ahi; ++ uint64_t blo, bhi; ++ ++ if (dhi == 0) { ++ mulu64(plow, phigh, dlo, factor); ++ return false; ++ } ++ ++ mulu64(plow, &ahi, dlo, factor); ++ mulu64(&blo, &bhi, dhi, factor); ++ ++ return uadd64_overflow(ahi, blo, phigh) || bhi != 0; ++#endif ++} ++ ++/** ++ * uadd64_carry - addition with carry-in and carry-out ++ * @x, @y: addends ++ * @pcarry: in-out carry value ++ * ++ * Computes @x + @y + *@pcarry, placing the carry-out back ++ * into *@pcarry and returning the 64-bit sum. ++ */ ++static inline uint64_t uadd64_carry(uint64_t x, uint64_t y, bool *pcarry) ++{ ++#if __has_builtin(__builtin_addcll) ++ unsigned long long c = *pcarry; ++ x = __builtin_addcll(x, y, c, &c); ++ *pcarry = c & 1; ++ return x; ++#else ++ bool c = *pcarry; ++ /* This is clang's internal expansion of __builtin_addc. */ ++ c = uadd64_overflow(x, c, &x); ++ c |= uadd64_overflow(x, y, &x); ++ *pcarry = c; ++ return x; ++#endif ++} ++ ++/** ++ * usub64_borrow - subtraction with borrow-in and borrow-out ++ * @x, @y: addends ++ * @pborrow: in-out borrow value ++ * ++ * Computes @x - @y - *@pborrow, placing the borrow-out back ++ * into *@pborrow and returning the 64-bit sum. ++ */ ++static inline uint64_t usub64_borrow(uint64_t x, uint64_t y, bool *pborrow) ++{ ++#if __has_builtin(__builtin_subcll) && !defined(BUILTIN_SUBCLL_BROKEN) ++ unsigned long long b = *pborrow; ++ x = __builtin_subcll(x, y, b, &b); ++ *pborrow = b & 1; ++ return x; ++#else ++ bool b = *pborrow; ++ b = usub64_overflow(x, b, &x); ++ b |= usub64_overflow(x, y, &x); ++ *pborrow = b; ++ return x; ++#endif ++} ++ ++/* Host type specific sizes of these routines. */ ++ ++#if ULONG_MAX == UINT32_MAX ++# define clzl clz32 ++# define ctzl ctz32 ++# define clol clo32 ++# define ctol cto32 ++# define ctpopl ctpop32 ++# define revbitl revbit32 ++#elif ULONG_MAX == UINT64_MAX ++# define clzl clz64 ++# define ctzl ctz64 ++# define clol clo64 ++# define ctol cto64 ++# define ctpopl ctpop64 ++# define revbitl revbit64 ++#else ++# error Unknown sizeof long ++#endif ++ ++static inline bool is_power_of_2(uint64_t value) ++{ ++ if (!value) { ++ return false; ++ } ++ ++ return !(value & (value - 1)); ++} ++ ++/** ++ * Return @value rounded down to the nearest power of two or zero. ++ */ ++static inline uint64_t pow2floor(uint64_t value) ++{ ++ if (!value) { ++ /* Avoid undefined shift by 64 */ ++ return 0; ++ } ++ return 0x8000000000000000ull >> clz64(value); ++} ++ ++/* ++ * Return @value rounded up to the nearest power of two modulo 2^64. ++ * This is *zero* for @value > 2^63, so be careful. ++ */ ++static inline uint64_t pow2ceil(uint64_t value) ++{ ++ int n = clz64(value - 1); ++ ++ if (!n) { ++ /* ++ * @value - 1 has no leading zeroes, thus @value - 1 >= 2^63 ++ * Therefore, either @value == 0 or @value > 2^63. ++ * If it's 0, return 1, else return 0. ++ */ ++ return !value; ++ } ++ return 0x8000000000000000ull >> (n - 1); ++} ++ ++static inline uint32_t pow2roundup32(uint32_t x) ++{ ++ x |= (x >> 1); ++ x |= (x >> 2); ++ x |= (x >> 4); ++ x |= (x >> 8); ++ x |= (x >> 16); ++ return x + 1; ++} ++ ++/** ++ * urshift - 128-bit Unsigned Right Shift. ++ * @plow: in/out - lower 64-bit integer. ++ * @phigh: in/out - higher 64-bit integer. ++ * @shift: in - bytes to shift, between 0 and 127. ++ * ++ * Result is zero-extended and stored in plow/phigh, which are ++ * input/output variables. Shift values outside the range will ++ * be mod to 128. In other words, the caller is responsible to ++ * verify/assert both the shift range and plow/phigh pointers. ++ */ ++void urshift(uint64_t *plow, uint64_t *phigh, int32_t shift); ++ ++/** ++ * ulshift - 128-bit Unsigned Left Shift. ++ * @plow: in/out - lower 64-bit integer. ++ * @phigh: in/out - higher 64-bit integer. ++ * @shift: in - bytes to shift, between 0 and 127. ++ * @overflow: out - true if any 1-bit is shifted out. ++ * ++ * Result is zero-extended and stored in plow/phigh, which are ++ * input/output variables. Shift values outside the range will ++ * be mod to 128. In other words, the caller is responsible to ++ * verify/assert both the shift range and plow/phigh pointers. ++ */ ++void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow); ++ ++/* From the GNU Multi Precision Library - longlong.h __udiv_qrnnd ++ * (https://gmplib.org/repo/gmp/file/tip/longlong.h) ++ * ++ * Licensed under the GPLv2/LGPLv3 ++ */ ++static inline uint64_t udiv_qrnnd(uint64_t *r, uint64_t n1, ++ uint64_t n0, uint64_t d) ++{ ++#if defined(__x86_64__) ++ uint64_t q; ++ asm("divq %4" : "=a"(q), "=d"(*r) : "0"(n0), "1"(n1), "rm"(d)); ++ return q; ++#elif defined(__s390x__) && !defined(__clang__) ++ /* Need to use a TImode type to get an even register pair for DLGR. */ ++ unsigned __int128 n = (unsigned __int128)n1 << 64 | n0; ++ asm("dlgr %0, %1" : "+r"(n) : "r"(d)); ++ *r = n >> 64; ++ return n; ++#elif defined(_ARCH_PPC64) && defined(_ARCH_PWR7) ++ /* From Power ISA 2.06, programming note for divdeu. */ ++ uint64_t q1, q2, Q, r1, r2, R; ++ asm("divdeu %0,%2,%4; divdu %1,%3,%4" ++ : "=&r"(q1), "=r"(q2) ++ : "r"(n1), "r"(n0), "r"(d)); ++ r1 = -(q1 * d); /* low part of (n1<<64) - (q1 * d) */ ++ r2 = n0 - (q2 * d); ++ Q = q1 + q2; ++ R = r1 + r2; ++ if (R >= d || R < r2) { /* overflow implies R > d */ ++ Q += 1; ++ R -= d; ++ } ++ *r = R; ++ return Q; ++#else ++ uint64_t d0, d1, q0, q1, r1, r0, m; ++ ++ d0 = (uint32_t)d; ++ d1 = d >> 32; ++ ++ r1 = n1 % d1; ++ q1 = n1 / d1; ++ m = q1 * d0; ++ r1 = (r1 << 32) | (n0 >> 32); ++ if (r1 < m) { ++ q1 -= 1; ++ r1 += d; ++ if (r1 >= d) { ++ if (r1 < m) { ++ q1 -= 1; ++ r1 += d; ++ } ++ } ++ } ++ r1 -= m; ++ ++ r0 = r1 % d1; ++ q0 = r1 / d1; ++ m = q0 * d0; ++ r0 = (r0 << 32) | (uint32_t)n0; ++ if (r0 < m) { ++ q0 -= 1; ++ r0 += d; ++ if (r0 >= d) { ++ if (r0 < m) { ++ q0 -= 1; ++ r0 += d; ++ } ++ } ++ } ++ r0 -= m; ++ ++ *r = r0; ++ return (q1 << 32) | q0; ++#endif ++} ++ ++Int128 divu256(Int128 *plow, Int128 *phigh, Int128 divisor); ++Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor); ++#endif +diff --git a/include/qemu/id.h b/include/qemu/id.h +new file mode 100644 +index 00000000..46b759b2 +--- /dev/null ++++ b/include/qemu/id.h +@@ -0,0 +1,15 @@ ++#ifndef QEMU_ID_H ++#define QEMU_ID_H ++ ++typedef enum IdSubSystems { ++ ID_QDEV, ++ ID_BLOCK, ++ ID_CHR, ++ ID_NET, ++ ID_MAX /* last element, used as array size */ ++} IdSubSystems; ++ ++char *id_generate(IdSubSystems id); ++bool id_wellformed(const char *id); ++ ++#endif +diff --git a/include/qemu/iov.h b/include/qemu/iov.h +new file mode 100644 +index 00000000..63a1c019 +--- /dev/null ++++ b/include/qemu/iov.h +@@ -0,0 +1,250 @@ ++/* ++ * Helpers for using (partial) iovecs. ++ * ++ * Copyright (C) 2010 Red Hat, Inc. ++ * ++ * Author(s): ++ * Amit Shah ++ * Michael Tokarev ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ */ ++ ++#ifndef IOV_H ++#define IOV_H ++ ++/** ++ * count and return data size, in bytes, of an iovec ++ * starting at `iov' of `iov_cnt' number of elements. ++ */ ++size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt); ++ ++/** ++ * Copy from single continuous buffer to scatter-gather vector of buffers ++ * (iovec) and back like memcpy() between two continuous memory regions. ++ * Data in single continuous buffer starting at address `buf' and ++ * `bytes' bytes long will be copied to/from an iovec `iov' with ++ * `iov_cnt' number of elements, starting at byte position `offset' ++ * within the iovec. If the iovec does not contain enough space, ++ * only part of data will be copied, up to the end of the iovec. ++ * Number of bytes actually copied will be returned, which is ++ * min(bytes, iov_size(iov)-offset) ++ * `Offset' must point to the inside of iovec. ++ */ ++size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt, ++ size_t offset, const void *buf, size_t bytes); ++size_t iov_to_buf_full(const struct iovec *iov, const unsigned int iov_cnt, ++ size_t offset, void *buf, size_t bytes); ++ ++static inline size_t ++iov_from_buf(const struct iovec *iov, unsigned int iov_cnt, ++ size_t offset, const void *buf, size_t bytes) ++{ ++ if (__builtin_constant_p(bytes) && iov_cnt && ++ offset <= iov[0].iov_len && bytes <= iov[0].iov_len - offset) { ++ memcpy(iov[0].iov_base + offset, buf, bytes); ++ return bytes; ++ } else { ++ return iov_from_buf_full(iov, iov_cnt, offset, buf, bytes); ++ } ++} ++ ++static inline size_t ++iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, ++ size_t offset, void *buf, size_t bytes) ++{ ++ if (__builtin_constant_p(bytes) && iov_cnt && ++ offset <= iov[0].iov_len && bytes <= iov[0].iov_len - offset) { ++ memcpy(buf, iov[0].iov_base + offset, bytes); ++ return bytes; ++ } else { ++ return iov_to_buf_full(iov, iov_cnt, offset, buf, bytes); ++ } ++} ++ ++/** ++ * Set data bytes pointed out by iovec `iov' of size `iov_cnt' elements, ++ * starting at byte offset `start', to value `fillc', repeating it ++ * `bytes' number of times. `Offset' must point to the inside of iovec. ++ * If `bytes' is large enough, only last bytes portion of iovec, ++ * up to the end of it, will be filled with the specified value. ++ * Function return actual number of bytes processed, which is ++ * min(size, iov_size(iov) - offset). ++ */ ++size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt, ++ size_t offset, int fillc, size_t bytes); ++ ++/* ++ * Send/recv data from/to iovec buffers directly ++ * ++ * `offset' bytes in the beginning of iovec buffer are skipped and ++ * next `bytes' bytes are used, which must be within data of iovec. ++ * ++ * r = iov_send_recv(sockfd, iov, iovcnt, offset, bytes, true); ++ * ++ * is logically equivalent to ++ * ++ * char *buf = malloc(bytes); ++ * iov_to_buf(iov, iovcnt, offset, buf, bytes); ++ * r = send(sockfd, buf, bytes, 0); ++ * free(buf); ++ * ++ * For iov_send_recv() _whole_ area being sent or received ++ * should be within the iovec, not only beginning of it. ++ */ ++ssize_t iov_send_recv(int sockfd, const struct iovec *iov, unsigned iov_cnt, ++ size_t offset, size_t bytes, bool do_send); ++#define iov_recv(sockfd, iov, iov_cnt, offset, bytes) \ ++ iov_send_recv(sockfd, iov, iov_cnt, offset, bytes, false) ++#define iov_send(sockfd, iov, iov_cnt, offset, bytes) \ ++ iov_send_recv(sockfd, iov, iov_cnt, offset, bytes, true) ++ ++/** ++ * Produce a text hexdump of iovec `iov' with `iov_cnt' number of elements ++ * in file `fp', prefixing each line with `prefix' and processing not more ++ * than `limit' data bytes. ++ */ ++void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, ++ FILE *fp, const char *prefix, size_t limit); ++ ++/* ++ * Partial copy of vector from iov to dst_iov (data is not copied). ++ * dst_iov overlaps iov at a specified offset. ++ * size of dst_iov is at most bytes. dst vector count is returned. ++ */ ++unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt, ++ const struct iovec *iov, unsigned int iov_cnt, ++ size_t offset, size_t bytes); ++ ++/* ++ * Remove a given number of bytes from the front or back of a vector. ++ * This may update iov and/or iov_cnt to exclude iovec elements that are ++ * no longer required. ++ * ++ * The number of bytes actually discarded is returned. This number may be ++ * smaller than requested if the vector is too small. ++ */ ++size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt, ++ size_t bytes); ++size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt, ++ size_t bytes); ++ ++/* Information needed to undo an iov_discard_*() operation */ ++typedef struct { ++ struct iovec *modified_iov; ++ struct iovec orig; ++} IOVDiscardUndo; ++ ++/* ++ * Undo an iov_discard_front_undoable() or iov_discard_back_undoable() ++ * operation. If multiple operations are made then each one needs a separate ++ * IOVDiscardUndo and iov_discard_undo() must be called in the reverse order ++ * that the operations were made. ++ */ ++void iov_discard_undo(IOVDiscardUndo *undo); ++ ++/* ++ * Undoable versions of iov_discard_front() and iov_discard_back(). Use ++ * iov_discard_undo() to reset to the state before the discard operations. ++ */ ++size_t iov_discard_front_undoable(struct iovec **iov, unsigned int *iov_cnt, ++ size_t bytes, IOVDiscardUndo *undo); ++size_t iov_discard_back_undoable(struct iovec *iov, unsigned int *iov_cnt, ++ size_t bytes, IOVDiscardUndo *undo); ++ ++typedef struct QEMUIOVector { ++ struct iovec *iov; ++ int niov; ++ ++ /* ++ * For external @iov (qemu_iovec_init_external()) or allocated @iov ++ * (qemu_iovec_init()), @size is the cumulative size of iovecs and ++ * @local_iov is invalid and unused. ++ * ++ * For embedded @iov (QEMU_IOVEC_INIT_BUF() or qemu_iovec_init_buf()), ++ * @iov is equal to &@local_iov, and @size is valid, as it has same ++ * offset and type as @local_iov.iov_len, which is guaranteed by ++ * static assertion below. ++ * ++ * @nalloc is always valid and is -1 both for embedded and external ++ * cases. It is included in the union only to ensure the padding prior ++ * to the @size field will not result in a 0-length array. ++ */ ++ union { ++ struct { ++ int nalloc; ++ struct iovec local_iov; ++ }; ++ struct { ++ char __pad[sizeof(int) + offsetof(struct iovec, iov_len)]; ++ size_t size; ++ }; ++ }; ++} QEMUIOVector; ++ ++QEMU_BUILD_BUG_ON(offsetof(QEMUIOVector, size) != ++ offsetof(QEMUIOVector, local_iov.iov_len)); ++ ++#define QEMU_IOVEC_INIT_BUF(self, buf, len) \ ++{ \ ++ .iov = &(self).local_iov, \ ++ .niov = 1, \ ++ .nalloc = -1, \ ++ .local_iov = { \ ++ .iov_base = (void *)(buf), /* cast away const */ \ ++ .iov_len = (len), \ ++ }, \ ++} ++ ++/* ++ * qemu_iovec_init_buf ++ * ++ * Initialize embedded QEMUIOVector. ++ * ++ * Note: "const" is used over @buf pointer to make it simple to pass ++ * const pointers, appearing in read functions. Then this "const" is ++ * cast away by QEMU_IOVEC_INIT_BUF(). ++ */ ++static inline void qemu_iovec_init_buf(QEMUIOVector *qiov, ++ const void *buf, size_t len) ++{ ++ *qiov = (QEMUIOVector) QEMU_IOVEC_INIT_BUF(*qiov, buf, len); ++} ++ ++static inline void *qemu_iovec_buf(QEMUIOVector *qiov) ++{ ++ /* Only supports embedded iov */ ++ assert(qiov->nalloc == -1 && qiov->iov == &qiov->local_iov); ++ ++ return qiov->local_iov.iov_base; ++} ++ ++void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint); ++void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov); ++void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source, ++ size_t offset, size_t len); ++struct iovec *qemu_iovec_slice(QEMUIOVector *qiov, ++ size_t offset, size_t len, ++ size_t *head, size_t *tail, int *niov); ++int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len); ++void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len); ++void qemu_iovec_concat(QEMUIOVector *dst, ++ QEMUIOVector *src, size_t soffset, size_t sbytes); ++size_t qemu_iovec_concat_iov(QEMUIOVector *dst, ++ struct iovec *src_iov, unsigned int src_cnt, ++ size_t soffset, size_t sbytes); ++bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t qiov_offeset, size_t bytes); ++void qemu_iovec_destroy(QEMUIOVector *qiov); ++void qemu_iovec_reset(QEMUIOVector *qiov); ++size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset, ++ void *buf, size_t bytes); ++size_t qemu_iovec_from_buf(QEMUIOVector *qiov, size_t offset, ++ const void *buf, size_t bytes); ++size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset, ++ int fillc, size_t bytes); ++ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b); ++void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf); ++void qemu_iovec_discard_back(QEMUIOVector *qiov, size_t bytes); ++ ++#endif +diff --git a/include/qemu/job.h b/include/qemu/job.h +new file mode 100644 +index 00000000..2b873f25 +--- /dev/null ++++ b/include/qemu/job.h +@@ -0,0 +1,731 @@ ++/* ++ * Declarations for background jobs ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012, 2018 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef JOB_H ++#define JOB_H ++ ++#include "qapi/qapi-types-job.h" ++#include "qemu/queue.h" ++#include "qemu/progress_meter.h" ++#include "qemu/coroutine.h" ++#include "block/aio.h" ++ ++typedef struct JobDriver JobDriver; ++typedef struct JobTxn JobTxn; ++ ++ ++/** ++ * Long-running operation. ++ */ ++typedef struct Job { ++ ++ /* Fields set at initialization (job_create), and never modified */ ++ ++ /** The ID of the job. May be NULL for internal jobs. */ ++ char *id; ++ ++ /** ++ * The type of this job. ++ * All callbacks are called with job_mutex *not* held. ++ */ ++ const JobDriver *driver; ++ ++ /** ++ * The coroutine that executes the job. If not NULL, it is reentered when ++ * busy is false and the job is cancelled. ++ * Initialized in job_start() ++ */ ++ Coroutine *co; ++ ++ /** True if this job should automatically finalize itself */ ++ bool auto_finalize; ++ ++ /** True if this job should automatically dismiss itself */ ++ bool auto_dismiss; ++ ++ /** ++ * The completion function that will be called when the job completes. ++ */ ++ BlockCompletionFunc *cb; ++ ++ /** The opaque value that is passed to the completion function. */ ++ void *opaque; ++ ++ /* ProgressMeter API is thread-safe */ ++ ProgressMeter progress; ++ ++ /** ++ * AioContext to run the job coroutine in. ++ * The job Aiocontext can be read when holding *either* ++ * the BQL (so we are in the main loop) or the job_mutex. ++ * It can only be written when we hold *both* BQL ++ * and the job_mutex. ++ */ ++ AioContext *aio_context; ++ ++ ++ /** Protected by job_mutex */ ++ ++ /** Reference count of the block job */ ++ int refcnt; ++ ++ /** Current state; See @JobStatus for details. */ ++ JobStatus status; ++ ++ /** ++ * Timer that is used by @job_sleep_ns. Accessed under job_mutex (in ++ * job.c). ++ */ ++ QEMUTimer sleep_timer; ++ ++ /** ++ * Counter for pause request. If non-zero, the block job is either paused, ++ * or if busy == true will pause itself as soon as possible. ++ */ ++ int pause_count; ++ ++ /** ++ * Set to false by the job while the coroutine has yielded and may be ++ * re-entered by job_enter(). There may still be I/O or event loop activity ++ * pending. Accessed under job_mutex. ++ * ++ * When the job is deferred to the main loop, busy is true as long as the ++ * bottom half is still pending. ++ */ ++ bool busy; ++ ++ /** ++ * Set to true by the job while it is in a quiescent state, where ++ * no I/O or event loop activity is pending. ++ */ ++ bool paused; ++ ++ /** ++ * Set to true if the job is paused by user. Can be unpaused with the ++ * block-job-resume QMP command. ++ */ ++ bool user_paused; ++ ++ /** ++ * Set to true if the job should cancel itself. The flag must ++ * always be tested just before toggling the busy flag from false ++ * to true. After a job has been cancelled, it should only yield ++ * if #aio_poll will ("sooner or later") reenter the coroutine. ++ */ ++ bool cancelled; ++ ++ /** ++ * Set to true if the job should abort immediately without waiting ++ * for data to be in sync. ++ */ ++ bool force_cancel; ++ ++ /** Set to true when the job has deferred work to the main loop. */ ++ bool deferred_to_main_loop; ++ ++ /** ++ * Return code from @run and/or @prepare callback(s). ++ * Not final until the job has reached the CONCLUDED status. ++ * 0 on success, -errno on failure. ++ */ ++ int ret; ++ ++ /** ++ * Error object for a failed job. ++ * If job->ret is nonzero and an error object was not set, it will be set ++ * to strerror(-job->ret) during job_completed. ++ */ ++ Error *err; ++ ++ /** Notifiers called when a cancelled job is finalised */ ++ NotifierList on_finalize_cancelled; ++ ++ /** Notifiers called when a successfully completed job is finalised */ ++ NotifierList on_finalize_completed; ++ ++ /** Notifiers called when the job transitions to PENDING */ ++ NotifierList on_pending; ++ ++ /** Notifiers called when the job transitions to READY */ ++ NotifierList on_ready; ++ ++ /** Notifiers called when the job coroutine yields or terminates */ ++ NotifierList on_idle; ++ ++ /** Element of the list of jobs */ ++ QLIST_ENTRY(Job) job_list; ++ ++ /** Transaction this job is part of */ ++ JobTxn *txn; ++ ++ /** Element of the list of jobs in a job transaction */ ++ QLIST_ENTRY(Job) txn_list; ++} Job; ++ ++/** ++ * Callbacks and other information about a Job driver. ++ * All callbacks are invoked with job_mutex *not* held. ++ */ ++struct JobDriver { ++ ++ /* ++ * These fields are initialized when this object is created, ++ * and are never changed afterwards ++ */ ++ ++ /** Derived Job struct size */ ++ size_t instance_size; ++ ++ /** Enum describing the operation */ ++ JobType job_type; ++ ++ /** ++ * Mandatory: Entrypoint for the Coroutine. ++ * ++ * This callback will be invoked when moving from CREATED to RUNNING. ++ * ++ * If this callback returns nonzero, the job transaction it is part of is ++ * aborted. If it returns zero, the job moves into the WAITING state. If it ++ * is the last job to complete in its transaction, all jobs in the ++ * transaction move from WAITING to PENDING. ++ * ++ * This callback must be run in the job's context. ++ */ ++ int coroutine_fn (*run)(Job *job, Error **errp); ++ ++ /* ++ * Functions run without regard to the BQL that may run in any ++ * arbitrary thread. These functions do not need to be thread-safe ++ * because the caller ensures that they are invoked from one ++ * thread at time. ++ */ ++ ++ /** ++ * If the callback is not NULL, it will be invoked when the job transitions ++ * into the paused state. Paused jobs must not perform any asynchronous ++ * I/O or event loop activity. This callback is used to quiesce jobs. ++ */ ++ void coroutine_fn (*pause)(Job *job); ++ ++ /** ++ * If the callback is not NULL, it will be invoked when the job transitions ++ * out of the paused state. Any asynchronous I/O or event loop activity ++ * should be restarted from this callback. ++ */ ++ void coroutine_fn (*resume)(Job *job); ++ ++ /* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++ /** ++ * Called when the job is resumed by the user (i.e. user_paused becomes ++ * false). .user_resume is called before .resume. ++ */ ++ void (*user_resume)(Job *job); ++ ++ /** ++ * Optional callback for job types whose completion must be triggered ++ * manually. ++ */ ++ void (*complete)(Job *job, Error **errp); ++ ++ /** ++ * If the callback is not NULL, prepare will be invoked when all the jobs ++ * belonging to the same transaction complete; or upon this job's completion ++ * if it is not in a transaction. ++ * ++ * This callback will not be invoked if the job has already failed. ++ * If it fails, abort and then clean will be called. ++ */ ++ int (*prepare)(Job *job); ++ ++ /** ++ * If the callback is not NULL, it will be invoked when all the jobs ++ * belonging to the same transaction complete; or upon this job's ++ * completion if it is not in a transaction. Skipped if NULL. ++ * ++ * All jobs will complete with a call to either .commit() or .abort() but ++ * never both. ++ */ ++ void (*commit)(Job *job); ++ ++ /** ++ * If the callback is not NULL, it will be invoked when any job in the ++ * same transaction fails; or upon this job's failure (due to error or ++ * cancellation) if it is not in a transaction. Skipped if NULL. ++ * ++ * All jobs will complete with a call to either .commit() or .abort() but ++ * never both. ++ */ ++ void (*abort)(Job *job); ++ ++ /** ++ * If the callback is not NULL, it will be invoked after a call to either ++ * .commit() or .abort(). Regardless of which callback is invoked after ++ * completion, .clean() will always be called, even if the job does not ++ * belong to a transaction group. ++ */ ++ void (*clean)(Job *job); ++ ++ /** ++ * If the callback is not NULL, it will be invoked in job_cancel_async ++ * ++ * This function must return true if the job will be cancelled ++ * immediately without any further I/O (mandatory if @force is ++ * true), and false otherwise. This lets the generic job layer ++ * know whether a job has been truly (force-)cancelled, or whether ++ * it is just in a special completion mode (like mirror after ++ * READY). ++ * (If the callback is NULL, the job is assumed to terminate ++ * without I/O.) ++ */ ++ bool (*cancel)(Job *job, bool force); ++ ++ ++ /** ++ * Called when the job is freed. ++ */ ++ void (*free)(Job *job); ++}; ++ ++typedef enum JobCreateFlags { ++ /* Default behavior */ ++ JOB_DEFAULT = 0x00, ++ /* Job is not QMP-created and should not send QMP events */ ++ JOB_INTERNAL = 0x01, ++ /* Job requires manual finalize step */ ++ JOB_MANUAL_FINALIZE = 0x02, ++ /* Job requires manual dismiss step */ ++ JOB_MANUAL_DISMISS = 0x04, ++} JobCreateFlags; ++ ++extern QemuMutex job_mutex; ++ ++#define JOB_LOCK_GUARD() QEMU_LOCK_GUARD(&job_mutex) ++ ++#define WITH_JOB_LOCK_GUARD() WITH_QEMU_LOCK_GUARD(&job_mutex) ++ ++/** ++ * job_lock: ++ * ++ * Take the mutex protecting the list of jobs and their status. ++ * Most functions called by the monitor need to call job_lock ++ * and job_unlock manually. On the other hand, function called ++ * by the block jobs themselves and by the block layer will take the ++ * lock for you. ++ */ ++void job_lock(void); ++ ++/** ++ * job_unlock: ++ * ++ * Release the mutex protecting the list of jobs and their status. ++ */ ++void job_unlock(void); ++ ++/** ++ * Allocate and return a new job transaction. Jobs can be added to the ++ * transaction using job_txn_add_job(). ++ * ++ * The transaction is automatically freed when the last job completes or is ++ * cancelled. ++ * ++ * All jobs in the transaction either complete successfully or fail/cancel as a ++ * group. Jobs wait for each other before completing. Cancelling one job ++ * cancels all jobs in the transaction. ++ */ ++JobTxn *job_txn_new(void); ++ ++/** ++ * Release a reference that was previously acquired with job_txn_add_job or ++ * job_txn_new. If it's the last reference to the object, it will be freed. ++ * ++ * Called with job lock *not* held. ++ */ ++void job_txn_unref(JobTxn *txn); ++ ++/* ++ * Same as job_txn_unref(), but called with job lock held. ++ * Might release the lock temporarily. ++ */ ++void job_txn_unref_locked(JobTxn *txn); ++ ++/** ++ * Create a new long-running job and return it. ++ * Called with job_mutex *not* held. ++ * ++ * @job_id: The id of the newly-created job, or %NULL for internal jobs ++ * @driver: The class object for the newly-created job. ++ * @txn: The transaction this job belongs to, if any. %NULL otherwise. ++ * @ctx: The AioContext to run the job coroutine in. ++ * @flags: Creation flags for the job. See @JobCreateFlags. ++ * @cb: Completion function for the job. ++ * @opaque: Opaque pointer value passed to @cb. ++ * @errp: Error object. ++ */ ++void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, ++ AioContext *ctx, int flags, BlockCompletionFunc *cb, ++ void *opaque, Error **errp); ++ ++/** ++ * Add a reference to Job refcnt, it will be decreased with job_unref, and then ++ * be freed if it comes to be the last reference. ++ * ++ * Called with job lock held. ++ */ ++void job_ref_locked(Job *job); ++ ++/** ++ * Release a reference that was previously acquired with job_ref_locked() or ++ * job_create(). If it's the last reference to the object, it will be freed. ++ * ++ * Called with job lock held. ++ */ ++void job_unref_locked(Job *job); ++ ++/** ++ * @job: The job that has made progress ++ * @done: How much progress the job made since the last call ++ * ++ * Updates the progress counter of the job. ++ * ++ * May be called with mutex held or not held. ++ */ ++void job_progress_update(Job *job, uint64_t done); ++ ++/** ++ * @job: The job whose expected progress end value is set ++ * @remaining: Missing progress (on top of the current progress counter value) ++ * until the new expected end value is reached ++ * ++ * Sets the expected end value of the progress counter of a job so that a ++ * completion percentage can be calculated when the progress is updated. ++ * ++ * May be called with mutex held or not held. ++ */ ++void job_progress_set_remaining(Job *job, uint64_t remaining); ++ ++/** ++ * @job: The job whose expected progress end value is updated ++ * @delta: Value which is to be added to the current expected end ++ * value ++ * ++ * Increases the expected end value of the progress counter of a job. ++ * This is useful for parenthesis operations: If a job has to ++ * conditionally perform a high-priority operation as part of its ++ * progress, it calls this function with the expected operation's ++ * length before, and job_progress_update() afterwards. ++ * (So the operation acts as a parenthesis in regards to the main job ++ * operation running in background.) ++ * ++ * May be called with mutex held or not held. ++ */ ++void job_progress_increase_remaining(Job *job, uint64_t delta); ++ ++/** ++ * Conditionally enter the job coroutine if the job is ready to run, not ++ * already busy and fn() returns true. fn() is called while under the job_lock ++ * critical section. ++ * ++ * Called with job lock held, but might release it temporarily. ++ */ ++void job_enter_cond_locked(Job *job, bool(*fn)(Job *job)); ++ ++/** ++ * @job: A job that has not yet been started. ++ * ++ * Begins execution of a job. ++ * Takes ownership of one reference to the job object. ++ * ++ * Called with job_mutex *not* held. ++ */ ++void job_start(Job *job); ++ ++/** ++ * @job: The job to enter. ++ * ++ * Continue the specified job by entering the coroutine. ++ * Called with job_mutex *not* held. ++ */ ++void job_enter(Job *job); ++ ++/** ++ * @job: The job that is ready to pause. ++ * ++ * Pause now if job_pause() has been called. Jobs that perform lots of I/O ++ * must call this between requests so that the job can be paused. ++ * ++ * Called with job_mutex *not* held. ++ */ ++void coroutine_fn GRAPH_UNLOCKED job_pause_point(Job *job); ++ ++/** ++ * @job: The job that calls the function. ++ * ++ * Yield the job coroutine. ++ * Called with job_mutex *not* held. ++ */ ++void coroutine_fn job_yield(Job *job); ++ ++/** ++ * @job: The job that calls the function. ++ * @ns: How many nanoseconds to stop for. ++ * ++ * Put the job to sleep (assuming that it wasn't canceled) for @ns ++ * %QEMU_CLOCK_REALTIME nanoseconds. Canceling the job will immediately ++ * interrupt the wait. ++ * ++ * Called with job_mutex *not* held. ++ */ ++void coroutine_fn job_sleep_ns(Job *job, int64_t ns); ++ ++/** Returns the JobType of a given Job. */ ++JobType job_type(const Job *job); ++ ++/** Returns the enum string for the JobType of a given Job. */ ++const char *job_type_str(const Job *job); ++ ++/** Returns true if the job should not be visible to the management layer. */ ++bool job_is_internal(Job *job); ++ ++/** ++ * Returns whether the job is being cancelled. ++ * Called with job_mutex *not* held. ++ */ ++bool job_is_cancelled(Job *job); ++ ++/* Same as job_is_cancelled(), but called with job lock held. */ ++bool job_is_cancelled_locked(Job *job); ++ ++/** ++ * Returns whether the job is scheduled for cancellation (at an ++ * indefinite point). ++ * Called with job_mutex *not* held. ++ */ ++bool job_cancel_requested(Job *job); ++ ++/** ++ * Returns whether the job is in a completed state. ++ * Called with job lock held. ++ */ ++bool job_is_completed_locked(Job *job); ++ ++/** ++ * Returns whether the job is ready to be completed. ++ * Called with job_mutex *not* held. ++ */ ++bool job_is_ready(Job *job); ++ ++/* Same as job_is_ready(), but called with job lock held. */ ++bool job_is_ready_locked(Job *job); ++ ++/** ++ * Request @job to pause at the next pause point. Must be paired with ++ * job_resume(). If the job is supposed to be resumed by user action, call ++ * job_user_pause_locked() instead. ++ * ++ * Called with job lock *not* held. ++ */ ++void job_pause(Job *job); ++ ++/* Same as job_pause(), but called with job lock held. */ ++void job_pause_locked(Job *job); ++ ++/** Resumes a @job paused with job_pause. Called with job lock *not* held. */ ++void job_resume(Job *job); ++ ++/* ++ * Same as job_resume(), but called with job lock held. ++ * Might release the lock temporarily. ++ */ ++void job_resume_locked(Job *job); ++ ++/** ++ * Asynchronously pause the specified @job. ++ * Do not allow a resume until a matching call to job_user_resume. ++ * Called with job lock held. ++ */ ++void job_user_pause_locked(Job *job, Error **errp); ++ ++/** ++ * Returns true if the job is user-paused. ++ * Called with job lock held. ++ */ ++bool job_user_paused_locked(Job *job); ++ ++/** ++ * Resume the specified @job. ++ * Must be paired with a preceding job_user_pause_locked. ++ * Called with job lock held, but might release it temporarily. ++ */ ++void job_user_resume_locked(Job *job, Error **errp); ++ ++/** ++ * Get the next element from the list of block jobs after @job, or the ++ * first one if @job is %NULL. ++ * ++ * Returns the requested job, or %NULL if there are no more jobs left. ++ * Called with job lock *not* held. ++ */ ++Job *job_next(Job *job); ++ ++/* Same as job_next(), but called with job lock held. */ ++Job *job_next_locked(Job *job); ++ ++/** ++ * Get the job identified by @id (which must not be %NULL). ++ * ++ * Returns the requested job, or %NULL if it doesn't exist. ++ * Called with job lock held. ++ */ ++Job *job_get_locked(const char *id); ++ ++/** ++ * Check whether the verb @verb can be applied to @job in its current state. ++ * Returns 0 if the verb can be applied; otherwise errp is set and -EPERM ++ * returned. ++ * ++ * Called with job lock held. ++ */ ++int job_apply_verb_locked(Job *job, JobVerb verb, Error **errp); ++ ++/** ++ * The @job could not be started, free it. ++ * Called with job_mutex *not* held. ++ */ ++void job_early_fail(Job *job); ++ ++/** ++ * Moves the @job from RUNNING to READY. ++ * Called with job_mutex *not* held. ++ */ ++void job_transition_to_ready(Job *job); ++ ++/** ++ * Asynchronously complete the specified @job. ++ * Called with job lock held, but might release it temporarily. ++ */ ++void job_complete_locked(Job *job, Error **errp); ++ ++/** ++ * Asynchronously cancel the specified @job. If @force is true, the job should ++ * be cancelled immediately without waiting for a consistent state. ++ * Called with job lock held. ++ */ ++void job_cancel_locked(Job *job, bool force); ++ ++/** ++ * Cancels the specified job like job_cancel_locked(), but may refuse ++ * to do so if the operation isn't meaningful in the current state of the job. ++ * Called with job lock held. ++ */ ++void job_user_cancel_locked(Job *job, bool force, Error **errp); ++ ++/** ++ * Synchronously cancel the @job. The completion callback is called ++ * before the function returns. If @force is false, the job may ++ * actually complete instead of canceling itself; the circumstances ++ * under which this happens depend on the kind of job that is active. ++ * ++ * Returns the return value from the job if the job actually completed ++ * during the call, or -ECANCELED if it was canceled. ++ * ++ * Called with job_lock *not* held. ++ */ ++int job_cancel_sync(Job *job, bool force); ++ ++/* Same as job_cancel_sync, but called with job lock held. */ ++int job_cancel_sync_locked(Job *job, bool force); ++ ++/** ++ * Synchronously force-cancels all jobs using job_cancel_sync_locked(). ++ * ++ * Called with job_lock *not* held. ++ */ ++void job_cancel_sync_all(void); ++ ++/** ++ * @job: The job to be completed. ++ * @errp: Error object which may be set by job_complete_locked(); this is not ++ * necessarily set on every error, the job return value has to be ++ * checked as well. ++ * ++ * Synchronously complete the job. The completion callback is called before the ++ * function returns, unless it is NULL (which is permissible when using this ++ * function). ++ * ++ * Returns the return value from the job. ++ * Called with job_lock held. ++ */ ++int job_complete_sync_locked(Job *job, Error **errp); ++ ++/** ++ * For a @job that has finished its work and is pending awaiting explicit ++ * acknowledgement to commit its work, this will commit that work. ++ * ++ * FIXME: Make the below statement universally true: ++ * For jobs that support the manual workflow mode, all graph changes that occur ++ * as a result will occur after this command and before a successful reply. ++ * ++ * Called with job lock held. ++ */ ++void job_finalize_locked(Job *job, Error **errp); ++ ++/** ++ * Remove the concluded @job from the query list and resets the passed pointer ++ * to %NULL. Returns an error if the job is not actually concluded. ++ * ++ * Called with job lock held. ++ */ ++void job_dismiss_locked(Job **job, Error **errp); ++ ++/** ++ * Synchronously finishes the given @job. If @finish is given, it is called to ++ * trigger completion or cancellation of the job. ++ * ++ * Returns 0 if the job is successfully completed, -ECANCELED if the job was ++ * cancelled before completing, and -errno in other error cases. ++ * ++ * Called with job_lock held, but might release it temporarily. ++ */ ++int job_finish_sync_locked(Job *job, void (*finish)(Job *, Error **errp), ++ Error **errp); ++ ++/** ++ * Sets the @job->aio_context. ++ * Called with job_mutex *not* held. ++ * ++ * This function must run in the main thread to protect against ++ * concurrent read in job_finish_sync_locked(), takes the job_mutex ++ * lock to protect against the read in job_do_yield_locked(), and must ++ * be called when the job is quiescent. ++ */ ++void job_set_aio_context(Job *job, AioContext *ctx); ++ ++#endif +diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h +new file mode 100644 +index 00000000..66713bd4 +--- /dev/null ++++ b/include/qemu/lockable.h +@@ -0,0 +1,184 @@ ++/* ++ * Polymorphic locking functions (aka poor man templates) ++ * ++ * Copyright Red Hat, Inc. 2017, 2018 ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_LOCKABLE_H ++#define QEMU_LOCKABLE_H ++ ++#include "qemu/coroutine-core.h" ++#include "qemu/thread.h" ++ ++typedef void QemuLockUnlockFunc(void *); ++ ++typedef struct QemuLockable { ++ void *object; ++ QemuLockUnlockFunc *lock; ++ QemuLockUnlockFunc *unlock; ++} QemuLockable; ++ ++static inline __attribute__((__always_inline__)) QemuLockable * ++qemu_make_lockable(void *x, QemuLockable *lockable) ++{ ++ /* ++ * We cannot test this in a macro, otherwise we get compiler ++ * warnings like "the address of 'm' will always evaluate as 'true'". ++ */ ++ return x ? lockable : NULL; ++} ++ ++static inline __attribute__((__always_inline__)) QemuLockable * ++qemu_null_lockable(void *x) ++{ ++ if (x != NULL) { ++ qemu_build_not_reached(); ++ } ++ return NULL; ++} ++ ++#define QML_FUNC_(name) \ ++ static inline void qemu_lockable_ ## name ## _lock(void *x) \ ++ { \ ++ qemu_ ## name ## _lock(x); \ ++ } \ ++ static inline void qemu_lockable_ ## name ## _unlock(void *x) \ ++ { \ ++ qemu_ ## name ## _unlock(x); \ ++ } ++ ++QML_FUNC_(mutex) ++QML_FUNC_(rec_mutex) ++QML_FUNC_(co_mutex) ++QML_FUNC_(spin) ++ ++/* ++ * In C, compound literals have the lifetime of an automatic variable. ++ * In C++ it would be different, but then C++ wouldn't need QemuLockable ++ * either... ++ */ ++#define QML_OBJ_(x, name) (&(QemuLockable) { \ ++ .object = (x), \ ++ .lock = qemu_lockable_ ## name ## _lock, \ ++ .unlock = qemu_lockable_ ## name ## _unlock \ ++ }) ++ ++/** ++ * QEMU_MAKE_LOCKABLE - Make a polymorphic QemuLockable ++ * ++ * @x: a lock object (currently one of QemuMutex, QemuRecMutex, ++ * CoMutex, QemuSpin). ++ * ++ * Returns a QemuLockable object that can be passed around ++ * to a function that can operate with locks of any kind, or ++ * NULL if @x is %NULL. ++ * ++ * Note the special case for void *, so that we may pass "NULL". ++ */ ++#define QEMU_MAKE_LOCKABLE(x) \ ++ _Generic((x), QemuLockable *: (x), \ ++ void *: qemu_null_lockable(x), \ ++ QemuMutex *: qemu_make_lockable(x, QML_OBJ_(x, mutex)), \ ++ QemuRecMutex *: qemu_make_lockable(x, QML_OBJ_(x, rec_mutex)), \ ++ CoMutex *: qemu_make_lockable(x, QML_OBJ_(x, co_mutex)), \ ++ QemuSpin *: qemu_make_lockable(x, QML_OBJ_(x, spin))) ++ ++/** ++ * QEMU_MAKE_LOCKABLE_NONNULL - Make a polymorphic QemuLockable ++ * ++ * @x: a lock object (currently one of QemuMutex, QemuRecMutex, ++ * CoMutex, QemuSpin). ++ * ++ * Returns a QemuLockable object that can be passed around ++ * to a function that can operate with locks of any kind. ++ */ ++#define QEMU_MAKE_LOCKABLE_NONNULL(x) \ ++ _Generic((x), QemuLockable *: (x), \ ++ QemuMutex *: QML_OBJ_(x, mutex), \ ++ QemuRecMutex *: QML_OBJ_(x, rec_mutex), \ ++ CoMutex *: QML_OBJ_(x, co_mutex), \ ++ QemuSpin *: QML_OBJ_(x, spin)) ++ ++static inline void qemu_lockable_lock(QemuLockable *x) ++{ ++ x->lock(x->object); ++} ++ ++static inline void qemu_lockable_unlock(QemuLockable *x) ++{ ++ x->unlock(x->object); ++} ++ ++static inline QemuLockable *qemu_lockable_auto_lock(QemuLockable *x) ++{ ++ qemu_lockable_lock(x); ++ return x; ++} ++ ++static inline void qemu_lockable_auto_unlock(QemuLockable *x) ++{ ++ if (x) { ++ qemu_lockable_unlock(x); ++ } ++} ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuLockable, qemu_lockable_auto_unlock) ++ ++#define WITH_QEMU_LOCK_GUARD_(x, var) \ ++ for (g_autoptr(QemuLockable) var = \ ++ qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))); \ ++ var; \ ++ qemu_lockable_auto_unlock(var), var = NULL) ++ ++/** ++ * WITH_QEMU_LOCK_GUARD - Lock a lock object for scope ++ * ++ * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). ++ * ++ * This macro defines a lock scope such that entering the scope takes the lock ++ * and leaving the scope releases the lock. Return statements are allowed ++ * within the scope and release the lock. Break and continue statements leave ++ * the scope early and release the lock. ++ * ++ * WITH_QEMU_LOCK_GUARD(&mutex) { ++ * ... ++ * if (error) { ++ * return; <-- mutex is automatically unlocked ++ * } ++ * ++ * if (early_exit) { ++ * break; <-- leave this scope early ++ * } ++ * ... ++ * } ++ */ ++#define WITH_QEMU_LOCK_GUARD(x) \ ++ WITH_QEMU_LOCK_GUARD_((x), glue(qemu_lockable_auto, __COUNTER__)) ++ ++/** ++ * QEMU_LOCK_GUARD - Lock an object until the end of the scope ++ * ++ * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). ++ * ++ * This macro takes a lock until the end of the scope. Return statements ++ * release the lock. ++ * ++ * ... <-- mutex not locked ++ * QEMU_LOCK_GUARD(&mutex); <-- mutex locked from here onwards ++ * ... ++ * if (error) { ++ * return; <-- mutex is automatically unlocked ++ * } ++ */ ++#define QEMU_LOCK_GUARD(x) \ ++ g_autoptr(QemuLockable) \ ++ glue(qemu_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \ ++ qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE((x))) ++ ++#endif +diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h +new file mode 100644 +index 00000000..5764db15 +--- /dev/null ++++ b/include/qemu/main-loop.h +@@ -0,0 +1,411 @@ ++/* ++ * QEMU System Emulator ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_MAIN_LOOP_H ++#define QEMU_MAIN_LOOP_H ++ ++#include "block/aio.h" ++#include "qom/object.h" ++#include "sysemu/event-loop-base.h" ++ ++#define SIG_IPI SIGUSR1 ++ ++#define TYPE_MAIN_LOOP "main-loop" ++OBJECT_DECLARE_TYPE(MainLoop, MainLoopClass, MAIN_LOOP) ++ ++struct MainLoop { ++ EventLoopBase parent_obj; ++}; ++typedef struct MainLoop MainLoop; ++ ++/** ++ * qemu_init_main_loop: Set up the process so that it can run the main loop. ++ * ++ * This includes setting up signal handlers. It should be called before ++ * any other threads are created. In addition, threads other than the ++ * main one should block signals that are trapped by the main loop. ++ * For simplicity, you can consider these signals to be safe: SIGUSR1, ++ * SIGUSR2, thread signals (SIGFPE, SIGILL, SIGSEGV, SIGBUS) and real-time ++ * signals if available. Remember that Windows in practice does not have ++ * signals, though. ++ * ++ * In the case of QEMU tools, this will also start/initialize timers. ++ */ ++int qemu_init_main_loop(Error **errp); ++ ++/** ++ * main_loop_wait: Run one iteration of the main loop. ++ * ++ * If @nonblocking is true, poll for events, otherwise suspend until ++ * one actually occurs. The main loop usually consists of a loop that ++ * repeatedly calls main_loop_wait(false). ++ * ++ * Main loop services include file descriptor callbacks, bottom halves ++ * and timers (defined in qemu/timer.h). Bottom halves are similar to timers ++ * that execute immediately, but have a lower overhead and scheduling them ++ * is wait-free, thread-safe and signal-safe. ++ * ++ * It is sometimes useful to put a whole program in a coroutine. In this ++ * case, the coroutine actually should be started from within the main loop, ++ * so that the main loop can run whenever the coroutine yields. To do this, ++ * you can use a bottom half to enter the coroutine as soon as the main loop ++ * starts: ++ * ++ * void enter_co_bh(void *opaque) { ++ * QEMUCoroutine *co = opaque; ++ * qemu_coroutine_enter(co); ++ * } ++ * ++ * ... ++ * QEMUCoroutine *co = qemu_coroutine_create(coroutine_entry, NULL); ++ * QEMUBH *start_bh = qemu_bh_new(enter_co_bh, co); ++ * qemu_bh_schedule(start_bh); ++ * while (...) { ++ * main_loop_wait(false); ++ * } ++ * ++ * (In the future we may provide a wrapper for this). ++ * ++ * @nonblocking: Whether the caller should block until an event occurs. ++ */ ++void main_loop_wait(int nonblocking); ++ ++/** ++ * qemu_get_aio_context: Return the main loop's AioContext ++ */ ++AioContext *qemu_get_aio_context(void); ++ ++/** ++ * qemu_notify_event: Force processing of pending events. ++ * ++ * Similar to signaling a condition variable, qemu_notify_event forces ++ * main_loop_wait to look at pending events and exit. The caller of ++ * main_loop_wait will usually call it again very soon, so qemu_notify_event ++ * also has the side effect of recalculating the sets of file descriptors ++ * that the main loop waits for. ++ * ++ * Calling qemu_notify_event is rarely necessary, because main loop ++ * services (bottom halves and timers) call it themselves. ++ */ ++void qemu_notify_event(void); ++ ++#ifdef _WIN32 ++/* return TRUE if no sleep should be done afterwards */ ++typedef int PollingFunc(void *opaque); ++ ++/** ++ * qemu_add_polling_cb: Register a Windows-specific polling callback ++ * ++ * Currently, under Windows some events are polled rather than waited for. ++ * Polling callbacks do not ensure that @func is called timely, because ++ * the main loop might wait for an arbitrarily long time. If possible, ++ * you should instead create a separate thread that does a blocking poll ++ * and set a Win32 event object. The event can then be passed to ++ * qemu_add_wait_object. ++ * ++ * Polling callbacks really have nothing Windows specific in them, but ++ * as they are a hack and are currently not necessary under POSIX systems, ++ * they are only available when QEMU is running under Windows. ++ * ++ * @func: The function that does the polling, and returns 1 to force ++ * immediate completion of main_loop_wait. ++ * @opaque: A pointer-size value that is passed to @func. ++ */ ++int qemu_add_polling_cb(PollingFunc *func, void *opaque); ++ ++/** ++ * qemu_del_polling_cb: Unregister a Windows-specific polling callback ++ * ++ * This function removes a callback that was registered with ++ * qemu_add_polling_cb. ++ * ++ * @func: The function that was passed to qemu_add_polling_cb. ++ * @opaque: A pointer-size value that was passed to qemu_add_polling_cb. ++ */ ++void qemu_del_polling_cb(PollingFunc *func, void *opaque); ++ ++/* Wait objects handling */ ++typedef void WaitObjectFunc(void *opaque); ++ ++/** ++ * qemu_add_wait_object: Register a callback for a Windows handle ++ * ++ * Under Windows, the iohandler mechanism can only be used with sockets. ++ * QEMU must use the WaitForMultipleObjects API to wait on other handles. ++ * This function registers a #HANDLE with QEMU, so that it will be included ++ * in the main loop's calls to WaitForMultipleObjects. When the handle ++ * is in a signaled state, QEMU will call @func. ++ * ++ * If the same HANDLE is added twice, this function returns -1. ++ * ++ * @handle: The Windows handle to be observed. ++ * @func: A function to be called when @handle is in a signaled state. ++ * @opaque: A pointer-size value that is passed to @func. ++ */ ++int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); ++ ++/** ++ * qemu_del_wait_object: Unregister a callback for a Windows handle ++ * ++ * This function removes a callback that was registered with ++ * qemu_add_wait_object. ++ * ++ * @func: The function that was passed to qemu_add_wait_object. ++ * @opaque: A pointer-size value that was passed to qemu_add_wait_object. ++ */ ++void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); ++#endif ++ ++/* async I/O support */ ++ ++typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size); ++ ++/** ++ * IOCanReadHandler: Return the number of bytes that #IOReadHandler can accept ++ * ++ * This function reports how many bytes #IOReadHandler is prepared to accept. ++ * #IOReadHandler may be invoked with up to this number of bytes. If this ++ * function returns 0 then #IOReadHandler is not invoked. ++ * ++ * This function is typically called from an event loop. If the number of ++ * bytes changes outside the event loop (e.g. because a vcpu thread drained the ++ * buffer), then it is necessary to kick the event loop so that this function ++ * is called again. aio_notify() or qemu_notify_event() can be used to kick ++ * the event loop. ++ */ ++typedef int IOCanReadHandler(void *opaque); ++ ++/** ++ * qemu_set_fd_handler: Register a file descriptor with the main loop ++ * ++ * This function tells the main loop to wake up whenever one of the ++ * following conditions is true: ++ * ++ * 1) if @fd_write is not %NULL, when the file descriptor is writable; ++ * ++ * 2) if @fd_read is not %NULL, when the file descriptor is readable. ++ * ++ * The callbacks that are set up by qemu_set_fd_handler are level-triggered. ++ * If @fd_read does not read from @fd, or @fd_write does not write to @fd ++ * until its buffers are full, they will be called again on the next ++ * iteration. ++ * ++ * @fd: The file descriptor to be observed. Under Windows it must be ++ * a #SOCKET. ++ * ++ * @fd_read: A level-triggered callback that is fired if @fd is readable ++ * at the beginning of a main loop iteration, or if it becomes readable ++ * during one. ++ * ++ * @fd_write: A level-triggered callback that is fired when @fd is writable ++ * at the beginning of a main loop iteration, or if it becomes writable ++ * during one. ++ * ++ * @opaque: A pointer-sized value that is passed to @fd_read and @fd_write. ++ */ ++void qemu_set_fd_handler(int fd, ++ IOHandler *fd_read, ++ IOHandler *fd_write, ++ void *opaque); ++ ++ ++/** ++ * event_notifier_set_handler: Register an EventNotifier with the main loop ++ * ++ * This function tells the main loop to wake up whenever the ++ * #EventNotifier was set. ++ * ++ * @e: The #EventNotifier to be observed. ++ * ++ * @handler: A level-triggered callback that is fired when @e ++ * has been set. @e is passed to it as a parameter. ++ */ ++void event_notifier_set_handler(EventNotifier *e, ++ EventNotifierHandler *handler); ++ ++GSource *iohandler_get_g_source(void); ++AioContext *iohandler_get_aio_context(void); ++ ++/** ++ * bql_locked: Return lock status of the Big QEMU Lock (BQL) ++ * ++ * The Big QEMU Lock (BQL) is the coarsest lock in QEMU, and as such it ++ * must always be taken outside other locks. This function helps ++ * functions take different paths depending on whether the current ++ * thread is running within the BQL. ++ * ++ * This function should never be used in the block layer, because ++ * unit tests, block layer tools and qemu-storage-daemon do not ++ * have a BQL. ++ * Please instead refer to qemu_in_main_thread(). ++ */ ++bool bql_locked(void); ++ ++/** ++ * qemu_in_main_thread: return whether it's possible to safely access ++ * the global state of the block layer. ++ * ++ * Global state of the block layer is not accessible from I/O threads ++ * or worker threads; only from threads that "own" the default ++ * AioContext that qemu_get_aio_context() returns. For tests, block ++ * layer tools and qemu-storage-daemon there is a designated thread that ++ * runs the event loop for qemu_get_aio_context(), and that is the ++ * main thread. ++ * ++ * For emulators, however, any thread that holds the BQL can act ++ * as the block layer main thread; this will be any of the actual ++ * main thread, the vCPU threads or the RCU thread. ++ * ++ * For clarity, do not use this function outside the block layer. ++ */ ++bool qemu_in_main_thread(void); ++ ++/* ++ * Mark and check that the function is part of the Global State API. ++ * Please refer to include/block/block-global-state.h for more ++ * information about GS API. ++ */ ++#define GLOBAL_STATE_CODE() \ ++ do { \ ++ assert(qemu_in_main_thread()); \ ++ } while (0) ++ ++/* ++ * Mark and check that the function is part of the I/O API. ++ * Please refer to include/block/block-io.h for more ++ * information about IO API. ++ */ ++#define IO_CODE() \ ++ do { \ ++ /* nop */ \ ++ } while (0) ++ ++/* ++ * Mark and check that the function is part of the "I/O OR GS" API. ++ * Please refer to include/block/block-io.h for more ++ * information about "IO or GS" API. ++ */ ++#define IO_OR_GS_CODE() \ ++ do { \ ++ /* nop */ \ ++ } while (0) ++ ++/** ++ * bql_lock: Lock the Big QEMU Lock (BQL). ++ * ++ * This function locks the Big QEMU Lock (BQL). The lock is taken by ++ * main() in vl.c and always taken except while waiting on ++ * external events (such as with select). The lock should be taken ++ * by threads other than the main loop thread when calling ++ * qemu_bh_new(), qemu_set_fd_handler() and basically all other ++ * functions documented in this file. ++ * ++ * NOTE: tools currently are single-threaded and bql_lock ++ * is a no-op there. ++ */ ++#define bql_lock() bql_lock_impl(__FILE__, __LINE__) ++void bql_lock_impl(const char *file, int line); ++ ++/** ++ * bql_unlock: Unlock the Big QEMU Lock (BQL). ++ * ++ * This function unlocks the Big QEMU Lock. The lock is taken by ++ * main() in vl.c and always taken except while waiting on ++ * external events (such as with select). The lock should be unlocked ++ * as soon as possible by threads other than the main loop thread, ++ * because it prevents the main loop from processing callbacks, ++ * including timers and bottom halves. ++ * ++ * NOTE: tools currently are single-threaded and bql_unlock ++ * is a no-op there. ++ */ ++void bql_unlock(void); ++ ++/** ++ * BQL_LOCK_GUARD ++ * ++ * Wrap a block of code in a conditional bql_{lock,unlock}. ++ */ ++typedef struct BQLLockAuto BQLLockAuto; ++ ++static inline BQLLockAuto *bql_auto_lock(const char *file, int line) ++{ ++ if (bql_locked()) { ++ return NULL; ++ } ++ bql_lock_impl(file, line); ++ /* Anything non-NULL causes the cleanup function to be called */ ++ return (BQLLockAuto *)(uintptr_t)1; ++} ++ ++static inline void bql_auto_unlock(BQLLockAuto *l) ++{ ++ bql_unlock(); ++} ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BQLLockAuto, bql_auto_unlock) ++ ++#define BQL_LOCK_GUARD() \ ++ g_autoptr(BQLLockAuto) _bql_lock_auto __attribute__((unused)) \ ++ = bql_auto_lock(__FILE__, __LINE__) ++ ++/* ++ * qemu_cond_wait_bql: Wait on condition for the Big QEMU Lock (BQL) ++ * ++ * This function atomically releases the Big QEMU Lock (BQL) and causes ++ * the calling thread to block on the condition. ++ */ ++void qemu_cond_wait_bql(QemuCond *cond); ++ ++/* ++ * qemu_cond_timedwait_bql: like the previous, but with timeout ++ */ ++void qemu_cond_timedwait_bql(QemuCond *cond, int ms); ++ ++/* internal interfaces */ ++ ++#define qemu_bh_new_guarded(cb, opaque, guard) \ ++ qemu_bh_new_full((cb), (opaque), (stringify(cb)), guard) ++#define qemu_bh_new(cb, opaque) \ ++ qemu_bh_new_full((cb), (opaque), (stringify(cb)), NULL) ++QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name, ++ MemReentrancyGuard *reentrancy_guard); ++void qemu_bh_schedule_idle(QEMUBH *bh); ++ ++enum { ++ MAIN_LOOP_POLL_FILL, ++ MAIN_LOOP_POLL_ERR, ++ MAIN_LOOP_POLL_OK, ++}; ++ ++typedef struct MainLoopPoll { ++ int state; ++ uint32_t timeout; ++ GArray *pollfds; ++} MainLoopPoll; ++ ++void main_loop_poll_add_notifier(Notifier *notify); ++void main_loop_poll_remove_notifier(Notifier *notify); ++ ++#endif +diff --git a/include/qemu/memalign.h b/include/qemu/memalign.h +new file mode 100644 +index 00000000..fa299f3b +--- /dev/null ++++ b/include/qemu/memalign.h +@@ -0,0 +1,61 @@ ++/* ++ * Allocation and free functions for aligned memory ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_MEMALIGN_H ++#define QEMU_MEMALIGN_H ++ ++/** ++ * qemu_try_memalign: Allocate aligned memory ++ * @alignment: required alignment, in bytes ++ * @size: size of allocation, in bytes ++ * ++ * Allocate memory on an aligned boundary (i.e. the returned ++ * address will be an exact multiple of @alignment). ++ * @alignment must be a power of 2, or the function will assert(). ++ * On success, returns allocated memory; on failure, returns NULL. ++ * ++ * The memory allocated through this function must be freed via ++ * qemu_vfree() (and not via free()). ++ */ ++void *qemu_try_memalign(size_t alignment, size_t size); ++/** ++ * qemu_memalign: Allocate aligned memory, without failing ++ * @alignment: required alignment, in bytes ++ * @size: size of allocation, in bytes ++ * ++ * Allocate memory in the same way as qemu_try_memalign(), but ++ * abort() with an error message if the memory allocation fails. ++ * ++ * The memory allocated through this function must be freed via ++ * qemu_vfree() (and not via free()). ++ */ ++void *qemu_memalign(size_t alignment, size_t size); ++/** ++ * qemu_vfree: Free memory allocated through qemu_memalign ++ * @ptr: memory to free ++ * ++ * This function must be used to free memory allocated via qemu_memalign() ++ * or qemu_try_memalign(). (Using the wrong free function will cause ++ * subtle bugs on Windows hosts.) ++ */ ++void qemu_vfree(void *ptr); ++/* ++ * It's an analog of GLIB's g_autoptr_cleanup_generic_gfree(), used to define ++ * g_autofree macro. ++ */ ++static inline void qemu_cleanup_generic_vfree(void *p) ++{ ++ void **pp = (void **)p; ++ qemu_vfree(*pp); ++} ++ ++/* ++ * Analog of g_autofree, but qemu_vfree is called on cleanup instead of g_free. ++ */ ++#define QEMU_AUTO_VFREE __attribute__((cleanup(qemu_cleanup_generic_vfree))) ++ ++#endif +diff --git a/include/qemu/module.h b/include/qemu/module.h +new file mode 100644 +index 00000000..c37ce74b +--- /dev/null ++++ b/include/qemu/module.h +@@ -0,0 +1,192 @@ ++/* ++ * QEMU Module Infrastructure ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_MODULE_H ++#define QEMU_MODULE_H ++ ++ ++#define DSO_STAMP_FUN glue(qemu_stamp, CONFIG_STAMP) ++#define DSO_STAMP_FUN_STR stringify(DSO_STAMP_FUN) ++ ++#ifdef BUILD_DSO ++void DSO_STAMP_FUN(void); ++/* This is a dummy symbol to identify a loaded DSO as a QEMU module, so we can ++ * distinguish "version mismatch" from "not a QEMU module", when the stamp ++ * check fails during module loading */ ++void qemu_module_dummy(void); ++ ++#define module_init(function, type) \ ++static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ ++{ \ ++ register_dso_module_init(function, type); \ ++} ++#else ++/* This should not be used directly. Use block_init etc. instead. */ ++#define module_init(function, type) \ ++static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ ++{ \ ++ register_module_init(function, type); \ ++} ++#endif ++ ++typedef enum { ++ MODULE_INIT_MIGRATION, ++ MODULE_INIT_BLOCK, ++ MODULE_INIT_OPTS, ++ MODULE_INIT_QOM, ++ MODULE_INIT_TRACE, ++ MODULE_INIT_XEN_BACKEND, ++ MODULE_INIT_LIBQOS, ++ MODULE_INIT_FUZZ_TARGET, ++ MODULE_INIT_MAX ++} module_init_type; ++ ++#define block_init(function) module_init(function, MODULE_INIT_BLOCK) ++#define opts_init(function) module_init(function, MODULE_INIT_OPTS) ++#define type_init(function) module_init(function, MODULE_INIT_QOM) ++#define trace_init(function) module_init(function, MODULE_INIT_TRACE) ++#define xen_backend_init(function) module_init(function, \ ++ MODULE_INIT_XEN_BACKEND) ++#define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS) ++#define fuzz_target_init(function) module_init(function, \ ++ MODULE_INIT_FUZZ_TARGET) ++#define migration_init(function) module_init(function, MODULE_INIT_MIGRATION) ++#define block_module_load(lib, errp) module_load("block-", lib, errp) ++#define ui_module_load(lib, errp) module_load("ui-", lib, errp) ++#define audio_module_load(lib, errp) module_load("audio-", lib, errp) ++ ++void register_module_init(void (*fn)(void), module_init_type type); ++void register_dso_module_init(void (*fn)(void), module_init_type type); ++ ++void module_call_init(module_init_type type); ++ ++/* ++ * module_load: attempt to load a module from a set of directories ++ * ++ * directories searched are: ++ * - getenv("QEMU_MODULE_DIR") ++ * - get_relocated_path(CONFIG_QEMU_MODDIR); ++ * - /var/run/qemu/${version_dir} ++ * ++ * prefix: a subsystem prefix, or the empty string ("audio-", ..., "") ++ * name: name of the module ++ * errp: error to set in case the module is found, but load failed. ++ * ++ * Return value: -1 on error (errp set if not NULL). ++ * 0 if module or one of its dependencies are not installed, ++ * 1 if the module is found and loaded, ++ * 2 if the module is already loaded, or module is built-in. ++ */ ++int module_load(const char *prefix, const char *name, Error **errp); ++ ++/* ++ * module_load_qom: attempt to load a module to provide a QOM type ++ * ++ * type: the type to be provided ++ * errp: error to set. ++ * ++ * Return value: as per module_load. ++ */ ++int module_load_qom(const char *type, Error **errp); ++void module_load_qom_all(void); ++void module_allow_arch(const char *arch); ++ ++/** ++ * DOC: module info annotation macros ++ * ++ * ``scripts/modinfo-collect.py`` will collect module info, ++ * using the preprocessor and -DQEMU_MODINFO. ++ * ++ * ``scripts/modinfo-generate.py`` will create a module meta-data database ++ * from the collected information so qemu knows about module ++ * dependencies and QOM objects implemented by modules. ++ * ++ * See ``*.modinfo`` and ``modinfo.c`` in the build directory to check the ++ * script results. ++ */ ++#ifdef QEMU_MODINFO ++# define modinfo(kind, value) \ ++ MODINFO_START kind value MODINFO_END ++#else ++# define modinfo(kind, value) ++#endif ++ ++/** ++ * module_obj ++ * ++ * @name: QOM type. ++ * ++ * This module implements QOM type @name. ++ */ ++#define module_obj(name) modinfo(obj, name) ++ ++/** ++ * module_dep ++ * ++ * @name: module name ++ * ++ * This module depends on module @name. ++ */ ++#define module_dep(name) modinfo(dep, name) ++ ++/** ++ * module_arch ++ * ++ * @name: target architecture ++ * ++ * This module is for target architecture @arch. ++ * ++ * Note that target-dependent modules are tagged automatically, so ++ * this is only needed in case target-independent modules should be ++ * restricted. Use case example: the ccw bus is implemented by s390x ++ * only. ++ */ ++#define module_arch(name) modinfo(arch, name) ++ ++/** ++ * module_opts ++ * ++ * @name: QemuOpts name ++ * ++ * This module registers QemuOpts @name. ++ */ ++#define module_opts(name) modinfo(opts, name) ++ ++/** ++ * module_kconfig ++ * ++ * @name: Kconfig requirement necessary to load the module ++ * ++ * This module requires a core module that should be implemented and ++ * enabled in Kconfig. ++ */ ++#define module_kconfig(name) modinfo(kconfig, name) ++ ++/* ++ * module info database ++ * ++ * scripts/modinfo-generate.c will build this using the data collected ++ * by scripts/modinfo-collect.py ++ */ ++typedef struct QemuModinfo QemuModinfo; ++struct QemuModinfo { ++ const char *name; ++ const char *arch; ++ const char **objs; ++ const char **deps; ++ const char **opts; ++}; ++extern const QemuModinfo qemu_modinfo[]; ++void module_init_info(const QemuModinfo *info); ++ ++#endif +diff --git a/include/qemu/notify.h b/include/qemu/notify.h +new file mode 100644 +index 00000000..abf18dbf +--- /dev/null ++++ b/include/qemu/notify.h +@@ -0,0 +1,78 @@ ++/* ++ * Notifier lists ++ * ++ * Copyright IBM, Corp. 2010 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_NOTIFY_H ++#define QEMU_NOTIFY_H ++ ++#include "qemu/queue.h" ++ ++typedef struct Notifier Notifier; ++ ++struct Notifier ++{ ++ void (*notify)(Notifier *notifier, void *data); ++ QLIST_ENTRY(Notifier) node; ++}; ++ ++typedef struct NotifierList ++{ ++ QLIST_HEAD(, Notifier) notifiers; ++} NotifierList; ++ ++#define NOTIFIER_LIST_INITIALIZER(head) \ ++ { QLIST_HEAD_INITIALIZER((head).notifiers) } ++ ++void notifier_list_init(NotifierList *list); ++ ++void notifier_list_add(NotifierList *list, Notifier *notifier); ++ ++void notifier_remove(Notifier *notifier); ++ ++void notifier_list_notify(NotifierList *list, void *data); ++ ++bool notifier_list_empty(NotifierList *list); ++ ++/* Same as Notifier but allows .notify() to return errors */ ++typedef struct NotifierWithReturn NotifierWithReturn; ++ ++/* Return int to allow for different failure modes and recovery actions */ ++typedef int (*NotifierWithReturnFunc)(NotifierWithReturn *notifier, void *data, ++ Error **errp); ++ ++struct NotifierWithReturn { ++ /** ++ * Return 0 on success (next notifier will be invoked), otherwise ++ * notifier_with_return_list_notify() will stop and return the value. ++ */ ++ NotifierWithReturnFunc notify; ++ QLIST_ENTRY(NotifierWithReturn) node; ++}; ++ ++typedef struct NotifierWithReturnList { ++ QLIST_HEAD(, NotifierWithReturn) notifiers; ++} NotifierWithReturnList; ++ ++#define NOTIFIER_WITH_RETURN_LIST_INITIALIZER(head) \ ++ { QLIST_HEAD_INITIALIZER((head).notifiers) } ++ ++void notifier_with_return_list_init(NotifierWithReturnList *list); ++ ++void notifier_with_return_list_add(NotifierWithReturnList *list, ++ NotifierWithReturn *notifier); ++ ++void notifier_with_return_remove(NotifierWithReturn *notifier); ++ ++int notifier_with_return_list_notify(NotifierWithReturnList *list, ++ void *data, Error **errp); ++ ++#endif +diff --git a/include/qemu/option.h b/include/qemu/option.h +new file mode 100644 +index 00000000..01e673ae +--- /dev/null ++++ b/include/qemu/option.h +@@ -0,0 +1,151 @@ ++/* ++ * Commandline option parsing functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2009 Kevin Wolf ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_OPTION_H ++#define QEMU_OPTION_H ++ ++#include "qemu/queue.h" ++ ++/** ++ * get_opt_value ++ * @p: a pointer to the option name, delimited by commas ++ * @value: a non-NULL pointer that will received the delimited options ++ * ++ * The @value char pointer will be allocated and filled with ++ * the delimited options. ++ * ++ * Returns the position of the comma delimiter/zero byte after the ++ * option name in @p. ++ * The memory pointer in @value must be released with a call to g_free() ++ * when no longer required. ++ */ ++const char *get_opt_value(const char *p, char **value); ++ ++bool parse_option_size(const char *name, const char *value, ++ uint64_t *ret, Error **errp); ++bool has_help_option(const char *param); ++ ++enum QemuOptType { ++ QEMU_OPT_STRING = 0, /* no parsing (use string as-is) */ ++ QEMU_OPT_BOOL, /* on/off */ ++ QEMU_OPT_NUMBER, /* simple number */ ++ QEMU_OPT_SIZE, /* size, accepts (K)ilo, (M)ega, (G)iga, (T)era postfix */ ++}; ++ ++typedef struct QemuOpt QemuOpt; ++ ++typedef struct QemuOptDesc { ++ const char *name; ++ enum QemuOptType type; ++ const char *help; ++ const char *def_value_str; ++} QemuOptDesc; ++ ++struct QemuOptsList { ++ const char *name; ++ const char *implied_opt_name; ++ bool merge_lists; /* Merge multiple uses of option into a single list? */ ++ QTAILQ_HEAD(, QemuOpts) head; ++ QemuOptDesc desc[]; ++}; ++ ++const char *qemu_opt_get(QemuOpts *opts, const char *name); ++char *qemu_opt_get_del(QemuOpts *opts, const char *name); ++/** ++ * qemu_opt_has_help_opt: ++ * @opts: options to search for a help request ++ * ++ * Check whether the options specified by @opts include one of the ++ * standard strings which indicate that the user is asking for a ++ * list of the valid values for a command line option (as defined ++ * by is_help_option()). ++ * ++ * Returns: true if @opts includes 'help' or equivalent. ++ */ ++bool qemu_opt_has_help_opt(QemuOpts *opts); ++QemuOpt *qemu_opt_find(QemuOpts *opts, const char *name); ++bool qemu_opt_get_bool(QemuOpts *opts, const char *name, bool defval); ++uint64_t qemu_opt_get_number(QemuOpts *opts, const char *name, uint64_t defval); ++uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval); ++bool qemu_opt_get_bool_del(QemuOpts *opts, const char *name, bool defval); ++uint64_t qemu_opt_get_number_del(QemuOpts *opts, const char *name, ++ uint64_t defval); ++uint64_t qemu_opt_get_size_del(QemuOpts *opts, const char *name, ++ uint64_t defval); ++int qemu_opt_unset(QemuOpts *opts, const char *name); ++bool qemu_opt_set(QemuOpts *opts, const char *name, const char *value, ++ Error **errp); ++bool qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val, ++ Error **errp); ++bool qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val, ++ Error **errp); ++typedef int (*qemu_opt_loopfunc)(void *opaque, ++ const char *name, const char *value, ++ Error **errp); ++int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque, ++ Error **errp); ++ ++typedef struct { ++ QemuOpts *opts; ++ QemuOpt *opt; ++ const char *name; ++} QemuOptsIter; ++ ++void qemu_opt_iter_init(QemuOptsIter *iter, QemuOpts *opts, const char *name); ++const char *qemu_opt_iter_next(QemuOptsIter *iter); ++ ++QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id); ++QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, ++ int fail_if_exists, Error **errp); ++void qemu_opts_reset(QemuOptsList *list); ++void qemu_opts_loc_restore(QemuOpts *opts); ++const char *qemu_opts_id(QemuOpts *opts); ++void qemu_opts_set_id(QemuOpts *opts, char *id); ++void qemu_opts_del(QemuOpts *opts); ++bool qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp); ++bool qemu_opts_do_parse(QemuOpts *opts, const char *params, ++ const char *firstname, Error **errp); ++QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params, ++ bool permit_abbrev); ++QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, ++ bool permit_abbrev, Error **errp); ++QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict, ++ Error **errp); ++QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict, ++ QemuOptsList *list, bool del); ++QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict); ++bool qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp); ++ ++typedef int (*qemu_opts_loopfunc)(void *opaque, QemuOpts *opts, Error **errp); ++int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, ++ void *opaque, Error **errp); ++void qemu_opts_print(QemuOpts *opts, const char *sep); ++void qemu_opts_print_help(QemuOptsList *list, bool print_caption); ++void qemu_opts_free(QemuOptsList *list); ++QemuOptsList *qemu_opts_append(QemuOptsList *dst, QemuOptsList *list); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuOpts, qemu_opts_del) ++ ++#endif +diff --git a/include/qemu/option_int.h b/include/qemu/option_int.h +new file mode 100644 +index 00000000..5dd9a516 +--- /dev/null ++++ b/include/qemu/option_int.h +@@ -0,0 +1,54 @@ ++/* ++ * Commandline option parsing functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2009 Kevin Wolf ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_OPTION_INT_H ++#define QEMU_OPTION_INT_H ++ ++#include "qemu/option.h" ++#include "qemu/error-report.h" ++ ++struct QemuOpt { ++ char *name; ++ char *str; ++ ++ const QemuOptDesc *desc; ++ union { ++ bool boolean; ++ uint64_t uint; ++ } value; ++ ++ QemuOpts *opts; ++ QTAILQ_ENTRY(QemuOpt) next; ++}; ++ ++struct QemuOpts { ++ char *id; ++ QemuOptsList *list; ++ Location loc; ++ QTAILQ_HEAD(, QemuOpt) head; ++ QTAILQ_ENTRY(QemuOpts) next; ++}; ++ ++#endif +diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h +new file mode 100644 +index 00000000..fe7c3c5f +--- /dev/null ++++ b/include/qemu/osdep.h +@@ -0,0 +1,831 @@ ++/* ++ * OS includes and handling of OS dependencies ++ * ++ * This header exists to pull in some common system headers that ++ * most code in QEMU will want, and to fix up some possible issues with ++ * it (missing defines, Windows weirdness, and so on). ++ * ++ * To avoid getting into possible circular include dependencies, this ++ * file should not include any other QEMU headers, with the exceptions ++ * of config-host.h, config-target.h, qemu/compiler.h, ++ * sysemu/os-posix.h, sysemu/os-win32.h, glib-compat.h and ++ * qemu/typedefs.h, all of which are doing a similar job to this file ++ * and are under similar constraints. ++ * ++ * This header also contains prototypes for functions defined in ++ * os-*.c and util/oslib-*.c; those would probably be better split ++ * out into separate header files. ++ * ++ * In an ideal world this header would contain only: ++ * (1) things which everybody needs ++ * (2) things without which code would work on most platforms but ++ * fail to compile or misbehave on a minority of host OSes ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++#ifndef QEMU_OSDEP_H ++#define QEMU_OSDEP_H ++ ++#if !defined _FORTIFY_SOURCE && defined __OPTIMIZE__ && __OPTIMIZE__ && defined __linux__ ++# define _FORTIFY_SOURCE 2 ++#endif ++ ++#include "config-host.h" ++#ifdef COMPILING_PER_TARGET ++#include CONFIG_TARGET ++#else ++#include "exec/poison.h" ++#endif ++ ++/* ++ * HOST_WORDS_BIGENDIAN was replaced with HOST_BIG_ENDIAN. Prevent it from ++ * creeping back in. ++ */ ++#pragma GCC poison HOST_WORDS_BIGENDIAN ++ ++/* ++ * TARGET_WORDS_BIGENDIAN was replaced with TARGET_BIG_ENDIAN. Prevent it from ++ * creeping back in. ++ */ ++#pragma GCC poison TARGET_WORDS_BIGENDIAN ++ ++#include "qemu/compiler.h" ++ ++/* Older versions of C++ don't get definitions of various macros from ++ * stdlib.h unless we define these macros before first inclusion of ++ * that system header. ++ */ ++#ifndef __STDC_CONSTANT_MACROS ++#define __STDC_CONSTANT_MACROS ++#endif ++#ifndef __STDC_LIMIT_MACROS ++#define __STDC_LIMIT_MACROS ++#endif ++#ifndef __STDC_FORMAT_MACROS ++#define __STDC_FORMAT_MACROS ++#endif ++ ++/* The following block of code temporarily renames the daemon() function so the ++ * compiler does not see the warning associated with it in stdlib.h on OSX ++ */ ++#ifdef __APPLE__ ++#define daemon qemu_fake_daemon_function ++#include ++#undef daemon ++QEMU_EXTERN_C int daemon(int, int); ++#endif ++ ++#ifdef _WIN32 ++/* as defined in sdkddkver.h */ ++#ifndef _WIN32_WINNT ++#define _WIN32_WINNT 0x0602 /* Windows 8 API (should be >= the one from glib) */ ++#endif ++/* reduces the number of implicitly included headers */ ++#ifndef WIN32_LEAN_AND_MEAN ++#define WIN32_LEAN_AND_MEAN ++#endif ++#endif ++ ++/* enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later) */ ++#ifdef __MINGW32__ ++#define __USE_MINGW_ANSI_STDIO 1 ++#endif ++ ++/* ++ * We need the FreeBSD "legacy" definitions. Rust needs the FreeBSD 11 system ++ * calls since it doesn't use libc at all, so we have to emulate that despite ++ * FreeBSD 11 being EOL'd. ++ */ ++#ifdef __FreeBSD__ ++#define _WANT_FREEBSD11_STAT ++#define _WANT_FREEBSD11_STATFS ++#define _WANT_FREEBSD11_DIRENT ++#define _WANT_KERNEL_ERRNO ++#define _WANT_SEMUN ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++/* Put unistd.h before time.h as that triggers localtime_r/gmtime_r ++ * function availability on recentish Mingw-w64 platforms. */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++/* setjmp must be declared before sysemu/os-win32.h ++ * because it is redefined there. */ ++#include ++#include ++ ++#ifdef CONFIG_IOVEC ++#include ++#endif ++ ++#if defined(__linux__) && defined(__sparc__) ++/* The SPARC definition of QEMU_VMALLOC_ALIGN needs SHMLBA */ ++#include ++#endif ++ ++#ifndef _WIN32 ++#include ++#else ++#define WIFEXITED(x) 1 ++#define WEXITSTATUS(x) (x) ++#endif ++ ++#ifdef __APPLE__ ++#include ++#endif ++ ++/* ++ * This is somewhat like a system header; it must be outside any extern "C" ++ * block because it includes system headers itself, including glib.h, ++ * which will not compile if inside an extern "C" block. ++ */ ++#include "glib-compat.h" ++ ++#ifdef _WIN32 ++#include "sysemu/os-win32.h" ++#endif ++ ++#ifdef CONFIG_POSIX ++#include "sysemu/os-posix.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "qemu/typedefs.h" ++ ++/** ++ * Mark a function that executes in coroutine context ++ * ++ * Functions that execute in coroutine context cannot be called directly from ++ * normal functions. In the future it would be nice to enable compiler or ++ * static checker support for catching such errors. This annotation might make ++ * it possible and in the meantime it serves as documentation. ++ * ++ * For example: ++ * ++ * static void coroutine_fn foo(void) { ++ * .... ++ * } ++ */ ++#ifdef __clang__ ++#define coroutine_fn QEMU_ANNOTATE("coroutine_fn") ++#else ++#define coroutine_fn ++#endif ++ ++/** ++ * Mark a function that can suspend when executed in coroutine context, ++ * but can handle running in non-coroutine context too. ++ */ ++#ifdef __clang__ ++#define coroutine_mixed_fn QEMU_ANNOTATE("coroutine_mixed_fn") ++#else ++#define coroutine_mixed_fn ++#endif ++ ++/** ++ * Mark a function that should not be called from a coroutine context. ++ * Usually there will be an analogous, coroutine_fn function that should ++ * be used instead. ++ * ++ * When the function is also marked as coroutine_mixed_fn, the function should ++ * only be called if the caller does not know whether it is in coroutine ++ * context. ++ * ++ * Functions that are only no_coroutine_fn, on the other hand, should not ++ * be called from within coroutines at all. This for example includes ++ * functions that block. ++ * ++ * In the future it would be nice to enable compiler or static checker ++ * support for catching such errors. This annotation is the first step ++ * towards this, and in the meantime it serves as documentation. ++ * ++ * For example: ++ * ++ * static void no_coroutine_fn foo(void) { ++ * .... ++ * } ++ */ ++#ifdef __clang__ ++#define no_coroutine_fn QEMU_ANNOTATE("no_coroutine_fn") ++#else ++#define no_coroutine_fn ++#endif ++ ++ ++/* ++ * For mingw, as of v6.0.0, the function implementing the assert macro is ++ * not marked as noreturn, so the compiler cannot delete code following an ++ * assert(false) as unused. We rely on this within the code base to delete ++ * code that is unreachable when features are disabled. ++ * All supported versions of Glib's g_assert() satisfy this requirement. ++ */ ++#ifdef __MINGW32__ ++#undef assert ++#define assert(x) g_assert(x) ++#endif ++ ++/** ++ * qemu_build_not_reached() ++ * ++ * The compiler, during optimization, is expected to prove that a call ++ * to this function cannot be reached and remove it. If the compiler ++ * supports QEMU_ERROR, this will be reported at compile time; otherwise ++ * this will be reported at link time due to the missing symbol. ++ */ ++G_NORETURN ++void QEMU_ERROR("code path is reachable") ++ qemu_build_not_reached_always(void); ++#if defined(__OPTIMIZE__) && !defined(__NO_INLINE__) ++#define qemu_build_not_reached() qemu_build_not_reached_always() ++#else ++#define qemu_build_not_reached() g_assert_not_reached() ++#endif ++ ++/** ++ * qemu_build_assert() ++ * ++ * The compiler, during optimization, is expected to prove that the ++ * assertion is true. ++ */ ++#define qemu_build_assert(test) while (!(test)) qemu_build_not_reached() ++ ++/* ++ * According to waitpid man page: ++ * WCOREDUMP ++ * This macro is not specified in POSIX.1-2001 and is not ++ * available on some UNIX implementations (e.g., AIX, SunOS). ++ * Therefore, enclose its use inside #ifdef WCOREDUMP ... #endif. ++ */ ++#ifndef WCOREDUMP ++#define WCOREDUMP(status) 0 ++#endif ++/* ++ * We have a lot of unaudited code that may fail in strange ways, or ++ * even be a security risk during migration, if you disable assertions ++ * at compile-time. You may comment out these safety checks if you ++ * absolutely want to disable assertion overhead, but it is not ++ * supported upstream so the risk is all yours. Meanwhile, please ++ * submit patches to remove any side-effects inside an assertion, or ++ * fixing error handling that should use Error instead of assert. ++ */ ++#ifdef NDEBUG ++#error building with NDEBUG is not supported ++#endif ++#ifdef G_DISABLE_ASSERT ++#error building with G_DISABLE_ASSERT is not supported ++#endif ++ ++#ifndef O_LARGEFILE ++#define O_LARGEFILE 0 ++#endif ++#ifndef O_BINARY ++#define O_BINARY 0 ++#endif ++#ifndef MAP_ANONYMOUS ++#define MAP_ANONYMOUS MAP_ANON ++#endif ++#ifndef MAP_NORESERVE ++#define MAP_NORESERVE 0 ++#endif ++#ifndef ENOMEDIUM ++#define ENOMEDIUM ENODEV ++#endif ++#if !defined(ENOTSUP) ++#define ENOTSUP 4096 ++#endif ++#if !defined(ECANCELED) ++#define ECANCELED 4097 ++#endif ++#if !defined(EMEDIUMTYPE) ++#define EMEDIUMTYPE 4098 ++#endif ++#if !defined(ESHUTDOWN) ++#define ESHUTDOWN 4099 ++#endif ++ ++#define RETRY_ON_EINTR(expr) \ ++ (__extension__ \ ++ ({ typeof(expr) __result; \ ++ do { \ ++ __result = (expr); \ ++ } while (__result == -1 && errno == EINTR); \ ++ __result; })) ++ ++/* time_t may be either 32 or 64 bits depending on the host OS, and ++ * can be either signed or unsigned, so we can't just hardcode a ++ * specific maximum value. This is not a C preprocessor constant, ++ * so you can't use TIME_MAX in an #ifdef, but for our purposes ++ * this isn't a problem. ++ */ ++ ++/* The macros TYPE_SIGNED, TYPE_WIDTH, and TYPE_MAXIMUM are from ++ * Gnulib, and are under the LGPL v2.1 or (at your option) any ++ * later version. ++ */ ++ ++/* True if the real type T is signed. */ ++#define TYPE_SIGNED(t) (!((t)0 < (t)-1)) ++ ++/* The width in bits of the integer type or expression T. ++ * Padding bits are not supported. ++ */ ++#define TYPE_WIDTH(t) (sizeof(t) * CHAR_BIT) ++ ++/* The maximum and minimum values for the integer type T. */ ++#define TYPE_MAXIMUM(t) \ ++ ((t) (!TYPE_SIGNED(t) \ ++ ? (t)-1 \ ++ : ((((t)1 << (TYPE_WIDTH(t) - 2)) - 1) * 2 + 1))) ++ ++#ifndef TIME_MAX ++#define TIME_MAX TYPE_MAXIMUM(time_t) ++#endif ++ ++/* Mac OSX has a bug that incorrectly defines SIZE_MAX with ++ * the wrong type. Our replacement isn't usable in preprocessor ++ * expressions, but it is sufficient for our needs. */ ++#ifdef HAVE_BROKEN_SIZE_MAX ++#undef SIZE_MAX ++#define SIZE_MAX ((size_t)-1) ++#endif ++ ++/* ++ * Two variations of MIN/MAX macros. The first is for runtime use, and ++ * evaluates arguments only once (so it is safe even with side ++ * effects), but will not work in constant contexts (such as array ++ * size declarations) because of the '{}'. The second is for constant ++ * expression use, where evaluating arguments twice is safe because ++ * the result is going to be constant anyway, but will not work in a ++ * runtime context because of a void expression where a value is ++ * expected. Thus, both gcc and clang will fail to compile if you use ++ * the wrong macro (even if the error may seem a bit cryptic). ++ * ++ * Note that neither form is usable as an #if condition; if you truly ++ * need to write conditional code that depends on a minimum or maximum ++ * determined by the pre-processor instead of the compiler, you'll ++ * have to open-code it. Sadly, Coverity is severely confused by the ++ * constant variants, so we have to dumb things down there. ++ * ++ * Preprocessor sorcery ahead: use different identifiers for the local ++ * variables in each expansion, so we can nest macro calls without ++ * shadowing variables. ++ */ ++#define MIN_INTERNAL(a, b, _a, _b) \ ++ ({ \ ++ typeof(1 ? (a) : (b)) _a = (a), _b = (b); \ ++ _a < _b ? _a : _b; \ ++ }) ++#undef MIN ++#define MIN(a, b) \ ++ MIN_INTERNAL((a), (b), MAKE_IDENTIFIER(_a), MAKE_IDENTIFIER(_b)) ++ ++#define MAX_INTERNAL(a, b, _a, _b) \ ++ ({ \ ++ typeof(1 ? (a) : (b)) _a = (a), _b = (b); \ ++ _a > _b ? _a : _b; \ ++ }) ++#undef MAX ++#define MAX(a, b) \ ++ MAX_INTERNAL((a), (b), MAKE_IDENTIFIER(_a), MAKE_IDENTIFIER(_b)) ++ ++#ifdef __COVERITY__ ++# define MIN_CONST(a, b) ((a) < (b) ? (a) : (b)) ++# define MAX_CONST(a, b) ((a) > (b) ? (a) : (b)) ++#else ++# define MIN_CONST(a, b) \ ++ __builtin_choose_expr( \ ++ __builtin_constant_p(a) && __builtin_constant_p(b), \ ++ (a) < (b) ? (a) : (b), \ ++ ((void)0)) ++# define MAX_CONST(a, b) \ ++ __builtin_choose_expr( \ ++ __builtin_constant_p(a) && __builtin_constant_p(b), \ ++ (a) > (b) ? (a) : (b), \ ++ ((void)0)) ++#endif ++ ++/* ++ * Minimum function that returns zero only if both values are zero. ++ * Intended for use with unsigned values only. ++ * ++ * Preprocessor sorcery ahead: use different identifiers for the local ++ * variables in each expansion, so we can nest macro calls without ++ * shadowing variables. ++ */ ++#define MIN_NON_ZERO_INTERNAL(a, b, _a, _b) \ ++ ({ \ ++ typeof(1 ? (a) : (b)) _a = (a), _b = (b); \ ++ _a == 0 ? _b : (_b == 0 || _b > _a) ? _a : _b; \ ++ }) ++#define MIN_NON_ZERO(a, b) \ ++ MIN_NON_ZERO_INTERNAL((a), (b), MAKE_IDENTIFIER(_a), MAKE_IDENTIFIER(_b)) ++ ++/* ++ * Round number down to multiple. Safe when m is not a power of 2 (see ++ * ROUND_DOWN for a faster version when a power of 2 is guaranteed). ++ */ ++#define QEMU_ALIGN_DOWN(n, m) ((n) / (m) * (m)) ++ ++/* ++ * Round number up to multiple. Safe when m is not a power of 2 (see ++ * ROUND_UP for a faster version when a power of 2 is guaranteed). ++ */ ++#define QEMU_ALIGN_UP(n, m) QEMU_ALIGN_DOWN((n) + (m) - 1, (m)) ++ ++/* Check if n is a multiple of m */ ++#define QEMU_IS_ALIGNED(n, m) (((n) % (m)) == 0) ++ ++/* n-byte align pointer down */ ++#define QEMU_ALIGN_PTR_DOWN(p, n) \ ++ ((typeof(p))QEMU_ALIGN_DOWN((uintptr_t)(p), (n))) ++ ++/* n-byte align pointer up */ ++#define QEMU_ALIGN_PTR_UP(p, n) \ ++ ((typeof(p))QEMU_ALIGN_UP((uintptr_t)(p), (n))) ++ ++/* Check if pointer p is n-bytes aligned */ ++#define QEMU_PTR_IS_ALIGNED(p, n) QEMU_IS_ALIGNED((uintptr_t)(p), (n)) ++ ++/* ++ * Round number down to multiple. Requires that d be a power of 2 (see ++ * QEMU_ALIGN_UP for a safer but slower version on arbitrary ++ * numbers); works even if d is a smaller type than n. ++ */ ++#ifndef ROUND_DOWN ++#define ROUND_DOWN(n, d) ((n) & -(0 ? (n) : (d))) ++#endif ++ ++/* ++ * Round number up to multiple. Requires that d be a power of 2 (see ++ * QEMU_ALIGN_UP for a safer but slower version on arbitrary ++ * numbers); works even if d is a smaller type than n. ++ */ ++#ifndef ROUND_UP ++#define ROUND_UP(n, d) ROUND_DOWN((n) + (d) - 1, (d)) ++#endif ++ ++#ifndef DIV_ROUND_UP ++#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) ++#endif ++ ++/* ++ * &(x)[0] is always a pointer - if it's same type as x then the argument is a ++ * pointer, not an array. ++ */ ++#define QEMU_IS_ARRAY(x) (!__builtin_types_compatible_p(typeof(x), \ ++ typeof(&(x)[0]))) ++#ifndef ARRAY_SIZE ++#define ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0])) + \ ++ QEMU_BUILD_BUG_ON_ZERO(!QEMU_IS_ARRAY(x))) ++#endif ++ ++int qemu_daemon(int nochdir, int noclose); ++void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared, ++ bool noreserve); ++void qemu_anon_ram_free(void *ptr, size_t size); ++ ++#ifdef _WIN32 ++#define HAVE_CHARDEV_SERIAL 1 ++#define HAVE_CHARDEV_PARALLEL 1 ++#else ++#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ ++ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ ++ || defined(__GLIBC__) || defined(__APPLE__) ++#define HAVE_CHARDEV_SERIAL 1 ++#endif ++#if defined(__linux__) || defined(__FreeBSD__) \ ++ || defined(__FreeBSD_kernel__) || defined(__DragonFly__) ++#define HAVE_CHARDEV_PARALLEL 1 ++#endif ++#endif ++ ++#if defined(__HAIKU__) ++#define SIGIO SIGPOLL ++#endif ++ ++#ifdef HAVE_MADVISE_WITHOUT_PROTOTYPE ++/* ++ * See MySQL bug #7156 (http://bugs.mysql.com/bug.php?id=7156) for discussion ++ * about Solaris missing the madvise() prototype. ++ */ ++int madvise(char *, size_t, int); ++#endif ++ ++#if defined(CONFIG_LINUX) ++#ifndef BUS_MCEERR_AR ++#define BUS_MCEERR_AR 4 ++#endif ++#ifndef BUS_MCEERR_AO ++#define BUS_MCEERR_AO 5 ++#endif ++#endif ++ ++#if defined(__linux__) && \ ++ (defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) \ ++ || defined(__powerpc64__)) ++ /* Use 2 MiB alignment so transparent hugepages can be used by KVM. ++ Valgrind does not support alignments larger than 1 MiB, ++ therefore we need special code which handles running on Valgrind. */ ++# define QEMU_VMALLOC_ALIGN (512 * 4096) ++#elif defined(__linux__) && defined(__s390x__) ++ /* Use 1 MiB (segment size) alignment so gmap can be used by KVM. */ ++# define QEMU_VMALLOC_ALIGN (256 * 4096) ++#elif defined(__linux__) && defined(__sparc__) ++# define QEMU_VMALLOC_ALIGN MAX(qemu_real_host_page_size(), SHMLBA) ++#elif defined(__linux__) && defined(__loongarch__) ++ /* ++ * For transparent hugepage optimization, it has better be huge page ++ * aligned. LoongArch host system supports two kinds of pagesize: 4K ++ * and 16K, here calculate huge page size from host page size ++ */ ++# define QEMU_VMALLOC_ALIGN (qemu_real_host_page_size() * \ ++ qemu_real_host_page_size() / sizeof(long)) ++#else ++# define QEMU_VMALLOC_ALIGN qemu_real_host_page_size() ++#endif ++ ++#ifdef CONFIG_POSIX ++struct qemu_signalfd_siginfo { ++ uint32_t ssi_signo; /* Signal number */ ++ int32_t ssi_errno; /* Error number (unused) */ ++ int32_t ssi_code; /* Signal code */ ++ uint32_t ssi_pid; /* PID of sender */ ++ uint32_t ssi_uid; /* Real UID of sender */ ++ int32_t ssi_fd; /* File descriptor (SIGIO) */ ++ uint32_t ssi_tid; /* Kernel timer ID (POSIX timers) */ ++ uint32_t ssi_band; /* Band event (SIGIO) */ ++ uint32_t ssi_overrun; /* POSIX timer overrun count */ ++ uint32_t ssi_trapno; /* Trap number that caused signal */ ++ int32_t ssi_status; /* Exit status or signal (SIGCHLD) */ ++ int32_t ssi_int; /* Integer sent by sigqueue(2) */ ++ uint64_t ssi_ptr; /* Pointer sent by sigqueue(2) */ ++ uint64_t ssi_utime; /* User CPU time consumed (SIGCHLD) */ ++ uint64_t ssi_stime; /* System CPU time consumed (SIGCHLD) */ ++ uint64_t ssi_addr; /* Address that generated signal ++ (for hardware-generated signals) */ ++ uint8_t pad[48]; /* Pad size to 128 bytes (allow for ++ additional fields in the future) */ ++}; ++ ++int qemu_signalfd(const sigset_t *mask); ++void sigaction_invoke(struct sigaction *action, ++ struct qemu_signalfd_siginfo *info); ++#endif ++ ++/* ++ * Don't introduce new usage of this function, prefer the following ++ * qemu_open/qemu_create that take an "Error **errp" ++ */ ++int qemu_open_old(const char *name, int flags, ...); ++int qemu_open(const char *name, int flags, Error **errp); ++int qemu_create(const char *name, int flags, mode_t mode, Error **errp); ++int qemu_close(int fd); ++int qemu_unlink(const char *name); ++#ifndef _WIN32 ++int qemu_dup_flags(int fd, int flags); ++int qemu_dup(int fd); ++int qemu_lock_fd(int fd, int64_t start, int64_t len, bool exclusive); ++int qemu_unlock_fd(int fd, int64_t start, int64_t len); ++int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive); ++bool qemu_has_ofd_lock(void); ++#endif ++ ++bool qemu_has_direct_io(void); ++ ++#if defined(__HAIKU__) && defined(__i386__) ++#define FMT_pid "%ld" ++#elif defined(WIN64) ++#define FMT_pid "%" PRId64 ++#else ++#define FMT_pid "%d" ++#endif ++ ++bool qemu_write_pidfile(const char *pidfile, Error **errp); ++ ++int qemu_get_thread_id(void); ++ ++#ifndef CONFIG_IOVEC ++struct iovec { ++ void *iov_base; ++ size_t iov_len; ++}; ++/* ++ * Use the same value as Linux for now. ++ */ ++#define IOV_MAX 1024 ++ ++ssize_t readv(int fd, const struct iovec *iov, int iov_cnt); ++ssize_t writev(int fd, const struct iovec *iov, int iov_cnt); ++#endif ++ ++#ifdef _WIN32 ++static inline void qemu_timersub(const struct timeval *val1, ++ const struct timeval *val2, ++ struct timeval *res) ++{ ++ res->tv_sec = val1->tv_sec - val2->tv_sec; ++ if (val1->tv_usec < val2->tv_usec) { ++ res->tv_sec--; ++ res->tv_usec = val1->tv_usec - val2->tv_usec + 1000 * 1000; ++ } else { ++ res->tv_usec = val1->tv_usec - val2->tv_usec; ++ } ++} ++#else ++#define qemu_timersub timersub ++#endif ++ ++ssize_t qemu_write_full(int fd, const void *buf, size_t count) ++ G_GNUC_WARN_UNUSED_RESULT; ++ ++void qemu_set_cloexec(int fd); ++ ++/* Return a dynamically allocated directory path that is appropriate for storing ++ * local state. ++ * ++ * The caller is responsible for releasing the value returned with g_free() ++ * after use. ++ */ ++char *qemu_get_local_state_dir(void); ++ ++/** ++ * qemu_getauxval: ++ * @type: the auxiliary vector key to lookup ++ * ++ * Search the auxiliary vector for @type, returning the value ++ * or 0 if @type is not present. ++ */ ++unsigned long qemu_getauxval(unsigned long type); ++ ++void qemu_set_tty_echo(int fd, bool echo); ++ ++typedef struct ThreadContext ThreadContext; ++ ++/** ++ * qemu_prealloc_mem: ++ * @fd: the fd mapped into the area, -1 for anonymous memory ++ * @area: start address of the are to preallocate ++ * @sz: the size of the area to preallocate ++ * @max_threads: maximum number of threads to use ++ * @tc: prealloc context threads pointer, NULL if not in use ++ * @async: request asynchronous preallocation, requires @tc ++ * @errp: returns an error if this function fails ++ * ++ * Preallocate memory (populate/prefault page tables writable) for the virtual ++ * memory area starting at @area with the size of @sz. After a successful call, ++ * each page in the area was faulted in writable at least once, for example, ++ * after allocating file blocks for mapped files. ++ * ++ * When setting @async, allocation might be performed asynchronously. ++ * qemu_finish_async_prealloc_mem() must be called to finish any asynchronous ++ * preallocation. ++ * ++ * Return: true on success, else false setting @errp with error. ++ */ ++bool qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, ++ ThreadContext *tc, bool async, Error **errp); ++ ++/** ++ * qemu_finish_async_prealloc_mem: ++ * @errp: returns an error if this function fails ++ * ++ * Finish all outstanding asynchronous memory preallocation. ++ * ++ * Return: true on success, else false setting @errp with error. ++ */ ++bool qemu_finish_async_prealloc_mem(Error **errp); ++ ++/** ++ * qemu_get_pid_name: ++ * @pid: pid of a process ++ * ++ * For given @pid fetch its name. Caller is responsible for ++ * freeing the string when no longer needed. ++ * Returns allocated string on success, NULL on failure. ++ */ ++char *qemu_get_pid_name(pid_t pid); ++ ++/* Using intptr_t ensures that qemu_*_page_mask is sign-extended even ++ * when intptr_t is 32-bit and we are aligning a long long. ++ */ ++static inline uintptr_t qemu_real_host_page_size(void) ++{ ++ return getpagesize(); ++} ++ ++static inline intptr_t qemu_real_host_page_mask(void) ++{ ++ return -(intptr_t)qemu_real_host_page_size(); ++} ++ ++/* ++ * After using getopt or getopt_long, if you need to parse another set ++ * of options, then you must reset optind. Unfortunately the way to ++ * do this varies between implementations of getopt. ++ */ ++static inline void qemu_reset_optind(void) ++{ ++#ifdef HAVE_OPTRESET ++ optind = 1; ++ optreset = 1; ++#else ++ optind = 0; ++#endif ++} ++ ++int qemu_fdatasync(int fd); ++ ++/** ++ * qemu_close_all_open_fd: ++ * ++ * Close all open file descriptors except the ones supplied in the @skip array ++ * ++ * @skip: ordered array of distinct file descriptors that should not be closed ++ * if any, or NULL. ++ * @nskip: number of entries in the @skip array or 0 if @skip is NULL. ++ */ ++void qemu_close_all_open_fd(const int *skip, unsigned int nskip); ++ ++/** ++ * Sync changes made to the memory mapped file back to the backing ++ * storage. For POSIX compliant systems this will fallback ++ * to regular msync call. Otherwise it will trigger whole file sync ++ * (including the metadata case there is no support to skip that otherwise) ++ * ++ * @addr - start of the memory area to be synced ++ * @length - length of the are to be synced ++ * @fd - file descriptor for the file to be synced ++ * (mandatory only for POSIX non-compliant systems) ++ */ ++int qemu_msync(void *addr, size_t length, int fd); ++ ++/** ++ * qemu_get_host_physmem: ++ * ++ * Operating system agnostic way of querying host memory. ++ * ++ * Returns amount of physical memory on the system. This is purely ++ * advisery and may return 0 if we can't work it out. At the other ++ * end we saturate to SIZE_MAX if you are lucky enough to have that ++ * much memory. ++ */ ++size_t qemu_get_host_physmem(void); ++ ++/* ++ * Toggle write/execute on the pages marked MAP_JIT ++ * for the current thread. ++ */ ++#ifdef __APPLE__ ++static inline void qemu_thread_jit_execute(void) ++{ ++ pthread_jit_write_protect_np(true); ++} ++ ++static inline void qemu_thread_jit_write(void) ++{ ++ pthread_jit_write_protect_np(false); ++} ++#else ++static inline void qemu_thread_jit_write(void) {} ++static inline void qemu_thread_jit_execute(void) {} ++#endif ++ ++/** ++ * Platforms which do not support system() return ENOSYS ++ */ ++#ifndef HAVE_SYSTEM_FUNCTION ++#define system platform_does_not_support_system ++static inline int platform_does_not_support_system(const char *command) ++{ ++ errno = ENOSYS; ++ return -1; ++} ++#endif /* !HAVE_SYSTEM_FUNCTION */ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/include/qemu/processor.h b/include/qemu/processor.h +new file mode 100644 +index 00000000..9f0dcdf2 +--- /dev/null ++++ b/include/qemu/processor.h +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (C) 2016, Emilio G. Cota ++ * ++ * License: GNU GPL, version 2. ++ * See the COPYING file in the top-level directory. ++ */ ++#ifndef QEMU_PROCESSOR_H ++#define QEMU_PROCESSOR_H ++ ++#if defined(__i386__) || defined(__x86_64__) ++# define cpu_relax() asm volatile("rep; nop" ::: "memory") ++ ++#elif defined(__aarch64__) ++# define cpu_relax() asm volatile("yield" ::: "memory") ++ ++#elif defined(__powerpc64__) ++/* set Hardware Multi-Threading (HMT) priority to low; then back to medium */ ++# define cpu_relax() asm volatile("or 1, 1, 1;" \ ++ "or 2, 2, 2;" ::: "memory") ++ ++#else ++# define cpu_relax() barrier() ++#endif ++ ++#endif /* QEMU_PROCESSOR_H */ +diff --git a/include/qemu/progress_meter.h b/include/qemu/progress_meter.h +new file mode 100644 +index 00000000..0f2c0a32 +--- /dev/null ++++ b/include/qemu/progress_meter.h +@@ -0,0 +1,62 @@ ++/* ++ * Helper functionality for some process progress tracking. ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012, 2018 Red Hat, Inc. ++ * Copyright (c) 2020 Virtuozzo International GmbH ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_PROGRESS_METER_H ++#define QEMU_PROGRESS_METER_H ++ ++#include "qemu/thread.h" ++ ++typedef struct ProgressMeter { ++ /** ++ * Current progress. The unit is arbitrary as long as the ratio between ++ * current and total represents the estimated percentage ++ * of work already done. ++ */ ++ uint64_t current; ++ ++ /** Estimated current value at the completion of the process */ ++ uint64_t total; ++ ++ QemuMutex lock; /* protects concurrent access to above fields */ ++} ProgressMeter; ++ ++void progress_init(ProgressMeter *pm); ++void progress_destroy(ProgressMeter *pm); ++ ++/* Get a snapshot of internal current and total values */ ++void progress_get_snapshot(ProgressMeter *pm, uint64_t *current, ++ uint64_t *total); ++ ++/* Increases the amount of work done so far by @done */ ++void progress_work_done(ProgressMeter *pm, uint64_t done); ++ ++/* Sets how much work has to be done to complete to @remaining */ ++void progress_set_remaining(ProgressMeter *pm, uint64_t remaining); ++ ++/* Increases the total work to do by @delta */ ++void progress_increase_remaining(ProgressMeter *pm, uint64_t delta); ++ ++#endif /* QEMU_PROGRESS_METER_H */ +diff --git a/include/qemu/qdist.h b/include/qemu/qdist.h +new file mode 100644 +index 00000000..bfb32115 +--- /dev/null ++++ b/include/qemu/qdist.h +@@ -0,0 +1,61 @@ ++/* ++ * Copyright (C) 2016, Emilio G. Cota ++ * ++ * License: GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++#ifndef QEMU_QDIST_H ++#define QEMU_QDIST_H ++ ++#include "qemu/bitops.h" ++ ++/* ++ * Samples with the same 'x value' end up in the same qdist_entry, ++ * e.g. inc(0.1) and inc(0.1) end up as {x=0.1, count=2}. ++ * ++ * Binning happens only at print time, so that we retain the flexibility to ++ * choose the binning. This might not be ideal for workloads that do not care ++ * much about precision and insert many samples all with different x values; ++ * in that case, pre-binning (e.g. entering both 0.115 and 0.097 as 0.1) ++ * should be considered. ++ */ ++struct qdist_entry { ++ double x; ++ unsigned long count; ++}; ++ ++struct qdist { ++ struct qdist_entry *entries; ++ size_t n; ++ size_t size; ++}; ++ ++#define QDIST_PR_BORDER BIT(0) ++#define QDIST_PR_LABELS BIT(1) ++/* the remaining options only work if PR_LABELS is set */ ++#define QDIST_PR_NODECIMAL BIT(2) ++#define QDIST_PR_PERCENT BIT(3) ++#define QDIST_PR_100X BIT(4) ++#define QDIST_PR_NOBINRANGE BIT(5) ++ ++void qdist_init(struct qdist *dist); ++void qdist_destroy(struct qdist *dist); ++ ++void qdist_add(struct qdist *dist, double x, long count); ++void qdist_inc(struct qdist *dist, double x); ++double qdist_xmin(const struct qdist *dist); ++double qdist_xmax(const struct qdist *dist); ++double qdist_avg(const struct qdist *dist); ++unsigned long qdist_sample_count(const struct qdist *dist); ++size_t qdist_unique_entries(const struct qdist *dist); ++ ++/* callers must free the returned string with g_free() */ ++char *qdist_pr_plain(const struct qdist *dist, size_t n_groups); ++ ++/* callers must free the returned string with g_free() */ ++char *qdist_pr(const struct qdist *dist, size_t n_groups, uint32_t opt); ++ ++/* Only qdist code and test code should ever call this function */ ++void qdist_bin__internal(struct qdist *to, const struct qdist *from, size_t n); ++ ++#endif /* QEMU_QDIST_H */ +diff --git a/include/qemu/qemu-print.h b/include/qemu/qemu-print.h +new file mode 100644 +index 00000000..1b709206 +--- /dev/null ++++ b/include/qemu/qemu-print.h +@@ -0,0 +1,23 @@ ++/* ++ * Print to stream or current monitor ++ * ++ * Copyright (C) 2019 Red Hat Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_PRINT_H ++#define QEMU_PRINT_H ++ ++int qemu_vprintf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); ++int qemu_printf(const char *fmt, ...) G_GNUC_PRINTF(1, 2); ++ ++int qemu_vfprintf(FILE *stream, const char *fmt, va_list ap) ++ G_GNUC_PRINTF(2, 0); ++int qemu_fprintf(FILE *stream, const char *fmt, ...) G_GNUC_PRINTF(2, 3); ++ ++#endif +diff --git a/include/qemu/qemu-progress.h b/include/qemu/qemu-progress.h +new file mode 100644 +index 00000000..137e1c31 +--- /dev/null ++++ b/include/qemu/qemu-progress.h +@@ -0,0 +1,8 @@ ++#ifndef QEMU_PROGRESS_H ++#define QEMU_PROGRESS_H ++ ++void qemu_progress_init(int enabled, float min_skip); ++void qemu_progress_end(void); ++void qemu_progress_print(float delta, int max); ++ ++#endif /* QEMU_PROGRESS_H */ +diff --git a/include/qemu/qsp.h b/include/qemu/qsp.h +new file mode 100644 +index 00000000..bf36aabf +--- /dev/null ++++ b/include/qemu/qsp.h +@@ -0,0 +1,27 @@ ++/* ++ * qsp.c - QEMU Synchronization Profiler ++ * ++ * Copyright (C) 2018, Emilio G. Cota ++ * ++ * License: GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ * Note: this header file can *only* be included from thread.h. ++ */ ++#ifndef QEMU_QSP_H ++#define QEMU_QSP_H ++ ++enum QSPSortBy { ++ QSP_SORT_BY_TOTAL_WAIT_TIME, ++ QSP_SORT_BY_AVG_WAIT_TIME, ++}; ++ ++void qsp_report(size_t max, enum QSPSortBy sort_by, ++ bool callsite_coalesce); ++ ++bool qsp_is_enabled(void); ++void qsp_enable(void); ++void qsp_disable(void); ++void qsp_reset(void); ++ ++#endif /* QEMU_QSP_H */ +diff --git a/include/qemu/queue.h b/include/qemu/queue.h +new file mode 100644 +index 00000000..e029e7bf +--- /dev/null ++++ b/include/qemu/queue.h +@@ -0,0 +1,576 @@ ++/* $NetBSD: queue.h,v 1.52 2009/04/20 09:56:08 mschuett Exp $ */ ++ ++/* ++ * QEMU version: Copy from netbsd, removed debug code, removed some of ++ * the implementations. Left in singly-linked lists, lists, simple ++ * queues, and tail queues. ++ */ ++ ++/* ++ * Copyright (c) 1991, 1993 ++ * The Regents of the University of California. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the University nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * @(#)queue.h 8.5 (Berkeley) 8/20/94 ++ */ ++ ++#ifndef QEMU_SYS_QUEUE_H ++#define QEMU_SYS_QUEUE_H ++ ++/* ++ * This file defines four types of data structures: singly-linked lists, ++ * lists, simple queues, and tail queues. ++ * ++ * A singly-linked list is headed by a single forward pointer. The ++ * elements are singly linked for minimum space and pointer manipulation ++ * overhead at the expense of O(n) removal for arbitrary elements. New ++ * elements can be added to the list after an existing element or at the ++ * head of the list. Elements being removed from the head of the list ++ * should use the explicit macro for this purpose for optimum ++ * efficiency. A singly-linked list may only be traversed in the forward ++ * direction. Singly-linked lists are ideal for applications with large ++ * datasets and few or no removals or for implementing a LIFO queue. ++ * ++ * A list is headed by a single forward pointer (or an array of forward ++ * pointers for a hash table header). The elements are doubly linked ++ * so that an arbitrary element can be removed without a need to ++ * traverse the list. New elements can be added to the list before ++ * or after an existing element or at the head of the list. A list ++ * may only be traversed in the forward direction. ++ * ++ * A simple queue is headed by a pair of pointers, one the head of the ++ * list and the other to the tail of the list. The elements are singly ++ * linked to save space, so elements can only be removed from the ++ * head of the list. New elements can be added to the list after ++ * an existing element, at the head of the list, or at the end of the ++ * list. A simple queue may only be traversed in the forward direction. ++ * ++ * A tail queue is headed by a pair of pointers, one to the head of the ++ * list and the other to the tail of the list. The elements are doubly ++ * linked so that an arbitrary element can be removed without a need to ++ * traverse the list. New elements can be added to the list before or ++ * after an existing element, at the head of the list, or at the end of ++ * the list. A tail queue may be traversed in either direction. ++ * ++ * For details on the use of these macros, see the queue(3) manual page. ++ */ ++ ++/* ++ * List definitions. ++ */ ++#define QLIST_HEAD(name, type) \ ++struct name { \ ++ struct type *lh_first; /* first element */ \ ++} ++ ++#define QLIST_HEAD_INITIALIZER(head) \ ++ { NULL } ++ ++#define QLIST_ENTRY(type) \ ++struct { \ ++ struct type *le_next; /* next element */ \ ++ struct type **le_prev; /* address of previous next element */ \ ++} ++ ++/* ++ * List functions. ++ */ ++#define QLIST_INIT(head) do { \ ++ (head)->lh_first = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++#define QLIST_SWAP(dstlist, srclist, field) do { \ ++ void *tmplist; \ ++ tmplist = (srclist)->lh_first; \ ++ (srclist)->lh_first = (dstlist)->lh_first; \ ++ if ((srclist)->lh_first != NULL) { \ ++ (srclist)->lh_first->field.le_prev = &(srclist)->lh_first; \ ++ } \ ++ (dstlist)->lh_first = tmplist; \ ++ if ((dstlist)->lh_first != NULL) { \ ++ (dstlist)->lh_first->field.le_prev = &(dstlist)->lh_first; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QLIST_INSERT_AFTER(listelm, elm, field) do { \ ++ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ ++ (listelm)->field.le_next->field.le_prev = \ ++ &(elm)->field.le_next; \ ++ (listelm)->field.le_next = (elm); \ ++ (elm)->field.le_prev = &(listelm)->field.le_next; \ ++} while (/*CONSTCOND*/0) ++ ++#define QLIST_INSERT_BEFORE(listelm, elm, field) do { \ ++ (elm)->field.le_prev = (listelm)->field.le_prev; \ ++ (elm)->field.le_next = (listelm); \ ++ *(listelm)->field.le_prev = (elm); \ ++ (listelm)->field.le_prev = &(elm)->field.le_next; \ ++} while (/*CONSTCOND*/0) ++ ++#define QLIST_INSERT_HEAD(head, elm, field) do { \ ++ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ ++ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ ++ (head)->lh_first = (elm); \ ++ (elm)->field.le_prev = &(head)->lh_first; \ ++} while (/*CONSTCOND*/0) ++ ++#define QLIST_REMOVE(elm, field) do { \ ++ if ((elm)->field.le_next != NULL) \ ++ (elm)->field.le_next->field.le_prev = \ ++ (elm)->field.le_prev; \ ++ *(elm)->field.le_prev = (elm)->field.le_next; \ ++ (elm)->field.le_next = NULL; \ ++ (elm)->field.le_prev = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++/* ++ * Like QLIST_REMOVE() but safe to call when elm is not in a list ++ */ ++#define QLIST_SAFE_REMOVE(elm, field) do { \ ++ if ((elm)->field.le_prev != NULL) { \ ++ if ((elm)->field.le_next != NULL) \ ++ (elm)->field.le_next->field.le_prev = \ ++ (elm)->field.le_prev; \ ++ *(elm)->field.le_prev = (elm)->field.le_next; \ ++ (elm)->field.le_next = NULL; \ ++ (elm)->field.le_prev = NULL; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++/* Is elm in a list? */ ++#define QLIST_IS_INSERTED(elm, field) ((elm)->field.le_prev != NULL) ++ ++#define QLIST_FOREACH(var, head, field) \ ++ for ((var) = ((head)->lh_first); \ ++ (var); \ ++ (var) = ((var)->field.le_next)) ++ ++#define QLIST_FOREACH_SAFE(var, head, field, next_var) \ ++ for ((var) = ((head)->lh_first); \ ++ (var) && ((next_var) = ((var)->field.le_next), 1); \ ++ (var) = (next_var)) ++ ++/* ++ * List access methods. ++ */ ++#define QLIST_EMPTY(head) ((head)->lh_first == NULL) ++#define QLIST_FIRST(head) ((head)->lh_first) ++#define QLIST_NEXT(elm, field) ((elm)->field.le_next) ++ ++ ++/* ++ * Singly-linked List definitions. ++ */ ++#define QSLIST_HEAD(name, type) \ ++struct name { \ ++ struct type *slh_first; /* first element */ \ ++} ++ ++#define QSLIST_HEAD_INITIALIZER(head) \ ++ { NULL } ++ ++#define QSLIST_ENTRY(type) \ ++struct { \ ++ struct type *sle_next; /* next element */ \ ++} ++ ++/* ++ * Singly-linked List functions. ++ */ ++#define QSLIST_INIT(head) do { \ ++ (head)->slh_first = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_INSERT_AFTER(slistelm, elm, field) do { \ ++ (elm)->field.sle_next = (slistelm)->field.sle_next; \ ++ (slistelm)->field.sle_next = (elm); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_INSERT_HEAD(head, elm, field) do { \ ++ (elm)->field.sle_next = (head)->slh_first; \ ++ (head)->slh_first = (elm); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_INSERT_HEAD_ATOMIC(head, elm, field) do { \ ++ typeof(elm) save_sle_next; \ ++ do { \ ++ save_sle_next = (elm)->field.sle_next = (head)->slh_first; \ ++ } while (qatomic_cmpxchg(&(head)->slh_first, save_sle_next, (elm)) !=\ ++ save_sle_next); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_MOVE_ATOMIC(dest, src) do { \ ++ (dest)->slh_first = qatomic_xchg(&(src)->slh_first, NULL); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_REMOVE_HEAD(head, field) do { \ ++ typeof((head)->slh_first) elm = (head)->slh_first; \ ++ (head)->slh_first = elm->field.sle_next; \ ++ elm->field.sle_next = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_REMOVE_AFTER(slistelm, field) do { \ ++ typeof(slistelm) next = (slistelm)->field.sle_next; \ ++ (slistelm)->field.sle_next = next->field.sle_next; \ ++ next->field.sle_next = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_REMOVE(head, elm, type, field) do { \ ++ if ((head)->slh_first == (elm)) { \ ++ QSLIST_REMOVE_HEAD((head), field); \ ++ } else { \ ++ struct type *curelm = (head)->slh_first; \ ++ while (curelm->field.sle_next != (elm)) \ ++ curelm = curelm->field.sle_next; \ ++ curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \ ++ (elm)->field.sle_next = NULL; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_FOREACH(var, head, field) \ ++ for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) ++ ++#define QSLIST_FOREACH_SAFE(var, head, field, tvar) \ ++ for ((var) = QSLIST_FIRST((head)); \ ++ (var) && ((tvar) = QSLIST_NEXT((var), field), 1); \ ++ (var) = (tvar)) ++ ++/* ++ * Singly-linked List access methods. ++ */ ++#define QSLIST_EMPTY(head) ((head)->slh_first == NULL) ++#define QSLIST_FIRST(head) ((head)->slh_first) ++#define QSLIST_NEXT(elm, field) ((elm)->field.sle_next) ++ ++ ++/* ++ * Simple queue definitions. ++ */ ++#define QSIMPLEQ_HEAD(name, type) \ ++struct name { \ ++ struct type *sqh_first; /* first element */ \ ++ struct type **sqh_last; /* addr of last next element */ \ ++} ++ ++#define QSIMPLEQ_HEAD_INITIALIZER(head) \ ++ { NULL, &(head).sqh_first } ++ ++#define QSIMPLEQ_ENTRY(type) \ ++struct { \ ++ struct type *sqe_next; /* next element */ \ ++} ++ ++/* ++ * Simple queue functions. ++ */ ++#define QSIMPLEQ_INIT(head) do { \ ++ (head)->sqh_first = NULL; \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ ++ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ (head)->sqh_first = (elm); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ ++ (elm)->field.sqe_next = NULL; \ ++ *(head)->sqh_last = (elm); \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ ++ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ (listelm)->field.sqe_next = (elm); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_REMOVE_HEAD(head, field) do { \ ++ typeof((head)->sqh_first) elm = (head)->sqh_first; \ ++ if (((head)->sqh_first = elm->field.sqe_next) == NULL) \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++ elm->field.sqe_next = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_SPLIT_AFTER(head, elm, field, removed) do { \ ++ QSIMPLEQ_INIT(removed); \ ++ if (((removed)->sqh_first = (head)->sqh_first) != NULL) { \ ++ if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) { \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++ } \ ++ (removed)->sqh_last = &(elm)->field.sqe_next; \ ++ (elm)->field.sqe_next = NULL; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_REMOVE(head, elm, type, field) do { \ ++ if ((head)->sqh_first == (elm)) { \ ++ QSIMPLEQ_REMOVE_HEAD((head), field); \ ++ } else { \ ++ struct type *curelm = (head)->sqh_first; \ ++ while (curelm->field.sqe_next != (elm)) \ ++ curelm = curelm->field.sqe_next; \ ++ if ((curelm->field.sqe_next = \ ++ curelm->field.sqe_next->field.sqe_next) == NULL) \ ++ (head)->sqh_last = &(curelm)->field.sqe_next; \ ++ (elm)->field.sqe_next = NULL; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_FOREACH(var, head, field) \ ++ for ((var) = ((head)->sqh_first); \ ++ (var); \ ++ (var) = ((var)->field.sqe_next)) ++ ++#define QSIMPLEQ_FOREACH_SAFE(var, head, field, next) \ ++ for ((var) = ((head)->sqh_first); \ ++ (var) && ((next = ((var)->field.sqe_next)), 1); \ ++ (var) = (next)) ++ ++#define QSIMPLEQ_CONCAT(head1, head2) do { \ ++ if (!QSIMPLEQ_EMPTY((head2))) { \ ++ *(head1)->sqh_last = (head2)->sqh_first; \ ++ (head1)->sqh_last = (head2)->sqh_last; \ ++ QSIMPLEQ_INIT((head2)); \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_PREPEND(head1, head2) do { \ ++ if (!QSIMPLEQ_EMPTY((head2))) { \ ++ *(head2)->sqh_last = (head1)->sqh_first; \ ++ (head1)->sqh_first = (head2)->sqh_first; \ ++ QSIMPLEQ_INIT((head2)); \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_LAST(head, type, field) \ ++ (QSIMPLEQ_EMPTY((head)) ? \ ++ NULL : \ ++ ((struct type *)(void *) \ ++ ((char *)((head)->sqh_last) - offsetof(struct type, field)))) ++ ++/* ++ * Simple queue access methods. ++ */ ++#define QSIMPLEQ_EMPTY_ATOMIC(head) \ ++ (qatomic_read(&((head)->sqh_first)) == NULL) ++#define QSIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) ++#define QSIMPLEQ_FIRST(head) ((head)->sqh_first) ++#define QSIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) ++ ++typedef struct QTailQLink { ++ void *tql_next; ++ struct QTailQLink *tql_prev; ++} QTailQLink; ++ ++/* ++ * Tail queue definitions. The union acts as a poor man template, as if ++ * it were QTailQLink. ++ */ ++#define QTAILQ_HEAD(name, type) \ ++union name { \ ++ struct type *tqh_first; /* first element */ \ ++ QTailQLink tqh_circ; /* link for circular backwards list */ \ ++} ++ ++#define QTAILQ_HEAD_INITIALIZER(head) \ ++ { .tqh_circ = { NULL, &(head).tqh_circ } } ++ ++#define QTAILQ_ENTRY(type) \ ++union { \ ++ struct type *tqe_next; /* next element */ \ ++ QTailQLink tqe_circ; /* link for circular backwards list */ \ ++} ++ ++/* ++ * Tail queue functions. ++ */ ++#define QTAILQ_INIT(head) do { \ ++ (head)->tqh_first = NULL; \ ++ (head)->tqh_circ.tql_prev = &(head)->tqh_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_HEAD(head, elm, field) do { \ ++ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ ++ (head)->tqh_first->field.tqe_circ.tql_prev = \ ++ &(elm)->field.tqe_circ; \ ++ else \ ++ (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ ++ (head)->tqh_first = (elm); \ ++ (elm)->field.tqe_circ.tql_prev = &(head)->tqh_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_TAIL(head, elm, field) do { \ ++ (elm)->field.tqe_next = NULL; \ ++ (elm)->field.tqe_circ.tql_prev = (head)->tqh_circ.tql_prev; \ ++ (head)->tqh_circ.tql_prev->tql_next = (elm); \ ++ (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ ++ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ ++ (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ ++ &(elm)->field.tqe_circ; \ ++ else \ ++ (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ ++ (listelm)->field.tqe_next = (elm); \ ++ (elm)->field.tqe_circ.tql_prev = &(listelm)->field.tqe_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_BEFORE(listelm, elm, field) do { \ ++ (elm)->field.tqe_circ.tql_prev = (listelm)->field.tqe_circ.tql_prev; \ ++ (elm)->field.tqe_next = (listelm); \ ++ (listelm)->field.tqe_circ.tql_prev->tql_next = (elm); \ ++ (listelm)->field.tqe_circ.tql_prev = &(elm)->field.tqe_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_REMOVE(head, elm, field) do { \ ++ if (((elm)->field.tqe_next) != NULL) \ ++ (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ ++ (elm)->field.tqe_circ.tql_prev; \ ++ else \ ++ (head)->tqh_circ.tql_prev = (elm)->field.tqe_circ.tql_prev; \ ++ (elm)->field.tqe_circ.tql_prev->tql_next = (elm)->field.tqe_next; \ ++ (elm)->field.tqe_circ.tql_prev = NULL; \ ++ (elm)->field.tqe_circ.tql_next = NULL; \ ++ (elm)->field.tqe_next = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++/* remove @left, @right and all elements in between from @head */ ++#define QTAILQ_REMOVE_SEVERAL(head, left, right, field) do { \ ++ if (((right)->field.tqe_next) != NULL) \ ++ (right)->field.tqe_next->field.tqe_circ.tql_prev = \ ++ (left)->field.tqe_circ.tql_prev; \ ++ else \ ++ (head)->tqh_circ.tql_prev = (left)->field.tqe_circ.tql_prev; \ ++ (left)->field.tqe_circ.tql_prev->tql_next = (right)->field.tqe_next; \ ++ } while (/*CONSTCOND*/0) ++ ++#define QTAILQ_FOREACH(var, head, field) \ ++ for ((var) = ((head)->tqh_first); \ ++ (var); \ ++ (var) = ((var)->field.tqe_next)) ++ ++#define QTAILQ_FOREACH_SAFE(var, head, field, next_var) \ ++ for ((var) = ((head)->tqh_first); \ ++ (var) && ((next_var) = ((var)->field.tqe_next), 1); \ ++ (var) = (next_var)) ++ ++#define QTAILQ_FOREACH_REVERSE(var, head, field) \ ++ for ((var) = QTAILQ_LAST(head); \ ++ (var); \ ++ (var) = QTAILQ_PREV(var, field)) ++ ++#define QTAILQ_FOREACH_REVERSE_SAFE(var, head, field, prev_var) \ ++ for ((var) = QTAILQ_LAST(head); \ ++ (var) && ((prev_var) = QTAILQ_PREV(var, field), 1); \ ++ (var) = (prev_var)) ++ ++/* ++ * Tail queue access methods. ++ */ ++#define QTAILQ_EMPTY(head) ((head)->tqh_first == NULL) ++#define QTAILQ_FIRST(head) ((head)->tqh_first) ++#define QTAILQ_NEXT(elm, field) ((elm)->field.tqe_next) ++#define QTAILQ_IN_USE(elm, field) ((elm)->field.tqe_circ.tql_prev != NULL) ++ ++#define QTAILQ_LINK_PREV(link) \ ++ ((link).tql_prev->tql_prev->tql_next) ++#define QTAILQ_LAST(head) \ ++ ((typeof((head)->tqh_first)) QTAILQ_LINK_PREV((head)->tqh_circ)) ++#define QTAILQ_PREV(elm, field) \ ++ ((typeof((elm)->field.tqe_next)) QTAILQ_LINK_PREV((elm)->field.tqe_circ)) ++ ++#define field_at_offset(base, offset, type) \ ++ ((type *) (((char *) (base)) + (offset))) ++ ++/* ++ * Raw access of elements of a tail queue head. Offsets are all zero ++ * because it's a union. ++ */ ++#define QTAILQ_RAW_FIRST(head) \ ++ field_at_offset(head, 0, void *) ++#define QTAILQ_RAW_TQH_CIRC(head) \ ++ field_at_offset(head, 0, QTailQLink) ++ ++/* ++ * Raw access of elements of a tail entry ++ */ ++#define QTAILQ_RAW_NEXT(elm, entry) \ ++ field_at_offset(elm, entry, void *) ++#define QTAILQ_RAW_TQE_CIRC(elm, entry) \ ++ field_at_offset(elm, entry, QTailQLink) ++/* ++ * Tail queue traversal using pointer arithmetic. ++ */ ++#define QTAILQ_RAW_FOREACH(elm, head, entry) \ ++ for ((elm) = *QTAILQ_RAW_FIRST(head); \ ++ (elm); \ ++ (elm) = *QTAILQ_RAW_NEXT(elm, entry)) ++/* ++ * Tail queue insertion using pointer arithmetic. ++ */ ++#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry) do { \ ++ *QTAILQ_RAW_NEXT(elm, entry) = NULL; \ ++ QTAILQ_RAW_TQE_CIRC(elm, entry)->tql_prev = QTAILQ_RAW_TQH_CIRC(head)->tql_prev; \ ++ QTAILQ_RAW_TQH_CIRC(head)->tql_prev->tql_next = (elm); \ ++ QTAILQ_RAW_TQH_CIRC(head)->tql_prev = QTAILQ_RAW_TQE_CIRC(elm, entry); \ ++} while (/*CONSTCOND*/0) ++ ++#define QLIST_RAW_FIRST(head) \ ++ field_at_offset(head, 0, void *) ++ ++#define QLIST_RAW_NEXT(elm, entry) \ ++ field_at_offset(elm, entry, void *) ++ ++#define QLIST_RAW_PREVIOUS(elm, entry) \ ++ field_at_offset(elm, entry + sizeof(void *), void *) ++ ++#define QLIST_RAW_FOREACH(elm, head, entry) \ ++ for ((elm) = *QLIST_RAW_FIRST(head); \ ++ (elm); \ ++ (elm) = *QLIST_RAW_NEXT(elm, entry)) ++ ++#define QLIST_RAW_INSERT_AFTER(head, prev, elem, entry) do { \ ++ *QLIST_RAW_NEXT(prev, entry) = elem; \ ++ *QLIST_RAW_PREVIOUS(elem, entry) = QLIST_RAW_NEXT(prev, entry); \ ++ *QLIST_RAW_NEXT(elem, entry) = NULL; \ ++} while (0) ++ ++#define QLIST_RAW_INSERT_HEAD(head, elm, entry) do { \ ++ void *first = *QLIST_RAW_FIRST(head); \ ++ *QLIST_RAW_FIRST(head) = elm; \ ++ *QLIST_RAW_PREVIOUS(elm, entry) = QLIST_RAW_FIRST(head); \ ++ if (first) { \ ++ *QLIST_RAW_NEXT(elm, entry) = first; \ ++ *QLIST_RAW_PREVIOUS(first, entry) = QLIST_RAW_NEXT(elm, entry); \ ++ } else { \ ++ *QLIST_RAW_NEXT(elm, entry) = NULL; \ ++ } \ ++} while (0) ++ ++#endif /* QEMU_SYS_QUEUE_H */ +diff --git a/include/qemu/range.h b/include/qemu/range.h +new file mode 100644 +index 00000000..d446ad88 +--- /dev/null ++++ b/include/qemu/range.h +@@ -0,0 +1,247 @@ ++/* ++ * QEMU 64-bit address ranges ++ * ++ * Copyright (c) 2015-2016 Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see . ++ */ ++ ++#ifndef QEMU_RANGE_H ++#define QEMU_RANGE_H ++ ++#include "qemu/bitops.h" ++ ++/* ++ * Operations on 64 bit address ranges. ++ * Notes: ++ * - Ranges must not wrap around 0, but can include UINT64_MAX. ++ */ ++ ++struct Range { ++ /* ++ * Do not access members directly, use the functions! ++ * A non-empty range has @lob <= @upb. ++ * An empty range has @lob == @upb + 1. ++ */ ++ uint64_t lob; /* inclusive lower bound */ ++ uint64_t upb; /* inclusive upper bound */ ++}; ++ ++static inline void range_invariant(const Range *range) ++{ ++ assert(range->lob <= range->upb || range->lob == range->upb + 1); ++} ++ ++/* Compound literal encoding the empty range */ ++#define range_empty ((Range){ .lob = 1, .upb = 0 }) ++ ++/* Is @range empty? */ ++static inline bool range_is_empty(const Range *range) ++{ ++ range_invariant(range); ++ return range->lob > range->upb; ++} ++ ++/* Does @range contain @val? */ ++static inline bool range_contains(const Range *range, uint64_t val) ++{ ++ return val >= range->lob && val <= range->upb; ++} ++ ++/* Initialize @range to the empty range */ ++static inline void range_make_empty(Range *range) ++{ ++ *range = range_empty; ++ assert(range_is_empty(range)); ++} ++ ++/* ++ * Initialize @range to span the interval [@lob,@upb]. ++ * Both bounds are inclusive. ++ * The interval must not be empty, i.e. @lob must be less than or ++ * equal @upb. ++ */ ++static inline void range_set_bounds(Range *range, uint64_t lob, uint64_t upb) ++{ ++ range->lob = lob; ++ range->upb = upb; ++ assert(!range_is_empty(range)); ++} ++ ++/* ++ * Initialize @range to span the interval [@lob,@upb_plus1). ++ * The lower bound is inclusive, the upper bound is exclusive. ++ * Zero @upb_plus1 is special: if @lob is also zero, set @range to the ++ * empty range. Else, set @range to [@lob,UINT64_MAX]. ++ */ ++static inline void range_set_bounds1(Range *range, ++ uint64_t lob, uint64_t upb_plus1) ++{ ++ if (!lob && !upb_plus1) { ++ *range = range_empty; ++ } else { ++ range->lob = lob; ++ range->upb = upb_plus1 - 1; ++ } ++ range_invariant(range); ++} ++ ++/* Return @range's lower bound. @range must not be empty. */ ++static inline uint64_t range_lob(Range *range) ++{ ++ assert(!range_is_empty(range)); ++ return range->lob; ++} ++ ++/* Return @range's upper bound. @range must not be empty. */ ++static inline uint64_t range_upb(Range *range) ++{ ++ assert(!range_is_empty(range)); ++ return range->upb; ++} ++ ++/* ++ * Initialize @range to span the interval [@lob,@lob + @size - 1]. ++ * @size may be 0. If the range would overflow, returns -ERANGE, otherwise ++ * 0. ++ */ ++G_GNUC_WARN_UNUSED_RESULT ++static inline int range_init(Range *range, uint64_t lob, uint64_t size) ++{ ++ if (lob + size < lob) { ++ return -ERANGE; ++ } ++ range->lob = lob; ++ range->upb = lob + size - 1; ++ range_invariant(range); ++ return 0; ++} ++ ++/* ++ * Initialize @range to span the interval [@lob,@lob + @size - 1]. ++ * @size may be 0. Range must not overflow. ++ */ ++static inline void range_init_nofail(Range *range, uint64_t lob, uint64_t size) ++{ ++ range->lob = lob; ++ range->upb = lob + size - 1; ++ range_invariant(range); ++} ++ ++/* ++ * Get the size of @range. ++ */ ++static inline uint64_t range_size(const Range *range) ++{ ++ return range->upb - range->lob + 1; ++} ++ ++/* ++ * Check if @range1 overlaps with @range2. If one of the ranges is empty, ++ * the result is always "false". ++ */ ++static inline bool range_overlaps_range(const Range *range1, ++ const Range *range2) ++{ ++ if (range_is_empty(range1) || range_is_empty(range2)) { ++ return false; ++ } ++ return !(range2->upb < range1->lob || range1->upb < range2->lob); ++} ++ ++/* ++ * Check if @range1 contains @range2. If one of the ranges is empty, ++ * the result is always "false". ++ */ ++static inline bool range_contains_range(const Range *range1, ++ const Range *range2) ++{ ++ if (range_is_empty(range1) || range_is_empty(range2)) { ++ return false; ++ } ++ return range1->lob <= range2->lob && range1->upb >= range2->upb; ++} ++ ++/* ++ * Extend @range to the smallest interval that includes @extend_by, too. ++ */ ++static inline void range_extend(Range *range, Range *extend_by) ++{ ++ if (range_is_empty(extend_by)) { ++ return; ++ } ++ if (range_is_empty(range)) { ++ *range = *extend_by; ++ return; ++ } ++ if (range->lob > extend_by->lob) { ++ range->lob = extend_by->lob; ++ } ++ if (range->upb < extend_by->upb) { ++ range->upb = extend_by->upb; ++ } ++ range_invariant(range); ++} ++ ++/* Get last byte of a range from offset + length. ++ * Undefined for ranges that wrap around 0. */ ++static inline uint64_t range_get_last(uint64_t offset, uint64_t len) ++{ ++ return offset + len - 1; ++} ++ ++/* Check whether a given range covers a given byte. */ ++static inline int range_covers_byte(uint64_t offset, uint64_t len, ++ uint64_t byte) ++{ ++ return offset <= byte && byte <= range_get_last(offset, len); ++} ++ ++/* Check whether 2 given ranges overlap. ++ * Undefined if ranges that wrap around 0. */ ++static inline bool ranges_overlap(uint64_t first1, uint64_t len1, ++ uint64_t first2, uint64_t len2) ++{ ++ uint64_t last1 = range_get_last(first1, len1); ++ uint64_t last2 = range_get_last(first2, len2); ++ ++ return !(last2 < first1 || last1 < first2); ++} ++ ++/* Get highest non-zero bit position of a range */ ++static inline int range_get_last_bit(Range *range) ++{ ++ if (range_is_empty(range)) { ++ return -1; ++ } ++ return 63 - clz64(range->upb); ++} ++ ++/* ++ * Return -1 if @a < @b, 1 @a > @b, and 0 if they touch or overlap. ++ * Both @a and @b must not be empty. ++ */ ++int range_compare(Range *a, Range *b); ++ ++GList *range_list_insert(GList *list, Range *data); ++ ++/* ++ * Inverse an array of sorted ranges over the [low, high] span, ie. ++ * original ranges becomes holes in the newly allocated inv_ranges ++ */ ++void range_inverse_array(GList *in_ranges, ++ GList **out_ranges, ++ uint64_t low, uint64_t high); ++ ++#endif +diff --git a/include/qemu/ratelimit.h b/include/qemu/ratelimit.h +new file mode 100644 +index 00000000..48bf59e8 +--- /dev/null ++++ b/include/qemu/ratelimit.h +@@ -0,0 +1,97 @@ ++/* ++ * Ratelimiting calculations ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Stefan Hajnoczi ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_RATELIMIT_H ++#define QEMU_RATELIMIT_H ++ ++#include "qemu/lockable.h" ++#include "qemu/timer.h" ++ ++typedef struct { ++ QemuMutex lock; ++ int64_t slice_start_time; ++ int64_t slice_end_time; ++ uint64_t slice_quota; ++ uint64_t slice_ns; ++ uint64_t dispatched; ++} RateLimit; ++ ++/** Calculate and return delay for next request in ns ++ * ++ * Record that we sent @n data units (where @n matches the scale chosen ++ * during ratelimit_set_speed). If we may send more data units ++ * in the current time slice, return 0 (i.e. no delay). Otherwise ++ * return the amount of time (in ns) until the start of the next time ++ * slice that will permit sending the next chunk of data. ++ * ++ * Recording sent data units even after exceeding the quota is ++ * permitted; the time slice will be extended accordingly. ++ */ ++static inline int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n) ++{ ++ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); ++ double delay_slices; ++ ++ QEMU_LOCK_GUARD(&limit->lock); ++ if (!limit->slice_quota) { ++ /* Throttling disabled. */ ++ return 0; ++ } ++ assert(limit->slice_ns); ++ ++ if (limit->slice_end_time < now) { ++ /* Previous, possibly extended, time slice finished; reset the ++ * accounting. */ ++ limit->slice_start_time = now; ++ limit->slice_end_time = now + limit->slice_ns; ++ limit->dispatched = 0; ++ } ++ ++ limit->dispatched += n; ++ if (limit->dispatched < limit->slice_quota) { ++ /* We may send further data within the current time slice, no ++ * need to delay the next request. */ ++ return 0; ++ } ++ ++ /* Quota exceeded. Wait based on the excess amount and then start a new ++ * slice. */ ++ delay_slices = (double)limit->dispatched / limit->slice_quota; ++ limit->slice_end_time = limit->slice_start_time + ++ (uint64_t)(delay_slices * limit->slice_ns); ++ return limit->slice_end_time - now; ++} ++ ++static inline void ratelimit_init(RateLimit *limit) ++{ ++ qemu_mutex_init(&limit->lock); ++} ++ ++static inline void ratelimit_destroy(RateLimit *limit) ++{ ++ qemu_mutex_destroy(&limit->lock); ++} ++ ++static inline void ratelimit_set_speed(RateLimit *limit, uint64_t speed, ++ uint64_t slice_ns) ++{ ++ QEMU_LOCK_GUARD(&limit->lock); ++ limit->slice_ns = slice_ns; ++ if (speed == 0) { ++ limit->slice_quota = 0; ++ } else { ++ limit->slice_quota = MAX(((double)speed * slice_ns) / 1000000000ULL, 1); ++ } ++} ++ ++#endif +diff --git a/include/qemu/rcu.h b/include/qemu/rcu.h +new file mode 100644 +index 00000000..fea058aa +--- /dev/null ++++ b/include/qemu/rcu.h +@@ -0,0 +1,198 @@ ++#ifndef QEMU_RCU_H ++#define QEMU_RCU_H ++ ++/* ++ * urcu-mb.h ++ * ++ * Userspace RCU header with explicit memory barrier. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * IBM's contributions to this file may be relicensed under LGPLv2 or later. ++ */ ++ ++ ++#include "qemu/thread.h" ++#include "qemu/queue.h" ++#include "qemu/atomic.h" ++#include "qemu/notify.h" ++#include "qemu/sys_membarrier.h" ++#include "qemu/coroutine-tls.h" ++ ++/* ++ * Important ! ++ * ++ * Each thread containing read-side critical sections must be registered ++ * with rcu_register_thread() before calling rcu_read_lock(). ++ * rcu_unregister_thread() should be called before the thread exits. ++ */ ++ ++#ifdef DEBUG_RCU ++#define rcu_assert(args...) assert(args) ++#else ++#define rcu_assert(args...) ++#endif ++ ++/* ++ * Global quiescent period counter with low-order bits unused. ++ * Using a int rather than a char to eliminate false register dependencies ++ * causing stalls on some architectures. ++ */ ++extern unsigned long rcu_gp_ctr; ++ ++extern QemuEvent rcu_gp_event; ++ ++struct rcu_reader_data { ++ /* Data used by both reader and synchronize_rcu() */ ++ unsigned long ctr; ++ bool waiting; ++ ++ /* Data used by reader only */ ++ unsigned depth; ++ ++ /* Data used for registry, protected by rcu_registry_lock */ ++ QLIST_ENTRY(rcu_reader_data) node; ++ ++ /* ++ * NotifierList used to force an RCU grace period. Accessed under ++ * rcu_registry_lock. Note that the notifier is called _outside_ ++ * the thread! ++ */ ++ NotifierList force_rcu; ++}; ++ ++QEMU_DECLARE_CO_TLS(struct rcu_reader_data, rcu_reader) ++ ++static inline void rcu_read_lock(void) ++{ ++ struct rcu_reader_data *p_rcu_reader = get_ptr_rcu_reader(); ++ unsigned ctr; ++ ++ if (p_rcu_reader->depth++ > 0) { ++ return; ++ } ++ ++ ctr = qatomic_read(&rcu_gp_ctr); ++ qatomic_set(&p_rcu_reader->ctr, ctr); ++ ++ /* ++ * Read rcu_gp_ptr and write p_rcu_reader->ctr before reading ++ * RCU-protected pointers. ++ */ ++ smp_mb_placeholder(); ++} ++ ++static inline void rcu_read_unlock(void) ++{ ++ struct rcu_reader_data *p_rcu_reader = get_ptr_rcu_reader(); ++ ++ assert(p_rcu_reader->depth != 0); ++ if (--p_rcu_reader->depth > 0) { ++ return; ++ } ++ ++ /* Ensure that the critical section is seen to precede the ++ * store to p_rcu_reader->ctr. Together with the following ++ * smp_mb_placeholder(), this ensures writes to p_rcu_reader->ctr ++ * are sequentially consistent. ++ */ ++ qatomic_store_release(&p_rcu_reader->ctr, 0); ++ ++ /* Write p_rcu_reader->ctr before reading p_rcu_reader->waiting. */ ++ smp_mb_placeholder(); ++ if (unlikely(qatomic_read(&p_rcu_reader->waiting))) { ++ qatomic_set(&p_rcu_reader->waiting, false); ++ qemu_event_set(&rcu_gp_event); ++ } ++} ++ ++void synchronize_rcu(void); ++ ++/* ++ * Reader thread registration. ++ */ ++void rcu_register_thread(void); ++void rcu_unregister_thread(void); ++ ++/* ++ * Support for fork(). fork() support is enabled at startup. ++ */ ++void rcu_enable_atfork(void); ++void rcu_disable_atfork(void); ++ ++struct rcu_head; ++typedef void RCUCBFunc(struct rcu_head *head); ++ ++struct rcu_head { ++ struct rcu_head *next; ++ RCUCBFunc *func; ++}; ++ ++void call_rcu1(struct rcu_head *head, RCUCBFunc *func); ++void drain_call_rcu(void); ++ ++/* The operands of the minus operator must have the same type, ++ * which must be the one that we specify in the cast. ++ */ ++#define call_rcu(head, func, field) \ ++ call_rcu1(({ \ ++ char __attribute__((unused)) \ ++ offset_must_be_zero[-offsetof(typeof(*(head)), field)], \ ++ func_type_invalid = (func) - (void (*)(typeof(head)))(func); \ ++ &(head)->field; \ ++ }), \ ++ (RCUCBFunc *)(func)) ++ ++#define g_free_rcu(obj, field) \ ++ call_rcu1(({ \ ++ char __attribute__((unused)) \ ++ offset_must_be_zero[-offsetof(typeof(*(obj)), field)]; \ ++ &(obj)->field; \ ++ }), \ ++ (RCUCBFunc *)g_free); ++ ++typedef void RCUReadAuto; ++static inline RCUReadAuto *rcu_read_auto_lock(void) ++{ ++ rcu_read_lock(); ++ /* Anything non-NULL causes the cleanup function to be called */ ++ return (void *)(uintptr_t)0x1; ++} ++ ++static inline void rcu_read_auto_unlock(RCUReadAuto *r) ++{ ++ rcu_read_unlock(); ++} ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RCUReadAuto, rcu_read_auto_unlock) ++ ++#define WITH_RCU_READ_LOCK_GUARD() \ ++ WITH_RCU_READ_LOCK_GUARD_(glue(_rcu_read_auto, __COUNTER__)) ++ ++#define WITH_RCU_READ_LOCK_GUARD_(var) \ ++ for (g_autoptr(RCUReadAuto) var = rcu_read_auto_lock(); \ ++ (var); rcu_read_auto_unlock(var), (var) = NULL) ++ ++#define RCU_READ_LOCK_GUARD() \ ++ g_autoptr(RCUReadAuto) _rcu_read_auto __attribute__((unused)) = rcu_read_auto_lock() ++ ++/* ++ * Force-RCU notifiers tell readers that they should exit their ++ * read-side critical section. ++ */ ++void rcu_add_force_rcu_notifier(Notifier *n); ++void rcu_remove_force_rcu_notifier(Notifier *n); ++ ++#endif /* QEMU_RCU_H */ +diff --git a/include/qemu/rcu_queue.h b/include/qemu/rcu_queue.h +new file mode 100644 +index 00000000..4e6298d4 +--- /dev/null ++++ b/include/qemu/rcu_queue.h +@@ -0,0 +1,309 @@ ++#ifndef QEMU_RCU_QUEUE_H ++#define QEMU_RCU_QUEUE_H ++ ++/* ++ * rcu_queue.h ++ * ++ * RCU-friendly versions of the queue.h primitives. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Copyright (c) 2013 Mike D. Day, IBM Corporation. ++ * ++ * IBM's contributions to this file may be relicensed under LGPLv2 or later. ++ */ ++ ++#include "qemu/queue.h" ++#include "qemu/atomic.h" ++ ++/* ++ * List access methods. ++ */ ++#define QLIST_EMPTY_RCU(head) (qatomic_read(&(head)->lh_first) == NULL) ++#define QLIST_FIRST_RCU(head) (qatomic_rcu_read(&(head)->lh_first)) ++#define QLIST_NEXT_RCU(elm, field) (qatomic_rcu_read(&(elm)->field.le_next)) ++ ++/* ++ * List functions. ++ */ ++ ++ ++/* ++ * The difference between qatomic_read/set and qatomic_rcu_read/set ++ * is in the including of a read/write memory barrier to the volatile ++ * access. atomic_rcu_* macros include the memory barrier, the ++ * plain atomic macros do not. Therefore, it should be correct to ++ * issue a series of reads or writes to the same element using only ++ * the atomic_* macro, until the last read or write, which should be ++ * atomic_rcu_* to introduce a read or write memory barrier as ++ * appropriate. ++ */ ++ ++/* Upon publication of the listelm->next value, list readers ++ * will see the new node when following next pointers from ++ * antecedent nodes, but may not see the new node when following ++ * prev pointers from subsequent nodes until after the RCU grace ++ * period expires. ++ * see linux/include/rculist.h __list_add_rcu(new, prev, next) ++ */ ++#define QLIST_INSERT_AFTER_RCU(listelm, elm, field) do { \ ++ (elm)->field.le_next = (listelm)->field.le_next; \ ++ (elm)->field.le_prev = &(listelm)->field.le_next; \ ++ qatomic_rcu_set(&(listelm)->field.le_next, (elm)); \ ++ if ((elm)->field.le_next != NULL) { \ ++ (elm)->field.le_next->field.le_prev = \ ++ &(elm)->field.le_next; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++/* Upon publication of the listelm->prev->next value, list ++ * readers will see the new element when following prev pointers ++ * from subsequent elements, but may not see the new element ++ * when following next pointers from antecedent elements ++ * until after the RCU grace period expires. ++ */ ++#define QLIST_INSERT_BEFORE_RCU(listelm, elm, field) do { \ ++ (elm)->field.le_prev = (listelm)->field.le_prev; \ ++ (elm)->field.le_next = (listelm); \ ++ qatomic_rcu_set((listelm)->field.le_prev, (elm)); \ ++ (listelm)->field.le_prev = &(elm)->field.le_next; \ ++} while (/*CONSTCOND*/0) ++ ++/* Upon publication of the head->first value, list readers ++ * will see the new element when following the head, but may ++ * not see the new element when following prev pointers from ++ * subsequent elements until after the RCU grace period has ++ * expired. ++ */ ++#define QLIST_INSERT_HEAD_RCU(head, elm, field) do { \ ++ (elm)->field.le_prev = &(head)->lh_first; \ ++ (elm)->field.le_next = (head)->lh_first; \ ++ qatomic_rcu_set((&(head)->lh_first), (elm)); \ ++ if ((elm)->field.le_next != NULL) { \ ++ (elm)->field.le_next->field.le_prev = \ ++ &(elm)->field.le_next; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++ ++/* prior to publication of the elm->prev->next value, some list ++ * readers may still see the removed element when following ++ * the antecedent's next pointer. ++ */ ++#define QLIST_REMOVE_RCU(elm, field) do { \ ++ if ((elm)->field.le_next != NULL) { \ ++ (elm)->field.le_next->field.le_prev = \ ++ (elm)->field.le_prev; \ ++ } \ ++ qatomic_set((elm)->field.le_prev, (elm)->field.le_next); \ ++} while (/*CONSTCOND*/0) ++ ++/* List traversal must occur within an RCU critical section. */ ++#define QLIST_FOREACH_RCU(var, head, field) \ ++ for ((var) = qatomic_rcu_read(&(head)->lh_first); \ ++ (var); \ ++ (var) = qatomic_rcu_read(&(var)->field.le_next)) ++ ++/* List traversal must occur within an RCU critical section. */ ++#define QLIST_FOREACH_SAFE_RCU(var, head, field, next_var) \ ++ for ((var) = (qatomic_rcu_read(&(head)->lh_first)); \ ++ (var) && \ ++ ((next_var) = qatomic_rcu_read(&(var)->field.le_next), 1); \ ++ (var) = (next_var)) ++ ++/* ++ * RCU simple queue ++ */ ++ ++/* Simple queue access methods */ ++#define QSIMPLEQ_EMPTY_RCU(head) \ ++ (qatomic_read(&(head)->sqh_first) == NULL) ++#define QSIMPLEQ_FIRST_RCU(head) qatomic_rcu_read(&(head)->sqh_first) ++#define QSIMPLEQ_NEXT_RCU(elm, field) qatomic_rcu_read(&(elm)->field.sqe_next) ++ ++/* Simple queue functions */ ++#define QSIMPLEQ_INSERT_HEAD_RCU(head, elm, field) do { \ ++ (elm)->field.sqe_next = (head)->sqh_first; \ ++ if ((elm)->field.sqe_next == NULL) { \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ } \ ++ qatomic_rcu_set(&(head)->sqh_first, (elm)); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_INSERT_TAIL_RCU(head, elm, field) do { \ ++ (elm)->field.sqe_next = NULL; \ ++ qatomic_rcu_set((head)->sqh_last, (elm)); \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_INSERT_AFTER_RCU(head, listelm, elm, field) do { \ ++ (elm)->field.sqe_next = (listelm)->field.sqe_next; \ ++ if ((elm)->field.sqe_next == NULL) { \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ } \ ++ qatomic_rcu_set(&(listelm)->field.sqe_next, (elm)); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_REMOVE_HEAD_RCU(head, field) do { \ ++ qatomic_set(&(head)->sqh_first, (head)->sqh_first->field.sqe_next);\ ++ if ((head)->sqh_first == NULL) { \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_REMOVE_RCU(head, elm, type, field) do { \ ++ if ((head)->sqh_first == (elm)) { \ ++ QSIMPLEQ_REMOVE_HEAD_RCU((head), field); \ ++ } else { \ ++ struct type *curr = (head)->sqh_first; \ ++ while (curr->field.sqe_next != (elm)) { \ ++ curr = curr->field.sqe_next; \ ++ } \ ++ qatomic_set(&curr->field.sqe_next, \ ++ curr->field.sqe_next->field.sqe_next); \ ++ if (curr->field.sqe_next == NULL) { \ ++ (head)->sqh_last = &(curr)->field.sqe_next; \ ++ } \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_FOREACH_RCU(var, head, field) \ ++ for ((var) = qatomic_rcu_read(&(head)->sqh_first); \ ++ (var); \ ++ (var) = qatomic_rcu_read(&(var)->field.sqe_next)) ++ ++#define QSIMPLEQ_FOREACH_SAFE_RCU(var, head, field, next) \ ++ for ((var) = qatomic_rcu_read(&(head)->sqh_first); \ ++ (var) && ((next) = qatomic_rcu_read(&(var)->field.sqe_next), 1);\ ++ (var) = (next)) ++ ++/* ++ * RCU tail queue ++ */ ++ ++/* Tail queue access methods */ ++#define QTAILQ_EMPTY_RCU(head) (qatomic_read(&(head)->tqh_first) == NULL) ++#define QTAILQ_FIRST_RCU(head) qatomic_rcu_read(&(head)->tqh_first) ++#define QTAILQ_NEXT_RCU(elm, field) qatomic_rcu_read(&(elm)->field.tqe_next) ++ ++/* Tail queue functions */ ++#define QTAILQ_INSERT_HEAD_RCU(head, elm, field) do { \ ++ (elm)->field.tqe_next = (head)->tqh_first; \ ++ if ((elm)->field.tqe_next != NULL) { \ ++ (head)->tqh_first->field.tqe_circ.tql_prev = \ ++ &(elm)->field.tqe_circ; \ ++ } else { \ ++ (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ ++ } \ ++ qatomic_rcu_set(&(head)->tqh_first, (elm)); \ ++ (elm)->field.tqe_circ.tql_prev = &(head)->tqh_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_TAIL_RCU(head, elm, field) do { \ ++ (elm)->field.tqe_next = NULL; \ ++ (elm)->field.tqe_circ.tql_prev = (head)->tqh_circ.tql_prev; \ ++ qatomic_rcu_set(&(head)->tqh_circ.tql_prev->tql_next, (elm)); \ ++ (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_AFTER_RCU(head, listelm, elm, field) do { \ ++ (elm)->field.tqe_next = (listelm)->field.tqe_next; \ ++ if ((elm)->field.tqe_next != NULL) { \ ++ (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ ++ &(elm)->field.tqe_circ; \ ++ } else { \ ++ (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ ++ } \ ++ qatomic_rcu_set(&(listelm)->field.tqe_next, (elm)); \ ++ (elm)->field.tqe_circ.tql_prev = &(listelm)->field.tqe_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_BEFORE_RCU(listelm, elm, field) do { \ ++ (elm)->field.tqe_circ.tql_prev = (listelm)->field.tqe_circ.tql_prev; \ ++ (elm)->field.tqe_next = (listelm); \ ++ qatomic_rcu_set(&(listelm)->field.tqe_circ.tql_prev->tql_next, (elm));\ ++ (listelm)->field.tqe_circ.tql_prev = &(elm)->field.tqe_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_REMOVE_RCU(head, elm, field) do { \ ++ if (((elm)->field.tqe_next) != NULL) { \ ++ (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ ++ (elm)->field.tqe_circ.tql_prev; \ ++ } else { \ ++ (head)->tqh_circ.tql_prev = (elm)->field.tqe_circ.tql_prev; \ ++ } \ ++ qatomic_set(&(elm)->field.tqe_circ.tql_prev->tql_next, \ ++ (elm)->field.tqe_next); \ ++ (elm)->field.tqe_circ.tql_prev = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_FOREACH_RCU(var, head, field) \ ++ for ((var) = qatomic_rcu_read(&(head)->tqh_first); \ ++ (var); \ ++ (var) = qatomic_rcu_read(&(var)->field.tqe_next)) ++ ++#define QTAILQ_FOREACH_SAFE_RCU(var, head, field, next) \ ++ for ((var) = qatomic_rcu_read(&(head)->tqh_first); \ ++ (var) && ((next) = qatomic_rcu_read(&(var)->field.tqe_next), 1);\ ++ (var) = (next)) ++ ++/* ++ * RCU singly-linked list ++ */ ++ ++/* Singly-linked list access methods */ ++#define QSLIST_EMPTY_RCU(head) (qatomic_read(&(head)->slh_first) == NULL) ++#define QSLIST_FIRST_RCU(head) qatomic_rcu_read(&(head)->slh_first) ++#define QSLIST_NEXT_RCU(elm, field) qatomic_rcu_read(&(elm)->field.sle_next) ++ ++/* Singly-linked list functions */ ++#define QSLIST_INSERT_HEAD_RCU(head, elm, field) do { \ ++ (elm)->field.sle_next = (head)->slh_first; \ ++ qatomic_rcu_set(&(head)->slh_first, (elm)); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_INSERT_AFTER_RCU(head, listelm, elm, field) do { \ ++ (elm)->field.sle_next = (listelm)->field.sle_next; \ ++ qatomic_rcu_set(&(listelm)->field.sle_next, (elm)); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_REMOVE_HEAD_RCU(head, field) do { \ ++ qatomic_set(&(head)->slh_first, (head)->slh_first->field.sle_next);\ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_REMOVE_RCU(head, elm, type, field) do { \ ++ if ((head)->slh_first == (elm)) { \ ++ QSLIST_REMOVE_HEAD_RCU((head), field); \ ++ } else { \ ++ struct type *curr = (head)->slh_first; \ ++ while (curr->field.sle_next != (elm)) { \ ++ curr = curr->field.sle_next; \ ++ } \ ++ qatomic_set(&curr->field.sle_next, \ ++ curr->field.sle_next->field.sle_next); \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_FOREACH_RCU(var, head, field) \ ++ for ((var) = qatomic_rcu_read(&(head)->slh_first); \ ++ (var); \ ++ (var) = qatomic_rcu_read(&(var)->field.sle_next)) ++ ++#define QSLIST_FOREACH_SAFE_RCU(var, head, field, next) \ ++ for ((var) = qatomic_rcu_read(&(head)->slh_first); \ ++ (var) && ((next) = qatomic_rcu_read(&(var)->field.sle_next), 1); \ ++ (var) = (next)) ++ ++#endif /* QEMU_RCU_QUEUE_H */ +diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h +new file mode 100644 +index 00000000..d935fd80 +--- /dev/null ++++ b/include/qemu/sockets.h +@@ -0,0 +1,156 @@ ++/* headers to use the BSD sockets */ ++ ++#ifndef QEMU_SOCKETS_H ++#define QEMU_SOCKETS_H ++ ++#ifdef _WIN32 ++ ++int inet_aton(const char *cp, struct in_addr *ia); ++ ++#endif /* !_WIN32 */ ++ ++#include "qapi/qapi-types-sockets.h" ++ ++/* misc helpers */ ++bool fd_is_socket(int fd); ++int qemu_socket(int domain, int type, int protocol); ++ ++/** ++ * qemu_socketpair: ++ * @domain: specifies a communication domain, such as PF_UNIX ++ * @type: specifies the socket type. ++ * @protocol: specifies a particular protocol to be used with the socket ++ * @sv: an array to store the pair of socket created ++ * ++ * Creates an unnamed pair of connected sockets in the specified domain, ++ * of the specified type, and using the optionally specified protocol. ++ * And automatically set the close-on-exec flags on the returned sockets ++ * ++ * Return 0 on success. ++ */ ++int qemu_socketpair(int domain, int type, int protocol, int sv[2]); ++ ++int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); ++/* ++ * A variant of send(2) which handles partial send. ++ * ++ * Return the number of bytes transferred over the socket. ++ * Set errno if fewer than `count' bytes are sent. ++ * ++ * This function don't work with non-blocking socket's. ++ * Any of the possibilities with non-blocking socket's is bad: ++ * - return a short write (then name is wrong) ++ * - busy wait adding (errno == EAGAIN) to the loop ++ */ ++ssize_t qemu_send_full(int s, const void *buf, size_t count) ++ G_GNUC_WARN_UNUSED_RESULT; ++int socket_set_cork(int fd, int v); ++int socket_set_nodelay(int fd); ++void qemu_socket_set_block(int fd); ++int qemu_socket_try_set_nonblock(int fd); ++void qemu_socket_set_nonblock(int fd); ++int socket_set_fast_reuse(int fd); ++ ++#ifdef WIN32 ++/* Windows has different names for the same constants with the same values */ ++#define SHUT_RD 0 ++#define SHUT_WR 1 ++#define SHUT_RDWR 2 ++#endif ++ ++int inet_ai_family_from_address(InetSocketAddress *addr, ++ Error **errp); ++int inet_parse(InetSocketAddress *addr, const char *str, Error **errp); ++int inet_connect(const char *str, Error **errp); ++int inet_connect_saddr(InetSocketAddress *saddr, Error **errp); ++ ++NetworkAddressFamily inet_netfamily(int family); ++ ++int unix_listen(const char *path, Error **errp); ++int unix_connect(const char *path, Error **errp); ++ ++char *socket_uri(SocketAddress *addr); ++SocketAddress *socket_parse(const char *str, Error **errp); ++int socket_connect(SocketAddress *addr, Error **errp); ++int socket_listen(SocketAddress *addr, int num, Error **errp); ++void socket_listen_cleanup(int fd, Error **errp); ++int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp); ++ ++/* Old, ipv4 only bits. Don't use for new code. */ ++int convert_host_port(struct sockaddr_in *saddr, const char *host, ++ const char *port, Error **errp); ++int parse_host_port(struct sockaddr_in *saddr, const char *str, ++ Error **errp); ++int socket_init(void); ++ ++/** ++ * socket_sockaddr_to_address: ++ * @sa: socket address struct ++ * @salen: size of @sa struct ++ * @errp: pointer to uninitialized error object ++ * ++ * Get the string representation of the socket ++ * address. A pointer to the allocated address information ++ * struct will be returned, which the caller is required to ++ * release with a call qapi_free_SocketAddress() when no ++ * longer required. ++ * ++ * Returns: the socket address struct, or NULL on error ++ */ ++SocketAddress * ++socket_sockaddr_to_address(struct sockaddr_storage *sa, ++ socklen_t salen, ++ Error **errp); ++ ++/** ++ * socket_local_address: ++ * @fd: the socket file handle ++ * @errp: pointer to uninitialized error object ++ * ++ * Get the string representation of the local socket ++ * address. A pointer to the allocated address information ++ * struct will be returned, which the caller is required to ++ * release with a call qapi_free_SocketAddress() when no ++ * longer required. ++ * ++ * Returns: the socket address struct, or NULL on error ++ */ ++SocketAddress *socket_local_address(int fd, Error **errp); ++ ++/** ++ * socket_remote_address: ++ * @fd: the socket file handle ++ * @errp: pointer to uninitialized error object ++ * ++ * Get the string representation of the remote socket ++ * address. A pointer to the allocated address information ++ * struct will be returned, which the caller is required to ++ * release with a call qapi_free_SocketAddress() when no ++ * longer required. ++ * ++ * Returns: the socket address struct, or NULL on error ++ */ ++SocketAddress *socket_remote_address(int fd, Error **errp); ++ ++/** ++ * socket_address_flatten: ++ * @addr: the socket address to flatten ++ * ++ * Convert SocketAddressLegacy to SocketAddress. Caller is responsible ++ * for freeing with qapi_free_SocketAddress(). ++ * ++ * Returns: the argument converted to SocketAddress. ++ */ ++SocketAddress *socket_address_flatten(SocketAddressLegacy *addr); ++ ++/** ++ * socket_address_parse_named_fd: ++ * ++ * Modify @addr, replacing a named fd by its corresponding number. ++ * Needed for callers that plan to pass @addr to a context where the ++ * current monitor is not available. ++ * ++ * Return 0 on success. ++ */ ++int socket_address_parse_named_fd(SocketAddress *addr, Error **errp); ++#endif /* QEMU_SOCKETS_H */ +diff --git a/include/qemu/stats64.h b/include/qemu/stats64.h +new file mode 100644 +index 00000000..99b5cb72 +--- /dev/null ++++ b/include/qemu/stats64.h +@@ -0,0 +1,199 @@ ++/* ++ * Atomic operations on 64-bit quantities. ++ * ++ * Copyright (C) 2017 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_STATS64_H ++#define QEMU_STATS64_H ++ ++#include "qemu/atomic.h" ++ ++/* This provides atomic operations on 64-bit type, using a reader-writer ++ * spinlock on architectures that do not have 64-bit accesses. Even on ++ * those architectures, it tries hard not to take the lock. ++ */ ++ ++typedef struct Stat64 { ++#ifdef CONFIG_ATOMIC64 ++ aligned_uint64_t value; ++#else ++ uint32_t low, high; ++ uint32_t lock; ++#endif ++} Stat64; ++ ++#ifdef CONFIG_ATOMIC64 ++static inline void stat64_init(Stat64 *s, uint64_t value) ++{ ++ /* This is not guaranteed to be atomic! */ ++ *s = (Stat64) { value }; ++} ++ ++static inline uint64_t stat64_get(const Stat64 *s) ++{ ++ return qatomic_read__nocheck(&s->value); ++} ++ ++static inline void stat64_set(Stat64 *s, uint64_t value) ++{ ++ qatomic_set__nocheck(&s->value, value); ++} ++ ++static inline void stat64_add(Stat64 *s, uint64_t value) ++{ ++ qatomic_add(&s->value, value); ++} ++ ++static inline void stat64_min(Stat64 *s, uint64_t value) ++{ ++ uint64_t orig = qatomic_read__nocheck(&s->value); ++ while (orig > value) { ++ orig = qatomic_cmpxchg__nocheck(&s->value, orig, value); ++ } ++} ++ ++static inline void stat64_max(Stat64 *s, uint64_t value) ++{ ++ uint64_t orig = qatomic_read__nocheck(&s->value); ++ while (orig < value) { ++ orig = qatomic_cmpxchg__nocheck(&s->value, orig, value); ++ } ++} ++#else ++uint64_t stat64_get(const Stat64 *s); ++void stat64_set(Stat64 *s, uint64_t value); ++bool stat64_min_slow(Stat64 *s, uint64_t value); ++bool stat64_max_slow(Stat64 *s, uint64_t value); ++bool stat64_add32_carry(Stat64 *s, uint32_t low, uint32_t high); ++ ++static inline void stat64_init(Stat64 *s, uint64_t value) ++{ ++ /* This is not guaranteed to be atomic! */ ++ *s = (Stat64) { .low = value, .high = value >> 32, .lock = 0 }; ++} ++ ++static inline void stat64_add(Stat64 *s, uint64_t value) ++{ ++ uint32_t low, high; ++ high = value >> 32; ++ low = (uint32_t) value; ++ if (!low) { ++ if (high) { ++ qatomic_add(&s->high, high); ++ } ++ return; ++ } ++ ++ for (;;) { ++ uint32_t orig = s->low; ++ uint32_t result = orig + low; ++ uint32_t old; ++ ++ if (result < low || high) { ++ /* If the high part is affected, take the lock. */ ++ if (stat64_add32_carry(s, low, high)) { ++ return; ++ } ++ continue; ++ } ++ ++ /* No carry, try with a 32-bit cmpxchg. The result is independent of ++ * the high 32 bits, so it can race just fine with stat64_add32_carry ++ * and even stat64_get! ++ */ ++ old = qatomic_cmpxchg(&s->low, orig, result); ++ if (orig == old) { ++ return; ++ } ++ } ++} ++ ++static inline void stat64_min(Stat64 *s, uint64_t value) ++{ ++ uint32_t low, high; ++ uint32_t orig_low, orig_high; ++ ++ high = value >> 32; ++ low = (uint32_t) value; ++ do { ++ orig_high = qatomic_read(&s->high); ++ if (orig_high < high) { ++ return; ++ } ++ ++ if (orig_high == high) { ++ /* High 32 bits are equal. Read low after high, otherwise we ++ * can get a false positive (e.g. 0x1235,0x0000 changes to ++ * 0x1234,0x8000 and we read it as 0x1234,0x0000). Pairs with ++ * the write barrier in stat64_min_slow. ++ */ ++ smp_rmb(); ++ orig_low = qatomic_read(&s->low); ++ if (orig_low <= low) { ++ return; ++ } ++ ++ /* See if we were lucky and a writer raced against us. The ++ * barrier is theoretically unnecessary, but if we remove it ++ * we may miss being lucky. ++ */ ++ smp_rmb(); ++ orig_high = qatomic_read(&s->high); ++ if (orig_high < high) { ++ return; ++ } ++ } ++ ++ /* If the value changes in any way, we have to take the lock. */ ++ } while (!stat64_min_slow(s, value)); ++} ++ ++static inline void stat64_max(Stat64 *s, uint64_t value) ++{ ++ uint32_t low, high; ++ uint32_t orig_low, orig_high; ++ ++ high = value >> 32; ++ low = (uint32_t) value; ++ do { ++ orig_high = qatomic_read(&s->high); ++ if (orig_high > high) { ++ return; ++ } ++ ++ if (orig_high == high) { ++ /* High 32 bits are equal. Read low after high, otherwise we ++ * can get a false positive (e.g. 0x1234,0x8000 changes to ++ * 0x1235,0x0000 and we read it as 0x1235,0x8000). Pairs with ++ * the write barrier in stat64_max_slow. ++ */ ++ smp_rmb(); ++ orig_low = qatomic_read(&s->low); ++ if (orig_low >= low) { ++ return; ++ } ++ ++ /* See if we were lucky and a writer raced against us. The ++ * barrier is theoretically unnecessary, but if we remove it ++ * we may miss being lucky. ++ */ ++ smp_rmb(); ++ orig_high = qatomic_read(&s->high); ++ if (orig_high > high) { ++ return; ++ } ++ } ++ ++ /* If the value changes in any way, we have to take the lock. */ ++ } while (!stat64_max_slow(s, value)); ++} ++ ++#endif ++ ++#endif +diff --git a/include/qemu/sys_membarrier.h b/include/qemu/sys_membarrier.h +new file mode 100644 +index 00000000..e7774891 +--- /dev/null ++++ b/include/qemu/sys_membarrier.h +@@ -0,0 +1,27 @@ ++/* ++ * Process-global memory barriers ++ * ++ * Copyright (c) 2018 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ */ ++ ++#ifndef QEMU_SYS_MEMBARRIER_H ++#define QEMU_SYS_MEMBARRIER_H ++ ++#ifdef CONFIG_MEMBARRIER ++/* Only block reordering at the compiler level in the performance-critical ++ * side. The slow side forces processor-level ordering on all other cores ++ * through a system call. ++ */ ++void smp_mb_global_init(void); ++void smp_mb_global(void); ++#define smp_mb_placeholder() barrier() ++#else ++/* Keep it simple, execute a real memory barrier on both sides. */ ++static inline void smp_mb_global_init(void) {} ++#define smp_mb_global() smp_mb() ++#define smp_mb_placeholder() smp_mb() ++#endif ++ ++#endif +diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h +new file mode 100644 +index 00000000..5f2f3d13 +--- /dev/null ++++ b/include/qemu/thread-posix.h +@@ -0,0 +1,48 @@ ++#ifndef QEMU_THREAD_POSIX_H ++#define QEMU_THREAD_POSIX_H ++ ++#include ++#include ++ ++struct QemuMutex { ++ pthread_mutex_t lock; ++#ifdef CONFIG_DEBUG_MUTEX ++ const char *file; ++ int line; ++#endif ++ bool initialized; ++}; ++ ++/* ++ * QemuRecMutex cannot be a typedef of QemuMutex lest we have two ++ * compatible cases in _Generic. See qemu/lockable.h. ++ */ ++typedef struct QemuRecMutex { ++ QemuMutex m; ++} QemuRecMutex; ++ ++struct QemuCond { ++ pthread_cond_t cond; ++ bool initialized; ++}; ++ ++struct QemuSemaphore { ++ QemuMutex mutex; ++ QemuCond cond; ++ unsigned int count; ++}; ++ ++struct QemuEvent { ++#ifndef __linux__ ++ pthread_mutex_t lock; ++ pthread_cond_t cond; ++#endif ++ unsigned value; ++ bool initialized; ++}; ++ ++struct QemuThread { ++ pthread_t thread; ++}; ++ ++#endif +diff --git a/include/qemu/thread.h b/include/qemu/thread.h +new file mode 100644 +index 00000000..fb74e21c +--- /dev/null ++++ b/include/qemu/thread.h +@@ -0,0 +1,407 @@ ++#ifndef QEMU_THREAD_H ++#define QEMU_THREAD_H ++ ++#include "qemu/processor.h" ++#include "qemu/atomic.h" ++#include "qemu/clang-tsa.h" ++ ++typedef struct QemuCond QemuCond; ++typedef struct QemuSemaphore QemuSemaphore; ++typedef struct QemuEvent QemuEvent; ++typedef struct QemuLockCnt QemuLockCnt; ++typedef struct QemuThread QemuThread; ++ ++#ifdef _WIN32 ++#include "qemu/thread-win32.h" ++#else ++#include "qemu/thread-posix.h" ++#endif ++ ++/* include QSP header once QemuMutex, QemuCond etc. are defined */ ++#include "qemu/qsp.h" ++ ++#define QEMU_THREAD_JOINABLE 0 ++#define QEMU_THREAD_DETACHED 1 ++ ++void qemu_mutex_init(QemuMutex *mutex); ++void qemu_mutex_destroy(QemuMutex *mutex); ++int TSA_NO_TSA qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, ++ const int line); ++void TSA_NO_TSA qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, ++ const int line); ++void TSA_NO_TSA qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, ++ const int line); ++ ++void qemu_rec_mutex_init(QemuRecMutex *mutex); ++void qemu_rec_mutex_destroy(QemuRecMutex *mutex); ++void qemu_rec_mutex_lock_impl(QemuRecMutex *mutex, const char *file, int line); ++int qemu_rec_mutex_trylock_impl(QemuRecMutex *mutex, const char *file, int line); ++void qemu_rec_mutex_unlock_impl(QemuRecMutex *mutex, const char *file, int line); ++ ++typedef void (*QemuMutexLockFunc)(QemuMutex *m, const char *f, int l); ++typedef int (*QemuMutexTrylockFunc)(QemuMutex *m, const char *f, int l); ++typedef void (*QemuRecMutexLockFunc)(QemuRecMutex *m, const char *f, int l); ++typedef int (*QemuRecMutexTrylockFunc)(QemuRecMutex *m, const char *f, int l); ++typedef void (*QemuCondWaitFunc)(QemuCond *c, QemuMutex *m, const char *f, ++ int l); ++typedef bool (*QemuCondTimedWaitFunc)(QemuCond *c, QemuMutex *m, int ms, ++ const char *f, int l); ++ ++extern QemuMutexLockFunc bql_mutex_lock_func; ++extern QemuMutexLockFunc qemu_mutex_lock_func; ++extern QemuMutexTrylockFunc qemu_mutex_trylock_func; ++extern QemuRecMutexLockFunc qemu_rec_mutex_lock_func; ++extern QemuRecMutexTrylockFunc qemu_rec_mutex_trylock_func; ++extern QemuCondWaitFunc qemu_cond_wait_func; ++extern QemuCondTimedWaitFunc qemu_cond_timedwait_func; ++ ++/* convenience macros to bypass the profiler */ ++#define qemu_mutex_lock__raw(m) \ ++ qemu_mutex_lock_impl(m, __FILE__, __LINE__) ++#define qemu_mutex_trylock__raw(m) \ ++ qemu_mutex_trylock_impl(m, __FILE__, __LINE__) ++ ++#ifdef __COVERITY__ ++/* ++ * Coverity is severely confused by the indirect function calls, ++ * hide them. ++ */ ++#define qemu_mutex_lock(m) \ ++ qemu_mutex_lock_impl(m, __FILE__, __LINE__) ++#define qemu_mutex_trylock(m) \ ++ qemu_mutex_trylock_impl(m, __FILE__, __LINE__) ++#define qemu_rec_mutex_lock(m) \ ++ qemu_rec_mutex_lock_impl(m, __FILE__, __LINE__) ++#define qemu_rec_mutex_trylock(m) \ ++ qemu_rec_mutex_trylock_impl(m, __FILE__, __LINE__) ++#define qemu_cond_wait(c, m) \ ++ qemu_cond_wait_impl(c, m, __FILE__, __LINE__) ++#define qemu_cond_timedwait(c, m, ms) \ ++ qemu_cond_timedwait_impl(c, m, ms, __FILE__, __LINE__) ++#else ++#define qemu_mutex_lock(m) ({ \ ++ QemuMutexLockFunc _f = qatomic_read(&qemu_mutex_lock_func); \ ++ _f(m, __FILE__, __LINE__); \ ++ }) ++ ++#define qemu_mutex_trylock(m) ({ \ ++ QemuMutexTrylockFunc _f = qatomic_read(&qemu_mutex_trylock_func); \ ++ _f(m, __FILE__, __LINE__); \ ++ }) ++ ++#define qemu_rec_mutex_lock(m) ({ \ ++ QemuRecMutexLockFunc _f = qatomic_read(&qemu_rec_mutex_lock_func);\ ++ _f(m, __FILE__, __LINE__); \ ++ }) ++ ++#define qemu_rec_mutex_trylock(m) ({ \ ++ QemuRecMutexTrylockFunc _f; \ ++ _f = qatomic_read(&qemu_rec_mutex_trylock_func); \ ++ _f(m, __FILE__, __LINE__); \ ++ }) ++ ++#define qemu_cond_wait(c, m) ({ \ ++ QemuCondWaitFunc _f = qatomic_read(&qemu_cond_wait_func); \ ++ _f(c, m, __FILE__, __LINE__); \ ++ }) ++ ++#define qemu_cond_timedwait(c, m, ms) ({ \ ++ QemuCondTimedWaitFunc _f = qatomic_read(&qemu_cond_timedwait_func);\ ++ _f(c, m, ms, __FILE__, __LINE__); \ ++ }) ++#endif ++ ++#define qemu_mutex_unlock(mutex) \ ++ qemu_mutex_unlock_impl(mutex, __FILE__, __LINE__) ++ ++#define qemu_rec_mutex_unlock(mutex) \ ++ qemu_rec_mutex_unlock_impl(mutex, __FILE__, __LINE__) ++ ++static inline void (qemu_mutex_lock)(QemuMutex *mutex) ++{ ++ qemu_mutex_lock(mutex); ++} ++ ++static inline int (qemu_mutex_trylock)(QemuMutex *mutex) ++{ ++ return qemu_mutex_trylock(mutex); ++} ++ ++static inline void (qemu_mutex_unlock)(QemuMutex *mutex) ++{ ++ qemu_mutex_unlock(mutex); ++} ++ ++static inline void (qemu_rec_mutex_lock)(QemuRecMutex *mutex) ++{ ++ qemu_rec_mutex_lock(mutex); ++} ++ ++static inline int (qemu_rec_mutex_trylock)(QemuRecMutex *mutex) ++{ ++ return qemu_rec_mutex_trylock(mutex); ++} ++ ++static inline void (qemu_rec_mutex_unlock)(QemuRecMutex *mutex) ++{ ++ qemu_rec_mutex_unlock(mutex); ++} ++ ++void qemu_cond_init(QemuCond *cond); ++void qemu_cond_destroy(QemuCond *cond); ++ ++/* ++ * IMPORTANT: The implementation does not guarantee that pthread_cond_signal ++ * and pthread_cond_broadcast can be called except while the same mutex is ++ * held as in the corresponding pthread_cond_wait calls! ++ */ ++void qemu_cond_signal(QemuCond *cond); ++void qemu_cond_broadcast(QemuCond *cond); ++void TSA_NO_TSA qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, ++ const char *file, const int line); ++bool qemu_cond_timedwait_impl(QemuCond *cond, QemuMutex *mutex, int ms, ++ const char *file, const int line); ++ ++static inline void (qemu_cond_wait)(QemuCond *cond, QemuMutex *mutex) ++{ ++ qemu_cond_wait(cond, mutex); ++} ++ ++/* Returns true if timeout has not expired, and false otherwise */ ++static inline bool (qemu_cond_timedwait)(QemuCond *cond, QemuMutex *mutex, ++ int ms) ++{ ++ return qemu_cond_timedwait(cond, mutex, ms); ++} ++ ++void qemu_sem_init(QemuSemaphore *sem, int init); ++void qemu_sem_post(QemuSemaphore *sem); ++void qemu_sem_wait(QemuSemaphore *sem); ++int qemu_sem_timedwait(QemuSemaphore *sem, int ms); ++void qemu_sem_destroy(QemuSemaphore *sem); ++ ++void qemu_event_init(QemuEvent *ev, bool init); ++void qemu_event_set(QemuEvent *ev); ++void qemu_event_reset(QemuEvent *ev); ++void qemu_event_wait(QemuEvent *ev); ++void qemu_event_destroy(QemuEvent *ev); ++ ++void qemu_thread_create(QemuThread *thread, const char *name, ++ void *(*start_routine)(void *), ++ void *arg, int mode); ++int qemu_thread_set_affinity(QemuThread *thread, unsigned long *host_cpus, ++ unsigned long nbits); ++int qemu_thread_get_affinity(QemuThread *thread, unsigned long **host_cpus, ++ unsigned long *nbits); ++void *qemu_thread_join(QemuThread *thread); ++void qemu_thread_get_self(QemuThread *thread); ++bool qemu_thread_is_self(QemuThread *thread); ++G_NORETURN void qemu_thread_exit(void *retval); ++void qemu_thread_naming(bool enable); ++ ++struct Notifier; ++/** ++ * qemu_thread_atexit_add: ++ * @notifier: Notifier to add ++ * ++ * Add the specified notifier to a list which will be run via ++ * notifier_list_notify() when this thread exits (either by calling ++ * qemu_thread_exit() or by returning from its start_routine). ++ * The usual usage is that the caller passes a Notifier which is ++ * a per-thread variable; it can then use the callback to free ++ * other per-thread data. ++ * ++ * If the thread exits as part of the entire process exiting, ++ * it is unspecified whether notifiers are called or not. ++ */ ++void qemu_thread_atexit_add(struct Notifier *notifier); ++/** ++ * qemu_thread_atexit_remove: ++ * @notifier: Notifier to remove ++ * ++ * Remove the specified notifier from the thread-exit notification ++ * list. It is not valid to try to remove a notifier which is not ++ * on the list. ++ */ ++void qemu_thread_atexit_remove(struct Notifier *notifier); ++ ++#ifdef CONFIG_TSAN ++#include ++#endif ++ ++struct QemuSpin { ++ int value; ++}; ++ ++static inline void qemu_spin_init(QemuSpin *spin) ++{ ++ qatomic_set(&spin->value, 0); ++#ifdef CONFIG_TSAN ++ __tsan_mutex_create(spin, __tsan_mutex_not_static); ++#endif ++} ++ ++static inline void qemu_spin_destroy(QemuSpin *spin) ++{ ++#ifdef CONFIG_TSAN ++ __tsan_mutex_destroy(spin, __tsan_mutex_not_static); ++#endif ++} ++ ++static inline void qemu_spin_lock(QemuSpin *spin) ++{ ++#ifdef CONFIG_TSAN ++ __tsan_mutex_pre_lock(spin, 0); ++#endif ++ while (unlikely(qatomic_xchg(&spin->value, 1))) { ++ while (qatomic_read(&spin->value)) { ++ cpu_relax(); ++ } ++ } ++#ifdef CONFIG_TSAN ++ __tsan_mutex_post_lock(spin, 0, 0); ++#endif ++} ++ ++static inline bool qemu_spin_trylock(QemuSpin *spin) ++{ ++#ifdef CONFIG_TSAN ++ __tsan_mutex_pre_lock(spin, __tsan_mutex_try_lock); ++#endif ++ bool busy = qatomic_xchg(&spin->value, true); ++#ifdef CONFIG_TSAN ++ unsigned flags = __tsan_mutex_try_lock; ++ flags |= busy ? __tsan_mutex_try_lock_failed : 0; ++ __tsan_mutex_post_lock(spin, flags, 0); ++#endif ++ return busy; ++} ++ ++static inline bool qemu_spin_locked(QemuSpin *spin) ++{ ++ return qatomic_read(&spin->value); ++} ++ ++static inline void qemu_spin_unlock(QemuSpin *spin) ++{ ++#ifdef CONFIG_TSAN ++ __tsan_mutex_pre_unlock(spin, 0); ++#endif ++ qatomic_store_release(&spin->value, 0); ++#ifdef CONFIG_TSAN ++ __tsan_mutex_post_unlock(spin, 0); ++#endif ++} ++ ++struct QemuLockCnt { ++#ifndef CONFIG_LINUX ++ QemuMutex mutex; ++#endif ++ unsigned count; ++}; ++ ++/** ++ * qemu_lockcnt_init: initialize a QemuLockcnt ++ * @lockcnt: the lockcnt to initialize ++ * ++ * Initialize lockcnt's counter to zero and prepare its mutex ++ * for usage. ++ */ ++void qemu_lockcnt_init(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_destroy: destroy a QemuLockcnt ++ * @lockcnt: the lockcnt to destruct ++ * ++ * Destroy lockcnt's mutex. ++ */ ++void qemu_lockcnt_destroy(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_inc: increment a QemuLockCnt's counter ++ * @lockcnt: the lockcnt to operate on ++ * ++ * If the lockcnt's count is zero, wait for critical sections ++ * to finish and increment lockcnt's count to 1. If the count ++ * is not zero, just increment it. ++ * ++ * Because this function can wait on the mutex, it must not be ++ * called while the lockcnt's mutex is held by the current thread. ++ * For the same reason, qemu_lockcnt_inc can also contribute to ++ * AB-BA deadlocks. This is a sample deadlock scenario: ++ * ++ * thread 1 thread 2 ++ * ------------------------------------------------------- ++ * qemu_lockcnt_lock(&lc1); ++ * qemu_lockcnt_lock(&lc2); ++ * qemu_lockcnt_inc(&lc2); ++ * qemu_lockcnt_inc(&lc1); ++ */ ++void qemu_lockcnt_inc(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_dec: decrement a QemuLockCnt's counter ++ * @lockcnt: the lockcnt to operate on ++ */ ++void qemu_lockcnt_dec(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_dec_and_lock: decrement a QemuLockCnt's counter and ++ * possibly lock it. ++ * @lockcnt: the lockcnt to operate on ++ * ++ * Decrement lockcnt's count. If the new count is zero, lock ++ * the mutex and return true. Otherwise, return false. ++ */ ++bool qemu_lockcnt_dec_and_lock(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_dec_if_lock: possibly decrement a QemuLockCnt's counter and ++ * lock it. ++ * @lockcnt: the lockcnt to operate on ++ * ++ * If the count is 1, decrement the count to zero, lock ++ * the mutex and return true. Otherwise, return false. ++ */ ++bool qemu_lockcnt_dec_if_lock(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_lock: lock a QemuLockCnt's mutex. ++ * @lockcnt: the lockcnt to operate on ++ * ++ * Remember that concurrent visits are not blocked unless the count is ++ * also zero. You can use qemu_lockcnt_count to check for this inside a ++ * critical section. ++ */ ++void qemu_lockcnt_lock(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_unlock: release a QemuLockCnt's mutex. ++ * @lockcnt: the lockcnt to operate on. ++ */ ++void qemu_lockcnt_unlock(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_inc_and_unlock: combined unlock/increment on a QemuLockCnt. ++ * @lockcnt: the lockcnt to operate on. ++ * ++ * This is the same as ++ * ++ * qemu_lockcnt_unlock(lockcnt); ++ * qemu_lockcnt_inc(lockcnt); ++ * ++ * but more efficient. ++ */ ++void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_count: query a LockCnt's count. ++ * @lockcnt: the lockcnt to query. ++ * ++ * Note that the count can change at any time. Still, while the ++ * lockcnt is locked, one can usefully check whether the count ++ * is non-zero. ++ */ ++unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt); ++ ++#endif +diff --git a/include/qemu/timed-average.h b/include/qemu/timed-average.h +new file mode 100644 +index 00000000..08245e7a +--- /dev/null ++++ b/include/qemu/timed-average.h +@@ -0,0 +1,63 @@ ++/* ++ * QEMU timed average computation ++ * ++ * Copyright (C) Nodalink, EURL. 2014 ++ * Copyright (C) Igalia, S.L. 2015 ++ * ++ * Authors: ++ * Benoît Canet ++ * Alberto Garcia ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 2 of the License, or ++ * (at your option) version 3 or any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#ifndef TIMED_AVERAGE_H ++#define TIMED_AVERAGE_H ++ ++ ++#include "qemu/timer.h" ++ ++typedef struct TimedAverageWindow TimedAverageWindow; ++typedef struct TimedAverage TimedAverage; ++ ++/* All fields of both structures are private */ ++ ++struct TimedAverageWindow { ++ uint64_t min; /* minimum value accounted in the window */ ++ uint64_t max; /* maximum value accounted in the window */ ++ uint64_t sum; /* sum of all values */ ++ uint64_t count; /* number of values */ ++ int64_t expiration; /* the end of the current window in ns */ ++}; ++ ++struct TimedAverage { ++ uint64_t period; /* period in nanoseconds */ ++ TimedAverageWindow windows[2]; /* two overlapping windows of with ++ * an offset of period / 2 between them */ ++ unsigned current; /* the current window index: it's also the ++ * oldest window index */ ++ QEMUClockType clock_type; /* the clock used */ ++}; ++ ++void timed_average_init(TimedAverage *ta, QEMUClockType clock_type, ++ uint64_t period); ++ ++void timed_average_account(TimedAverage *ta, uint64_t value); ++ ++uint64_t timed_average_min(TimedAverage *ta); ++uint64_t timed_average_avg(TimedAverage *ta); ++uint64_t timed_average_max(TimedAverage *ta); ++uint64_t timed_average_sum(TimedAverage *ta, uint64_t *elapsed); ++ ++#endif +diff --git a/include/qemu/timer.h b/include/qemu/timer.h +new file mode 100644 +index 00000000..fa56ec94 +--- /dev/null ++++ b/include/qemu/timer.h +@@ -0,0 +1,1038 @@ ++#ifndef QEMU_TIMER_H ++#define QEMU_TIMER_H ++ ++#include "qemu/bitops.h" ++#include "qemu/notify.h" ++#include "qemu/host-utils.h" ++ ++#define NANOSECONDS_PER_SECOND 1000000000LL ++ ++/* timers */ ++ ++#define SCALE_MS 1000000 ++#define SCALE_US 1000 ++#define SCALE_NS 1 ++ ++/** ++ * QEMUClockType: ++ * ++ * The following clock types are available: ++ * ++ * @QEMU_CLOCK_REALTIME: Real time clock ++ * ++ * The real time clock should be used only for stuff which does not ++ * change the virtual machine state, as it runs even if the virtual ++ * machine is stopped. ++ * ++ * @QEMU_CLOCK_VIRTUAL: virtual clock ++ * ++ * The virtual clock only runs during the emulation. It stops ++ * when the virtual machine is stopped. ++ * ++ * @QEMU_CLOCK_HOST: host clock ++ * ++ * The host clock should be used for device models that emulate accurate ++ * real time sources. It will continue to run when the virtual machine ++ * is suspended, and it will reflect system time changes the host may ++ * undergo (e.g. due to NTP). ++ * ++ * @QEMU_CLOCK_VIRTUAL_RT: realtime clock used for icount warp ++ * ++ * Outside icount mode, this clock is the same as @QEMU_CLOCK_VIRTUAL. ++ * In icount mode, this clock counts nanoseconds while the virtual ++ * machine is running. It is used to increase @QEMU_CLOCK_VIRTUAL ++ * while the CPUs are sleeping and thus not executing instructions. ++ */ ++ ++typedef enum { ++ QEMU_CLOCK_REALTIME = 0, ++ QEMU_CLOCK_VIRTUAL = 1, ++ QEMU_CLOCK_HOST = 2, ++ QEMU_CLOCK_VIRTUAL_RT = 3, ++ QEMU_CLOCK_MAX ++} QEMUClockType; ++ ++/** ++ * QEMU Timer attributes: ++ * ++ * An individual timer may be given one or multiple attributes when initialized. ++ * Each attribute corresponds to one bit. Attributes modify the processing ++ * of timers when they fire. ++ * ++ * The following attributes are available: ++ * ++ * QEMU_TIMER_ATTR_EXTERNAL: drives external subsystem ++ * QEMU_TIMER_ATTR_ALL: mask for all existing attributes ++ * ++ * Timers with this attribute do not recorded in rr mode, therefore it could be ++ * used for the subsystems that operate outside the guest core. Applicable only ++ * with virtual clock type. ++ */ ++ ++#define QEMU_TIMER_ATTR_EXTERNAL ((int)BIT(0)) ++#define QEMU_TIMER_ATTR_ALL 0xffffffff ++ ++typedef struct QEMUTimerList QEMUTimerList; ++ ++struct QEMUTimerListGroup { ++ QEMUTimerList *tl[QEMU_CLOCK_MAX]; ++}; ++ ++typedef void QEMUTimerCB(void *opaque); ++typedef void QEMUTimerListNotifyCB(void *opaque, QEMUClockType type); ++ ++struct QEMUTimer { ++ int64_t expire_time; /* in nanoseconds */ ++ QEMUTimerList *timer_list; ++ QEMUTimerCB *cb; ++ void *opaque; ++ QEMUTimer *next; ++ int attributes; ++ int scale; ++}; ++ ++extern QEMUTimerListGroup main_loop_tlg; ++ ++/* ++ * qemu_clock_get_ns; ++ * @type: the clock type ++ * ++ * Get the nanosecond value of a clock with ++ * type @type ++ * ++ * Returns: the clock value in nanoseconds ++ */ ++int64_t qemu_clock_get_ns(QEMUClockType type); ++ ++/** ++ * qemu_clock_get_ms; ++ * @type: the clock type ++ * ++ * Get the millisecond value of a clock with ++ * type @type ++ * ++ * Returns: the clock value in milliseconds ++ */ ++static inline int64_t qemu_clock_get_ms(QEMUClockType type) ++{ ++ return qemu_clock_get_ns(type) / SCALE_MS; ++} ++ ++/** ++ * qemu_clock_get_us; ++ * @type: the clock type ++ * ++ * Get the microsecond value of a clock with ++ * type @type ++ * ++ * Returns: the clock value in microseconds ++ */ ++static inline int64_t qemu_clock_get_us(QEMUClockType type) ++{ ++ return qemu_clock_get_ns(type) / SCALE_US; ++} ++ ++/** ++ * qemu_clock_has_timers: ++ * @type: the clock type ++ * ++ * Determines whether a clock's default timer list ++ * has timers attached ++ * ++ * Note that this function should not be used when other threads also access ++ * the timer list. The return value may be outdated by the time it is acted ++ * upon. ++ * ++ * Returns: true if the clock's default timer list ++ * has timers attached ++ */ ++bool qemu_clock_has_timers(QEMUClockType type); ++ ++/** ++ * qemu_clock_expired: ++ * @type: the clock type ++ * ++ * Determines whether a clock's default timer list ++ * has an expired timer. ++ * ++ * Returns: true if the clock's default timer list has ++ * an expired timer ++ */ ++bool qemu_clock_expired(QEMUClockType type); ++ ++/** ++ * qemu_clock_use_for_deadline: ++ * @type: the clock type ++ * ++ * Determine whether a clock should be used for deadline ++ * calculations. Some clocks, for instance vm_clock with ++ * icount_enabled() set, do not count in nanoseconds. ++ * Such clocks are not used for deadline calculations, and are presumed ++ * to interrupt any poll using qemu_notify/aio_notify ++ * etc. ++ * ++ * Returns: true if the clock runs in nanoseconds and ++ * should be used for a deadline. ++ */ ++bool qemu_clock_use_for_deadline(QEMUClockType type); ++ ++/** ++ * qemu_clock_deadline_ns_all: ++ * @type: the clock type ++ * @attr_mask: mask for the timer attributes that are included ++ * in deadline calculation ++ * ++ * Calculate the deadline across all timer lists associated ++ * with a clock (as opposed to just the default one) ++ * in nanoseconds, or -1 if no timer is set to expire. ++ * ++ * Returns: time until expiry in nanoseconds or -1 ++ */ ++int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask); ++ ++/** ++ * qemu_clock_get_main_loop_timerlist: ++ * @type: the clock type ++ * ++ * Return the default timer list associated with a clock. ++ * ++ * Returns: the default timer list ++ */ ++QEMUTimerList *qemu_clock_get_main_loop_timerlist(QEMUClockType type); ++ ++/** ++ * qemu_clock_nofify: ++ * @type: the clock type ++ * ++ * Call the notifier callback connected with the default timer ++ * list linked to the clock, or qemu_notify() if none. ++ */ ++void qemu_clock_notify(QEMUClockType type); ++ ++/** ++ * qemu_clock_enable: ++ * @type: the clock type ++ * @enabled: true to enable, false to disable ++ * ++ * Enable or disable a clock ++ * Disabling the clock will wait for related timerlists to stop ++ * executing qemu_run_timers. Thus, this functions should not ++ * be used from the callback of a timer that is based on @clock. ++ * Doing so would cause a deadlock. ++ * ++ * Caller should hold BQL. ++ */ ++void qemu_clock_enable(QEMUClockType type, bool enabled); ++ ++/** ++ * qemu_clock_run_timers: ++ * @type: clock on which to operate ++ * ++ * Run all the timers associated with the default timer list ++ * of a clock. ++ * ++ * Returns: true if any timer ran. ++ */ ++bool qemu_clock_run_timers(QEMUClockType type); ++ ++/** ++ * qemu_clock_run_all_timers: ++ * ++ * Run all the timers associated with the default timer list ++ * of every clock. ++ * ++ * Returns: true if any timer ran. ++ */ ++bool qemu_clock_run_all_timers(void); ++ ++/** ++ * qemu_clock_advance_virtual_time(): advance the virtual time tick ++ * @target_ns: target time in nanoseconds ++ * ++ * This function is used where the control of the flow of time has ++ * been delegated to outside the clock subsystem (be it qtest, icount ++ * or some other external source). You can ask the clock system to ++ * return @early at the first expired timer. ++ * ++ * Time can only move forward, attempts to reverse time would lead to ++ * an error. ++ * ++ * Returns: new virtual time. ++ */ ++int64_t qemu_clock_advance_virtual_time(int64_t target_ns); ++ ++/* ++ * QEMUTimerList ++ */ ++ ++/** ++ * timerlist_new: ++ * @type: the clock type to associate with the timerlist ++ * @cb: the callback to call on notification ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Create a new timerlist associated with the clock of ++ * type @type. ++ * ++ * Returns: a pointer to the QEMUTimerList created ++ */ ++QEMUTimerList *timerlist_new(QEMUClockType type, ++ QEMUTimerListNotifyCB *cb, void *opaque); ++ ++/** ++ * timerlist_free: ++ * @timer_list: the timer list to free ++ * ++ * Frees a timer_list. It must have no active timers. ++ */ ++void timerlist_free(QEMUTimerList *timer_list); ++ ++/** ++ * timerlist_has_timers: ++ * @timer_list: the timer list to operate on ++ * ++ * Determine whether a timer list has active timers ++ * ++ * Note that this function should not be used when other threads also access ++ * the timer list. The return value may be outdated by the time it is acted ++ * upon. ++ * ++ * Returns: true if the timer list has timers. ++ */ ++bool timerlist_has_timers(QEMUTimerList *timer_list); ++ ++/** ++ * timerlist_expired: ++ * @timer_list: the timer list to operate on ++ * ++ * Determine whether a timer list has any timers which ++ * are expired. ++ * ++ * Returns: true if the timer list has timers which ++ * have expired. ++ */ ++bool timerlist_expired(QEMUTimerList *timer_list); ++ ++/** ++ * timerlist_deadline_ns: ++ * @timer_list: the timer list to operate on ++ * ++ * Determine the deadline for a timer_list, i.e. ++ * the number of nanoseconds until the first timer ++ * expires. Return -1 if there are no timers. ++ * ++ * Returns: the number of nanoseconds until the earliest ++ * timer expires -1 if none ++ */ ++int64_t timerlist_deadline_ns(QEMUTimerList *timer_list); ++ ++/** ++ * timerlist_get_clock: ++ * @timer_list: the timer list to operate on ++ * ++ * Determine the clock type associated with a timer list. ++ * ++ * Returns: the clock type associated with the ++ * timer list. ++ */ ++QEMUClockType timerlist_get_clock(QEMUTimerList *timer_list); ++ ++/** ++ * timerlist_run_timers: ++ * @timer_list: the timer list to use ++ * ++ * Call all expired timers associated with the timer list. ++ * ++ * Returns: true if any timer expired ++ */ ++bool timerlist_run_timers(QEMUTimerList *timer_list); ++ ++/** ++ * timerlist_notify: ++ * @timer_list: the timer list to use ++ * ++ * call the notifier callback associated with the timer list. ++ */ ++void timerlist_notify(QEMUTimerList *timer_list); ++ ++/* ++ * QEMUTimerListGroup ++ */ ++ ++/** ++ * timerlistgroup_init: ++ * @tlg: the timer list group ++ * @cb: the callback to call when a notify is required ++ * @opaque: the opaque pointer to be passed to the callback. ++ * ++ * Initialise a timer list group. This must already be ++ * allocated in memory and zeroed. The notifier callback is ++ * called whenever a clock in the timer list group is ++ * reenabled or whenever a timer associated with any timer ++ * list is modified. If @cb is specified as null, qemu_notify() ++ * is used instead. ++ */ ++void timerlistgroup_init(QEMUTimerListGroup *tlg, ++ QEMUTimerListNotifyCB *cb, void *opaque); ++ ++/** ++ * timerlistgroup_deinit: ++ * @tlg: the timer list group ++ * ++ * Deinitialise a timer list group. This must already be ++ * initialised. Note the memory is not freed. ++ */ ++void timerlistgroup_deinit(QEMUTimerListGroup *tlg); ++ ++/** ++ * timerlistgroup_run_timers: ++ * @tlg: the timer list group ++ * ++ * Run the timers associated with a timer list group. ++ * This will run timers on multiple clocks. ++ * ++ * Returns: true if any timer callback ran ++ */ ++bool timerlistgroup_run_timers(QEMUTimerListGroup *tlg); ++ ++/** ++ * timerlistgroup_deadline_ns: ++ * @tlg: the timer list group ++ * ++ * Determine the deadline of the soonest timer to ++ * expire associated with any timer list linked to ++ * the timer list group. Only clocks suitable for ++ * deadline calculation are included. ++ * ++ * Returns: the deadline in nanoseconds or -1 if no ++ * timers are to expire. ++ */ ++int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg); ++ ++/* ++ * QEMUTimer ++ */ ++ ++/** ++ * timer_init_full: ++ * @ts: the timer to be initialised ++ * @timer_list_group: (optional) the timer list group to attach the timer to ++ * @type: the clock type to use ++ * @scale: the scale value for the timer ++ * @attributes: 0, or one or more OR'ed QEMU_TIMER_ATTR_ values ++ * @cb: the callback to be called when the timer expires ++ * @opaque: the opaque pointer to be passed to the callback ++ * ++ * Initialise a timer with the given scale and attributes, ++ * and associate it with timer list for given clock @type in @timer_list_group ++ * (or default timer list group, if NULL). ++ * The caller is responsible for allocating the memory. ++ * ++ * You need not call an explicit deinit call. Simply make ++ * sure it is not on a list with timer_del. ++ */ ++void timer_init_full(QEMUTimer *ts, ++ QEMUTimerListGroup *timer_list_group, QEMUClockType type, ++ int scale, int attributes, ++ QEMUTimerCB *cb, void *opaque); ++ ++/** ++ * timer_init: ++ * @ts: the timer to be initialised ++ * @type: the clock to associate with the timer ++ * @scale: the scale value for the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Initialize a timer with the given scale on the default timer list ++ * associated with the clock. ++ * See timer_init_full for details. ++ */ ++static inline void timer_init(QEMUTimer *ts, QEMUClockType type, int scale, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ timer_init_full(ts, NULL, type, scale, 0, cb, opaque); ++} ++ ++/** ++ * timer_init_ns: ++ * @ts: the timer to be initialised ++ * @type: the clock to associate with the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Initialize a timer with nanosecond scale on the default timer list ++ * associated with the clock. ++ * See timer_init_full for details. ++ */ ++static inline void timer_init_ns(QEMUTimer *ts, QEMUClockType type, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ timer_init(ts, type, SCALE_NS, cb, opaque); ++} ++ ++/** ++ * timer_init_us: ++ * @ts: the timer to be initialised ++ * @type: the clock to associate with the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Initialize a timer with microsecond scale on the default timer list ++ * associated with the clock. ++ * See timer_init_full for details. ++ */ ++static inline void timer_init_us(QEMUTimer *ts, QEMUClockType type, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ timer_init(ts, type, SCALE_US, cb, opaque); ++} ++ ++/** ++ * timer_init_ms: ++ * @ts: the timer to be initialised ++ * @type: the clock to associate with the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Initialize a timer with millisecond scale on the default timer list ++ * associated with the clock. ++ * See timer_init_full for details. ++ */ ++static inline void timer_init_ms(QEMUTimer *ts, QEMUClockType type, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ timer_init(ts, type, SCALE_MS, cb, opaque); ++} ++ ++/** ++ * timer_new_full: ++ * @timer_list_group: (optional) the timer list group to attach the timer to ++ * @type: the clock type to use ++ * @scale: the scale value for the timer ++ * @attributes: 0, or one or more OR'ed QEMU_TIMER_ATTR_ values ++ * @cb: the callback to be called when the timer expires ++ * @opaque: the opaque pointer to be passed to the callback ++ * ++ * Create a new timer with the given scale and attributes, ++ * and associate it with timer list for given clock @type in @timer_list_group ++ * (or default timer list group, if NULL). ++ * The memory is allocated by the function. ++ * ++ * This is not the preferred interface unless you know you ++ * are going to call timer_free. Use timer_init or timer_init_full instead. ++ * ++ * The default timer list has one special feature: in icount mode, ++ * %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is ++ * not true of other timer lists, which are typically associated ++ * with an AioContext---each of them runs its timer callbacks in its own ++ * AioContext thread. ++ * ++ * Returns: a pointer to the timer ++ */ ++static inline QEMUTimer *timer_new_full(QEMUTimerListGroup *timer_list_group, ++ QEMUClockType type, ++ int scale, int attributes, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ QEMUTimer *ts = g_new0(QEMUTimer, 1); ++ timer_init_full(ts, timer_list_group, type, scale, attributes, cb, opaque); ++ return ts; ++} ++ ++/** ++ * timer_new: ++ * @type: the clock type to use ++ * @scale: the scale value for the timer ++ * @cb: the callback to be called when the timer expires ++ * @opaque: the opaque pointer to be passed to the callback ++ * ++ * Create a new timer with the given scale, ++ * and associate it with the default timer list for the clock type @type. ++ * See timer_new_full for details. ++ * ++ * Returns: a pointer to the timer ++ */ ++static inline QEMUTimer *timer_new(QEMUClockType type, int scale, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ return timer_new_full(NULL, type, scale, 0, cb, opaque); ++} ++ ++/** ++ * timer_new_ns: ++ * @type: the clock type to associate with the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Create a new timer with nanosecond scale on the default timer list ++ * associated with the clock. ++ * See timer_new_full for details. ++ * ++ * Returns: a pointer to the newly created timer ++ */ ++static inline QEMUTimer *timer_new_ns(QEMUClockType type, QEMUTimerCB *cb, ++ void *opaque) ++{ ++ return timer_new(type, SCALE_NS, cb, opaque); ++} ++ ++/** ++ * timer_new_us: ++ * @type: the clock type to associate with the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Create a new timer with microsecond scale on the default timer list ++ * associated with the clock. ++ * See timer_new_full for details. ++ * ++ * Returns: a pointer to the newly created timer ++ */ ++static inline QEMUTimer *timer_new_us(QEMUClockType type, QEMUTimerCB *cb, ++ void *opaque) ++{ ++ return timer_new(type, SCALE_US, cb, opaque); ++} ++ ++/** ++ * timer_new_ms: ++ * @type: the clock type to associate with the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Create a new timer with millisecond scale on the default timer list ++ * associated with the clock. ++ * See timer_new_full for details. ++ * ++ * Returns: a pointer to the newly created timer ++ */ ++static inline QEMUTimer *timer_new_ms(QEMUClockType type, QEMUTimerCB *cb, ++ void *opaque) ++{ ++ return timer_new(type, SCALE_MS, cb, opaque); ++} ++ ++/** ++ * timer_deinit: ++ * @ts: the timer to be de-initialised ++ * ++ * Deassociate the timer from any timerlist. You should ++ * call timer_del before. After this call, any further ++ * timer_del call cannot cause dangling pointer accesses ++ * even if the previously used timerlist is freed. ++ */ ++void timer_deinit(QEMUTimer *ts); ++ ++/** ++ * timer_del: ++ * @ts: the timer ++ * ++ * Delete a timer from the active list. ++ * ++ * This function is thread-safe but the timer and its timer list must not be ++ * freed while this function is running. ++ */ ++void timer_del(QEMUTimer *ts); ++ ++/** ++ * timer_free: ++ * @ts: the timer ++ * ++ * Free a timer. This will call timer_del() for you to remove ++ * the timer from the active list if it was still active. ++ */ ++static inline void timer_free(QEMUTimer *ts) ++{ ++ if (ts) { ++ timer_del(ts); ++ g_free(ts); ++ } ++} ++ ++/** ++ * timer_mod_ns: ++ * @ts: the timer ++ * @expire_time: the expiry time in nanoseconds ++ * ++ * Modify a timer to expire at @expire_time ++ * ++ * This function is thread-safe but the timer and its timer list must not be ++ * freed while this function is running. ++ */ ++void timer_mod_ns(QEMUTimer *ts, int64_t expire_time); ++ ++/** ++ * timer_mod_anticipate_ns: ++ * @ts: the timer ++ * @expire_time: the expiry time in nanoseconds ++ * ++ * Modify a timer to expire at @expire_time or the current time, ++ * whichever comes earlier. ++ * ++ * This function is thread-safe but the timer and its timer list must not be ++ * freed while this function is running. ++ */ ++void timer_mod_anticipate_ns(QEMUTimer *ts, int64_t expire_time); ++ ++/** ++ * timer_mod: ++ * @ts: the timer ++ * @expire_time: the expire time in the units associated with the timer ++ * ++ * Modify a timer to expiry at @expire_time, taking into ++ * account the scale associated with the timer. ++ * ++ * This function is thread-safe but the timer and its timer list must not be ++ * freed while this function is running. ++ */ ++void timer_mod(QEMUTimer *ts, int64_t expire_timer); ++ ++/** ++ * timer_mod_anticipate: ++ * @ts: the timer ++ * @expire_time: the expire time in the units associated with the timer ++ * ++ * Modify a timer to expire at @expire_time or the current time, whichever ++ * comes earlier, taking into account the scale associated with the timer. ++ * ++ * This function is thread-safe but the timer and its timer list must not be ++ * freed while this function is running. ++ */ ++void timer_mod_anticipate(QEMUTimer *ts, int64_t expire_time); ++ ++/** ++ * timer_pending: ++ * @ts: the timer ++ * ++ * Determines whether a timer is pending (i.e. is on the ++ * active list of timers, whether or not it has not yet expired). ++ * ++ * Returns: true if the timer is pending ++ */ ++bool timer_pending(QEMUTimer *ts); ++ ++/** ++ * timer_expired: ++ * @ts: the timer ++ * @current_time: the current time ++ * ++ * Determines whether a timer has expired. ++ * ++ * Returns: true if the timer has expired ++ */ ++bool timer_expired(QEMUTimer *timer_head, int64_t current_time); ++ ++/** ++ * timer_expire_time_ns: ++ * @ts: the timer ++ * ++ * Determine the expiry time of a timer ++ * ++ * Returns: the expiry time in nanoseconds ++ */ ++uint64_t timer_expire_time_ns(QEMUTimer *ts); ++ ++/** ++ * timer_get: ++ * @f: the file ++ * @ts: the timer ++ * ++ * Read a timer @ts from a file @f ++ */ ++void timer_get(QEMUFile *f, QEMUTimer *ts); ++ ++/** ++ * timer_put: ++ * @f: the file ++ * @ts: the timer ++ */ ++void timer_put(QEMUFile *f, QEMUTimer *ts); ++ ++/* ++ * General utility functions ++ */ ++ ++/** ++ * qemu_timeout_ns_to_ms: ++ * @ns: nanosecond timeout value ++ * ++ * Convert a nanosecond timeout value (or -1) to ++ * a millisecond value (or -1), always rounding up. ++ * ++ * Returns: millisecond timeout value ++ */ ++int qemu_timeout_ns_to_ms(int64_t ns); ++ ++/** ++ * qemu_poll_ns: ++ * @fds: Array of file descriptors ++ * @nfds: number of file descriptors ++ * @timeout: timeout in nanoseconds ++ * ++ * Perform a poll like g_poll but with a timeout in nanoseconds. ++ * See g_poll documentation for further details. ++ * ++ * Returns: number of fds ready ++ */ ++int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t timeout); ++ ++/** ++ * qemu_soonest_timeout: ++ * @timeout1: first timeout in nanoseconds (or -1 for infinite) ++ * @timeout2: second timeout in nanoseconds (or -1 for infinite) ++ * ++ * Calculates the soonest of two timeout values. -1 means infinite, which ++ * is later than any other value. ++ * ++ * Returns: soonest timeout value in nanoseconds (or -1 for infinite) ++ */ ++static inline int64_t qemu_soonest_timeout(int64_t timeout1, int64_t timeout2) ++{ ++ /* we can abuse the fact that -1 (which means infinite) is a maximal ++ * value when cast to unsigned. As this is disgusting, it's kept in ++ * one inline function. ++ */ ++ return ((uint64_t) timeout1 < (uint64_t) timeout2) ? timeout1 : timeout2; ++} ++ ++/** ++ * initclocks: ++ * ++ * Initialise the clock & timer infrastructure ++ */ ++void init_clocks(QEMUTimerListNotifyCB *notify_cb); ++ ++static inline int64_t get_max_clock_jump(void) ++{ ++ /* This should be small enough to prevent excessive interrupts from being ++ * generated by the RTC on clock jumps, but large enough to avoid frequent ++ * unnecessary resets in idle VMs. ++ */ ++ return 60 * NANOSECONDS_PER_SECOND; ++} ++ ++/* ++ * Low level clock functions ++ */ ++ ++/* get host real time in nanosecond */ ++static inline int64_t get_clock_realtime(void) ++{ ++ struct timeval tv; ++ ++ gettimeofday(&tv, NULL); ++ return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000); ++} ++ ++extern int64_t clock_start; ++ ++/* Warning: don't insert tracepoints into these functions, they are ++ also used by simpletrace backend and tracepoints would cause ++ an infinite recursion! */ ++#ifdef _WIN32 ++extern int64_t clock_freq; ++ ++static inline int64_t get_clock(void) ++{ ++ LARGE_INTEGER ti; ++ QueryPerformanceCounter(&ti); ++ return muldiv64(ti.QuadPart, NANOSECONDS_PER_SECOND, clock_freq); ++} ++ ++#else ++ ++extern int use_rt_clock; ++ ++static inline int64_t get_clock(void) ++{ ++ if (use_rt_clock) { ++ struct timespec ts; ++ clock_gettime(CLOCK_MONOTONIC, &ts); ++ return ts.tv_sec * 1000000000LL + ts.tv_nsec; ++ } else { ++ /* XXX: using gettimeofday leads to problems if the date ++ changes, so it should be avoided. */ ++ return get_clock_realtime(); ++ } ++} ++#endif ++ ++/*******************************************/ ++/* host CPU ticks (if available) */ ++ ++#if defined(_ARCH_PPC) ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ int64_t retval; ++#ifdef _ARCH_PPC64 ++ /* This reads timebase in one 64bit go and includes Cell workaround from: ++ http://ozlabs.org/pipermail/linuxppc-dev/2006-October/027052.html ++ */ ++ __asm__ __volatile__ ("mftb %0\n\t" ++ "cmpwi %0,0\n\t" ++ "beq- $-8" ++ : "=r" (retval)); ++#else ++ /* http://ozlabs.org/pipermail/linuxppc-dev/1999-October/003889.html */ ++ unsigned long junk; ++ __asm__ __volatile__ ("mfspr %1,269\n\t" /* mftbu */ ++ "mfspr %L0,268\n\t" /* mftb */ ++ "mfspr %0,269\n\t" /* mftbu */ ++ "cmpw %0,%1\n\t" ++ "bne $-16" ++ : "=r" (retval), "=r" (junk)); ++#endif ++ return retval; ++} ++ ++#elif defined(__i386__) ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ int64_t val; ++ asm volatile ("rdtsc" : "=A" (val)); ++ return val; ++} ++ ++#elif defined(__x86_64__) ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ uint32_t low,high; ++ int64_t val; ++ asm volatile("rdtsc" : "=a" (low), "=d" (high)); ++ val = high; ++ val <<= 32; ++ val |= low; ++ return val; ++} ++ ++#elif defined(__hppa__) ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ int val; ++ asm volatile ("mfctl %%cr16, %0" : "=r"(val)); ++ return val; ++} ++ ++#elif defined(__s390__) ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ int64_t val; ++ asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc"); ++ return val; ++} ++ ++#elif defined(__sparc__) ++ ++static inline int64_t cpu_get_host_ticks (void) ++{ ++#if defined(_LP64) ++ uint64_t rval; ++ asm volatile("rd %%tick,%0" : "=r"(rval)); ++ return rval; ++#else ++ /* We need an %o or %g register for this. For recent enough gcc ++ there is an "h" constraint for that. Don't bother with that. */ ++ union { ++ uint64_t i64; ++ struct { ++ uint32_t high; ++ uint32_t low; ++ } i32; ++ } rval; ++ asm volatile("rd %%tick,%%g1; srlx %%g1,32,%0; mov %%g1,%1" ++ : "=r"(rval.i32.high), "=r"(rval.i32.low) : : "g1"); ++ return rval.i64; ++#endif ++} ++ ++#elif defined(__mips__) && \ ++ ((defined(__mips_isa_rev) && __mips_isa_rev >= 2) || defined(__linux__)) ++/* ++ * binutils wants to use rdhwr only on mips32r2 ++ * but as linux kernel emulate it, it's fine ++ * to use it. ++ * ++ */ ++#define MIPS_RDHWR(rd, value) { \ ++ __asm__ __volatile__ (".set push\n\t" \ ++ ".set mips32r2\n\t" \ ++ "rdhwr %0, "rd"\n\t" \ ++ ".set pop" \ ++ : "=r" (value)); \ ++ } ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ /* On kernels >= 2.6.25 rdhwr , $2 and $3 are emulated */ ++ uint32_t count; ++ static uint32_t cyc_per_count = 0; ++ ++ if (!cyc_per_count) { ++ MIPS_RDHWR("$3", cyc_per_count); ++ } ++ ++ MIPS_RDHWR("$2", count); ++ return (int64_t)(count * cyc_per_count); ++} ++ ++#elif defined(__alpha__) ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ uint64_t cc; ++ uint32_t cur, ofs; ++ ++ asm volatile("rpcc %0" : "=r"(cc)); ++ cur = cc; ++ ofs = cc >> 32; ++ return cur - ofs; ++} ++ ++#elif defined(__riscv) && __riscv_xlen == 32 ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ uint32_t lo, hi, tmph; ++ do { ++ asm volatile("RDTIMEH %0\n\t" ++ "RDTIME %1\n\t" ++ "RDTIMEH %2" ++ : "=r"(hi), "=r"(lo), "=r"(tmph)); ++ } while (unlikely(tmph != hi)); ++ return lo | (uint64_t)hi << 32; ++} ++ ++#elif defined(__riscv) && __riscv_xlen > 32 ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ int64_t val; ++ ++ asm volatile("RDTIME %0" : "=r"(val)); ++ return val; ++} ++ ++#elif defined(__loongarch64) ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ uint64_t val; ++ ++ asm volatile("rdtime.d %0, $zero" : "=r"(val)); ++ return val; ++} ++ ++#else ++/* The host CPU doesn't have an easily accessible cycle counter. ++ Just return a monotonically increasing value. This will be ++ totally wrong, but hopefully better than nothing. */ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ return get_clock(); ++} ++#endif ++ ++#endif +diff --git a/include/qemu/transactions.h b/include/qemu/transactions.h +new file mode 100644 +index 00000000..2f2060ac +--- /dev/null ++++ b/include/qemu/transactions.h +@@ -0,0 +1,66 @@ ++/* ++ * Simple transactions API ++ * ++ * Copyright (c) 2021 Virtuozzo International GmbH. ++ * ++ * Author: ++ * Vladimir Sementsov-Ogievskiy ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * ++ * = Generic transaction API = ++ * ++ * The intended usage is the following: you create "prepare" functions, which ++ * represents the actions. They will usually have Transaction* argument, and ++ * call tran_add() to register finalization callbacks. For finalization ++ * callbacks, prepare corresponding TransactionActionDrv structures. ++ * ++ * Then, when you need to make a transaction, create an empty Transaction by ++ * tran_create(), call your "prepare" functions on it, and finally call ++ * tran_abort() or tran_commit() to finalize the transaction by corresponding ++ * finalization actions in reverse order. ++ * ++ * The clean() functions registered by the drivers in a transaction are called ++ * last, after all abort() or commit() functions have been called. ++ */ ++ ++#ifndef QEMU_TRANSACTIONS_H ++#define QEMU_TRANSACTIONS_H ++ ++#include ++ ++typedef struct TransactionActionDrv { ++ void (*abort)(void *opaque); ++ void (*commit)(void *opaque); ++ void (*clean)(void *opaque); ++} TransactionActionDrv; ++ ++typedef struct Transaction Transaction; ++ ++Transaction *tran_new(void); ++void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque); ++void tran_abort(Transaction *tran); ++void tran_commit(Transaction *tran); ++ ++static inline void tran_finalize(Transaction *tran, int ret) ++{ ++ if (ret < 0) { ++ tran_abort(tran); ++ } else { ++ tran_commit(tran); ++ } ++} ++ ++#endif /* QEMU_TRANSACTIONS_H */ +diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h +new file mode 100644 +index 00000000..9d222dc3 +--- /dev/null ++++ b/include/qemu/typedefs.h +@@ -0,0 +1,134 @@ ++#ifndef QEMU_TYPEDEFS_H ++#define QEMU_TYPEDEFS_H ++ ++/* ++ * This header is for selectively avoiding #include just to get a ++ * typedef name. ++ * ++ * Declaring a typedef name in its "obvious" place can result in ++ * inclusion cycles, in particular for complete struct and union ++ * types that need more types for their members. It can also result ++ * in headers pulling in many more headers, slowing down builds. ++ * ++ * You can break such cycles and unwanted dependencies by declaring ++ * the typedef name here. ++ * ++ * For struct types used in only a few headers, judicious use of the ++ * struct tag instead of the typedef name is commonly preferable. ++ */ ++ ++/* ++ * Incomplete struct types ++ * Please keep this list in case-insensitive alphabetical order. ++ */ ++typedef struct AccelCPUState AccelCPUState; ++typedef struct AccelState AccelState; ++typedef struct AddressSpace AddressSpace; ++typedef struct AioContext AioContext; ++typedef struct Aml Aml; ++typedef struct ArchCPU ArchCPU; ++typedef struct BdrvDirtyBitmap BdrvDirtyBitmap; ++typedef struct BdrvDirtyBitmapIter BdrvDirtyBitmapIter; ++typedef struct BlockBackend BlockBackend; ++typedef struct BlockBackendRootState BlockBackendRootState; ++typedef struct BlockDriverState BlockDriverState; ++typedef struct BusClass BusClass; ++typedef struct BusState BusState; ++typedef struct Chardev Chardev; ++typedef struct Clock Clock; ++typedef struct ConfidentialGuestSupport ConfidentialGuestSupport; ++typedef struct CPUArchState CPUArchState; ++typedef struct CPUPluginState CPUPluginState; ++typedef struct CPUState CPUState; ++typedef struct DeviceState DeviceState; ++typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot; ++typedef struct DisasContextBase DisasContextBase; ++typedef struct DisplayChangeListener DisplayChangeListener; ++typedef struct DriveInfo DriveInfo; ++typedef struct DumpState DumpState; ++typedef struct Error Error; ++typedef struct EventNotifier EventNotifier; ++typedef struct FlatView FlatView; ++typedef struct FWCfgState FWCfgState; ++typedef struct HostMemoryBackend HostMemoryBackend; ++typedef struct I2CBus I2CBus; ++typedef struct I2SCodec I2SCodec; ++typedef struct IOMMUMemoryRegion IOMMUMemoryRegion; ++typedef struct ISABus ISABus; ++typedef struct ISADevice ISADevice; ++typedef struct IsaDma IsaDma; ++typedef struct JSONWriter JSONWriter; ++typedef struct MACAddr MACAddr; ++typedef struct MachineClass MachineClass; ++typedef struct MachineState MachineState; ++typedef struct MemoryListener MemoryListener; ++typedef struct MemoryMappingList MemoryMappingList; ++typedef struct MemoryRegion MemoryRegion; ++typedef struct MemoryRegionCache MemoryRegionCache; ++typedef struct MemoryRegionSection MemoryRegionSection; ++typedef struct MigrationIncomingState MigrationIncomingState; ++typedef struct MigrationState MigrationState; ++typedef struct Monitor Monitor; ++typedef struct MSIMessage MSIMessage; ++typedef struct NetClientState NetClientState; ++typedef struct NetFilterState NetFilterState; ++typedef struct NICInfo NICInfo; ++typedef struct Object Object; ++typedef struct ObjectClass ObjectClass; ++typedef struct PCIBridge PCIBridge; ++typedef struct PCIBus PCIBus; ++typedef struct PCIDevice PCIDevice; ++typedef struct PCIEPort PCIEPort; ++typedef struct PCIESlot PCIESlot; ++typedef struct PCIExpressDevice PCIExpressDevice; ++typedef struct PCIExpressHost PCIExpressHost; ++typedef struct PCIHostDeviceAddress PCIHostDeviceAddress; ++typedef struct PCIHostState PCIHostState; ++typedef struct Property Property; ++typedef struct PropertyInfo PropertyInfo; ++typedef struct QBool QBool; ++typedef struct QDict QDict; ++typedef struct QEMUBH QEMUBH; ++typedef struct QemuConsole QemuConsole; ++typedef struct QEMUCursor QEMUCursor; ++typedef struct QEMUFile QEMUFile; ++typedef struct QemuMutex QemuMutex; ++typedef struct QemuOpts QemuOpts; ++typedef struct QemuOptsList QemuOptsList; ++typedef struct QEMUSGList QEMUSGList; ++typedef struct QemuSpin QemuSpin; ++typedef struct QEMUTimer QEMUTimer; ++typedef struct QEMUTimerListGroup QEMUTimerListGroup; ++typedef struct QList QList; ++typedef struct QNull QNull; ++typedef struct QNum QNum; ++typedef struct QObject QObject; ++typedef struct QString QString; ++typedef struct RAMBlock RAMBlock; ++typedef struct Range Range; ++typedef struct ReservedRegion ReservedRegion; ++typedef struct SHPCDevice SHPCDevice; ++typedef struct SSIBus SSIBus; ++typedef struct TCGCPUOps TCGCPUOps; ++typedef struct TCGHelperInfo TCGHelperInfo; ++typedef struct TaskState TaskState; ++typedef struct TranslationBlock TranslationBlock; ++typedef struct VirtIODevice VirtIODevice; ++typedef struct Visitor Visitor; ++typedef struct VMChangeStateEntry VMChangeStateEntry; ++typedef struct VMStateDescription VMStateDescription; ++ ++/* ++ * Pointer types ++ * Such typedefs should be limited to cases where the typedef's users ++ * are oblivious of its "pointer-ness". ++ * Please keep this list in case-insensitive alphabetical order. ++ */ ++typedef struct IRQState *qemu_irq; ++ ++/* ++ * Function types ++ */ ++typedef void (*qemu_irq_handler)(void *opaque, int n, int level); ++ ++#endif /* QEMU_TYPEDEFS_H */ +diff --git a/include/qemu/unicode.h b/include/qemu/unicode.h +new file mode 100644 +index 00000000..7fa10b8e +--- /dev/null ++++ b/include/qemu/unicode.h +@@ -0,0 +1,7 @@ ++#ifndef QEMU_UNICODE_H ++#define QEMU_UNICODE_H ++ ++int mod_utf8_codepoint(const char *s, size_t n, char **end); ++ssize_t mod_utf8_encode(char buf[], size_t bufsz, int codepoint); ++ ++#endif +diff --git a/include/qemu/units.h b/include/qemu/units.h +new file mode 100644 +index 00000000..692db3fb +--- /dev/null ++++ b/include/qemu/units.h +@@ -0,0 +1,20 @@ ++/* ++ * IEC binary prefixes definitions ++ * ++ * Copyright (C) 2015 Nikunj A Dadhania, IBM Corporation ++ * Copyright (C) 2018 Philippe Mathieu-Daudé ++ * ++ * SPDX-License-Identifier: GPL-2.0-or-later ++ */ ++ ++#ifndef QEMU_UNITS_H ++#define QEMU_UNITS_H ++ ++#define KiB (INT64_C(1) << 10) ++#define MiB (INT64_C(1) << 20) ++#define GiB (INT64_C(1) << 30) ++#define TiB (INT64_C(1) << 40) ++#define PiB (INT64_C(1) << 50) ++#define EiB (INT64_C(1) << 60) ++ ++#endif +diff --git a/include/qemu/uri.h b/include/qemu/uri.h +new file mode 100644 +index 00000000..255e61f4 +--- /dev/null ++++ b/include/qemu/uri.h +@@ -0,0 +1,99 @@ ++/** ++ * Summary: library of generic URI related routines ++ * Description: library of generic URI related routines ++ * Implements RFC 2396 ++ * ++ * Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * DANIEL VEILLARD BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER ++ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Except as contained in this notice, the name of Daniel Veillard shall not ++ * be used in advertising or otherwise to promote the sale, use or other ++ * dealings in this Software without prior written authorization from him. ++ * ++ * Author: Daniel Veillard ++ ** ++ * Copyright (C) 2007 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library. If not, see . ++ * ++ * Authors: ++ * Richard W.M. Jones ++ * ++ * Utility functions to help parse and assemble query strings. ++ */ ++ ++#ifndef QEMU_URI_H ++#define QEMU_URI_H ++ ++/** ++ * URI: ++ * ++ * A parsed URI reference. This is a struct containing the various fields ++ * as described in RFC 2396 but separated for further processing. ++ */ ++typedef struct URI { ++ char *scheme; /* the URI scheme */ ++ char *opaque; /* opaque part */ ++ char *authority; /* the authority part */ ++ char *server; /* the server part */ ++ char *user; /* the user part */ ++ int port; /* the port number */ ++ char *path; /* the path string */ ++ char *fragment; /* the fragment identifier */ ++ int cleanup; /* parsing potentially unclean URI */ ++ char *query; /* the query string (as it appears in the URI) */ ++} URI; ++ ++URI *uri_new(void); ++URI *uri_parse(const char *str); ++URI *uri_parse_raw(const char *str, int raw); ++int uri_parse_into(URI *uri, const char *str); ++char *uri_to_string(URI *uri); ++void uri_free(URI *uri); ++ ++/* Single web service query parameter 'name=value'. */ ++typedef struct QueryParam { ++ char *name; /* Name (unescaped). */ ++ char *value; /* Value (unescaped). */ ++ int ignore; /* Ignore this field in qparam_get_query */ ++} QueryParam; ++ ++/* Set of parameters. */ ++typedef struct QueryParams { ++ int n; /* number of parameters used */ ++ int alloc; /* allocated space */ ++ QueryParam *p; /* array of parameters */ ++} QueryParams; ++ ++QueryParams *query_params_new(int init_alloc); ++QueryParams *query_params_parse(const char *query); ++void query_params_free(QueryParams *ps); ++ ++#endif /* QEMU_URI_H */ +diff --git a/include/qemu/yank.h b/include/qemu/yank.h +new file mode 100644 +index 00000000..3d88af69 +--- /dev/null ++++ b/include/qemu/yank.h +@@ -0,0 +1,87 @@ ++/* ++ * QEMU yank feature ++ * ++ * Copyright (c) Lukas Straub ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef YANK_H ++#define YANK_H ++ ++#include "qapi/qapi-types-yank.h" ++ ++typedef void (YankFn)(void *opaque); ++ ++/** ++ * yank_register_instance: Register a new instance. ++ * ++ * This registers a new instance for yanking. Must be called before any yank ++ * function is registered for this instance. ++ * ++ * This function is thread-safe. ++ * ++ * @instance: The instance. ++ * @errp: Error object. ++ * ++ * Returns true on success or false if an error occurred. ++ */ ++bool yank_register_instance(const YankInstance *instance, Error **errp); ++ ++/** ++ * yank_unregister_instance: Unregister a instance. ++ * ++ * This unregisters a instance. Must be called only after every yank function ++ * of the instance has been unregistered. ++ * ++ * This function is thread-safe. ++ * ++ * @instance: The instance. ++ */ ++void yank_unregister_instance(const YankInstance *instance); ++ ++/** ++ * yank_register_function: Register a yank function ++ * ++ * This registers a yank function. All limitations of qmp oob commands apply ++ * to the yank function as well. See docs/devel/qapi-code-gen.rst under ++ * "An OOB-capable command handler must satisfy the following conditions". ++ * ++ * This function is thread-safe. ++ * ++ * @instance: The instance. ++ * @func: The yank function. ++ * @opaque: Will be passed to the yank function. ++ */ ++void yank_register_function(const YankInstance *instance, ++ YankFn *func, ++ void *opaque); ++ ++/** ++ * yank_unregister_function: Unregister a yank function ++ * ++ * This unregisters a yank function. ++ * ++ * This function is thread-safe. ++ * ++ * @instance: The instance. ++ * @func: func that was passed to yank_register_function. ++ * @opaque: opaque that was passed to yank_register_function. ++ */ ++void yank_unregister_function(const YankInstance *instance, ++ YankFn *func, ++ void *opaque); ++ ++#define BLOCKDEV_YANK_INSTANCE(the_node_name) (&(YankInstance) { \ ++ .type = YANK_INSTANCE_TYPE_BLOCK_NODE, \ ++ .u.block_node.node_name = (the_node_name) }) ++ ++#define CHARDEV_YANK_INSTANCE(the_id) (&(YankInstance) { \ ++ .type = YANK_INSTANCE_TYPE_CHARDEV, \ ++ .u.chardev.id = (the_id) }) ++ ++#define MIGRATION_YANK_INSTANCE (&(YankInstance) { \ ++ .type = YANK_INSTANCE_TYPE_MIGRATION }) ++ ++#endif +diff --git a/include/qom/object.h b/include/qom/object.h +new file mode 100644 +index 00000000..13d3a655 +--- /dev/null ++++ b/include/qom/object.h +@@ -0,0 +1,2058 @@ ++/* ++ * QEMU Object Model ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_OBJECT_H ++#define QEMU_OBJECT_H ++ ++#include "qapi/qapi-builtin-types.h" ++#include "qemu/module.h" ++ ++struct TypeImpl; ++typedef struct TypeImpl *Type; ++ ++typedef struct TypeInfo TypeInfo; ++ ++typedef struct InterfaceClass InterfaceClass; ++typedef struct InterfaceInfo InterfaceInfo; ++ ++#define TYPE_OBJECT "object" ++ ++typedef struct ObjectProperty ObjectProperty; ++ ++/** ++ * typedef ObjectPropertyAccessor: ++ * @obj: the object that owns the property ++ * @v: the visitor that contains the property data ++ * @name: the name of the property ++ * @opaque: the object property opaque ++ * @errp: a pointer to an Error that is filled if getting/setting fails. ++ * ++ * Called when trying to get/set a property. ++ */ ++typedef void (ObjectPropertyAccessor)(Object *obj, ++ Visitor *v, ++ const char *name, ++ void *opaque, ++ Error **errp); ++ ++/** ++ * typedef ObjectPropertyResolve: ++ * @obj: the object that owns the property ++ * @opaque: the opaque registered with the property ++ * @part: the name of the property ++ * ++ * Resolves the #Object corresponding to property @part. ++ * ++ * The returned object can also be used as a starting point ++ * to resolve a relative path starting with "@part". ++ * ++ * Returns: If @path is the path that led to @obj, the function ++ * returns the #Object corresponding to "@path/@part". ++ * If "@path/@part" is not a valid object path, it returns #NULL. ++ */ ++typedef Object *(ObjectPropertyResolve)(Object *obj, ++ void *opaque, ++ const char *part); ++ ++/** ++ * typedef ObjectPropertyRelease: ++ * @obj: the object that owns the property ++ * @name: the name of the property ++ * @opaque: the opaque registered with the property ++ * ++ * Called when a property is removed from a object. ++ */ ++typedef void (ObjectPropertyRelease)(Object *obj, ++ const char *name, ++ void *opaque); ++ ++/** ++ * typedef ObjectPropertyInit: ++ * @obj: the object that owns the property ++ * @prop: the property to set ++ * ++ * Called when a property is initialized. ++ */ ++typedef void (ObjectPropertyInit)(Object *obj, ObjectProperty *prop); ++ ++struct ObjectProperty ++{ ++ char *name; ++ char *type; ++ char *description; ++ ObjectPropertyAccessor *get; ++ ObjectPropertyAccessor *set; ++ ObjectPropertyResolve *resolve; ++ ObjectPropertyRelease *release; ++ ObjectPropertyInit *init; ++ void *opaque; ++ QObject *defval; ++}; ++ ++/** ++ * typedef ObjectUnparent: ++ * @obj: the object that is being removed from the composition tree ++ * ++ * Called when an object is being removed from the QOM composition tree. ++ * The function should remove any backlinks from children objects to @obj. ++ */ ++typedef void (ObjectUnparent)(Object *obj); ++ ++/** ++ * typedef ObjectFree: ++ * @obj: the object being freed ++ * ++ * Called when an object's last reference is removed. ++ */ ++typedef void (ObjectFree)(void *obj); ++ ++#define OBJECT_CLASS_CAST_CACHE 4 ++ ++/** ++ * struct ObjectClass: ++ * ++ * The base for all classes. The only thing that #ObjectClass contains is an ++ * integer type handle. ++ */ ++struct ObjectClass ++{ ++ /* private: */ ++ Type type; ++ GSList *interfaces; ++ ++ const char *object_cast_cache[OBJECT_CLASS_CAST_CACHE]; ++ const char *class_cast_cache[OBJECT_CLASS_CAST_CACHE]; ++ ++ ObjectUnparent *unparent; ++ ++ GHashTable *properties; ++}; ++ ++/** ++ * struct Object: ++ * ++ * The base for all objects. The first member of this object is a pointer to ++ * a #ObjectClass. Since C guarantees that the first member of a structure ++ * always begins at byte 0 of that structure, as long as any sub-object places ++ * its parent as the first member, we can cast directly to a #Object. ++ * ++ * As a result, #Object contains a reference to the objects type as its ++ * first member. This allows identification of the real type of the object at ++ * run time. ++ */ ++struct Object ++{ ++ /* private: */ ++ ObjectClass *class; ++ ObjectFree *free; ++ GHashTable *properties; ++ uint32_t ref; ++ Object *parent; ++}; ++ ++/** ++ * DECLARE_INSTANCE_CHECKER: ++ * @InstanceType: instance struct name ++ * @OBJ_NAME: the object name in uppercase with underscore separators ++ * @TYPENAME: type name ++ * ++ * Direct usage of this macro should be avoided, and the complete ++ * OBJECT_DECLARE_TYPE macro is recommended instead. ++ * ++ * This macro will provide the instance type cast functions for a ++ * QOM type. ++ */ ++#define DECLARE_INSTANCE_CHECKER(InstanceType, OBJ_NAME, TYPENAME) \ ++ static inline G_GNUC_UNUSED InstanceType * \ ++ OBJ_NAME(const void *obj) \ ++ { return OBJECT_CHECK(InstanceType, obj, TYPENAME); } ++ ++/** ++ * DECLARE_CLASS_CHECKERS: ++ * @ClassType: class struct name ++ * @OBJ_NAME: the object name in uppercase with underscore separators ++ * @TYPENAME: type name ++ * ++ * Direct usage of this macro should be avoided, and the complete ++ * OBJECT_DECLARE_TYPE macro is recommended instead. ++ * ++ * This macro will provide the class type cast functions for a ++ * QOM type. ++ */ ++#define DECLARE_CLASS_CHECKERS(ClassType, OBJ_NAME, TYPENAME) \ ++ static inline G_GNUC_UNUSED ClassType * \ ++ OBJ_NAME##_GET_CLASS(const void *obj) \ ++ { return OBJECT_GET_CLASS(ClassType, obj, TYPENAME); } \ ++ \ ++ static inline G_GNUC_UNUSED ClassType * \ ++ OBJ_NAME##_CLASS(const void *klass) \ ++ { return OBJECT_CLASS_CHECK(ClassType, klass, TYPENAME); } ++ ++/** ++ * DECLARE_OBJ_CHECKERS: ++ * @InstanceType: instance struct name ++ * @ClassType: class struct name ++ * @OBJ_NAME: the object name in uppercase with underscore separators ++ * @TYPENAME: type name ++ * ++ * Direct usage of this macro should be avoided, and the complete ++ * OBJECT_DECLARE_TYPE macro is recommended instead. ++ * ++ * This macro will provide the three standard type cast functions for a ++ * QOM type. ++ */ ++#define DECLARE_OBJ_CHECKERS(InstanceType, ClassType, OBJ_NAME, TYPENAME) \ ++ DECLARE_INSTANCE_CHECKER(InstanceType, OBJ_NAME, TYPENAME) \ ++ \ ++ DECLARE_CLASS_CHECKERS(ClassType, OBJ_NAME, TYPENAME) ++ ++/** ++ * OBJECT_DECLARE_TYPE: ++ * @InstanceType: instance struct name ++ * @ClassType: class struct name ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * ++ * This macro is typically used in a header file, and will: ++ * ++ * - create the typedefs for the object and class structs ++ * - register the type for use with g_autoptr ++ * - provide three standard type cast functions ++ * ++ * The object struct and class struct need to be declared manually. ++ */ ++#define OBJECT_DECLARE_TYPE(InstanceType, ClassType, MODULE_OBJ_NAME) \ ++ typedef struct InstanceType InstanceType; \ ++ typedef struct ClassType ClassType; \ ++ \ ++ G_DEFINE_AUTOPTR_CLEANUP_FUNC(InstanceType, object_unref) \ ++ \ ++ DECLARE_OBJ_CHECKERS(InstanceType, ClassType, \ ++ MODULE_OBJ_NAME, TYPE_##MODULE_OBJ_NAME) ++ ++/** ++ * OBJECT_DECLARE_SIMPLE_TYPE: ++ * @InstanceType: instance struct name ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * ++ * This does the same as OBJECT_DECLARE_TYPE(), but with no class struct ++ * declared. ++ * ++ * This macro should be used unless the class struct needs to have ++ * virtual methods declared. ++ */ ++#define OBJECT_DECLARE_SIMPLE_TYPE(InstanceType, MODULE_OBJ_NAME) \ ++ typedef struct InstanceType InstanceType; \ ++ \ ++ G_DEFINE_AUTOPTR_CLEANUP_FUNC(InstanceType, object_unref) \ ++ \ ++ DECLARE_INSTANCE_CHECKER(InstanceType, MODULE_OBJ_NAME, TYPE_##MODULE_OBJ_NAME) ++ ++ ++/** ++ * DO_OBJECT_DEFINE_TYPE_EXTENDED: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * @ABSTRACT: boolean flag to indicate whether the object can be instantiated ++ * @CLASS_SIZE: size of the type's class ++ * @...: list of initializers for "InterfaceInfo" to declare implemented interfaces ++ * ++ * This is the base macro used to implement all the OBJECT_DEFINE_* ++ * macros. It should never be used directly in a source file. ++ */ ++#define DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, \ ++ PARENT_MODULE_OBJ_NAME, \ ++ ABSTRACT, CLASS_SIZE, ...) \ ++ static void \ ++ module_obj_name##_finalize(Object *obj); \ ++ static void \ ++ module_obj_name##_class_init(ObjectClass *oc, void *data); \ ++ static void \ ++ module_obj_name##_init(Object *obj); \ ++ \ ++ static const TypeInfo module_obj_name##_info = { \ ++ .parent = TYPE_##PARENT_MODULE_OBJ_NAME, \ ++ .name = TYPE_##MODULE_OBJ_NAME, \ ++ .instance_size = sizeof(ModuleObjName), \ ++ .instance_align = __alignof__(ModuleObjName), \ ++ .instance_init = module_obj_name##_init, \ ++ .instance_finalize = module_obj_name##_finalize, \ ++ .class_size = CLASS_SIZE, \ ++ .class_init = module_obj_name##_class_init, \ ++ .abstract = ABSTRACT, \ ++ .interfaces = (InterfaceInfo[]) { __VA_ARGS__ } , \ ++ }; \ ++ \ ++ static void \ ++ module_obj_name##_register_types(void) \ ++ { \ ++ type_register_static(&module_obj_name##_info); \ ++ } \ ++ type_init(module_obj_name##_register_types); ++ ++/** ++ * OBJECT_DEFINE_TYPE_EXTENDED: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * @ABSTRACT: boolean flag to indicate whether the object can be instantiated ++ * @...: list of initializers for "InterfaceInfo" to declare implemented interfaces ++ * ++ * This macro is typically used in a source file, and will: ++ * ++ * - declare prototypes for _finalize, _class_init and _init methods ++ * - declare the TypeInfo struct instance ++ * - provide the constructor to register the type ++ * ++ * After using this macro, implementations of the _finalize, _class_init, ++ * and _init methods need to be written. Any of these can be zero-line ++ * no-op impls if no special logic is required for a given type. ++ * ++ * This macro should rarely be used, instead one of the more specialized ++ * macros is usually a better choice. ++ */ ++#define OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ++ ABSTRACT, ...) \ ++ DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ++ ABSTRACT, sizeof(ModuleObjName##Class), \ ++ __VA_ARGS__) ++ ++/** ++ * OBJECT_DEFINE_TYPE: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * ++ * This is a specialization of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable ++ * for the common case of a non-abstract type, without any interfaces. ++ */ ++#define OBJECT_DEFINE_TYPE(ModuleObjName, module_obj_name, MODULE_OBJ_NAME, \ ++ PARENT_MODULE_OBJ_NAME) \ ++ OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ++ false, { NULL }) ++ ++/** ++ * OBJECT_DEFINE_TYPE_WITH_INTERFACES: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * @...: list of initializers for "InterfaceInfo" to declare implemented interfaces ++ * ++ * This is a specialization of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable ++ * for the common case of a non-abstract type, with one or more implemented ++ * interfaces. ++ * ++ * Note when passing the list of interfaces, be sure to include the final ++ * NULL entry, e.g. { TYPE_USER_CREATABLE }, { NULL } ++ */ ++#define OBJECT_DEFINE_TYPE_WITH_INTERFACES(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, \ ++ PARENT_MODULE_OBJ_NAME, ...) \ ++ OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ++ false, __VA_ARGS__) ++ ++/** ++ * OBJECT_DEFINE_ABSTRACT_TYPE: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * ++ * This is a specialization of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable ++ * for defining an abstract type, without any interfaces. ++ */ ++#define OBJECT_DEFINE_ABSTRACT_TYPE(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME) \ ++ OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ++ true, { NULL }) ++ ++/** ++ * OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * ++ * This is a variant of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable for ++ * the case of a non-abstract type, with interfaces, and with no requirement ++ * for a class struct. ++ */ ++#define OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ModuleObjName, \ ++ module_obj_name, \ ++ MODULE_OBJ_NAME, \ ++ PARENT_MODULE_OBJ_NAME, ...) \ ++ DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ++ false, 0, __VA_ARGS__) ++ ++/** ++ * OBJECT_DEFINE_SIMPLE_TYPE: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * ++ * This is a variant of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable for ++ * the common case of a non-abstract type, without any interfaces, and with ++ * no requirement for a class struct. If you declared your type with ++ * OBJECT_DECLARE_SIMPLE_TYPE then this is probably the right choice for ++ * defining it. ++ */ ++#define OBJECT_DEFINE_SIMPLE_TYPE(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME) \ ++ OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, { NULL }) ++ ++/** ++ * struct TypeInfo: ++ * @name: The name of the type. ++ * @parent: The name of the parent type. ++ * @instance_size: The size of the object (derivative of #Object). If ++ * @instance_size is 0, then the size of the object will be the size of the ++ * parent object. ++ * @instance_align: The required alignment of the object. If @instance_align ++ * is 0, then normal malloc alignment is sufficient; if non-zero, then we ++ * must use qemu_memalign for allocation. ++ * @instance_init: This function is called to initialize an object. The parent ++ * class will have already been initialized so the type is only responsible ++ * for initializing its own members. ++ * @instance_post_init: This function is called to finish initialization of ++ * an object, after all @instance_init functions were called. ++ * @instance_finalize: This function is called during object destruction. This ++ * is called before the parent @instance_finalize function has been called. ++ * An object should only free the members that are unique to its type in this ++ * function. ++ * @abstract: If this field is true, then the class is considered abstract and ++ * cannot be directly instantiated. ++ * @class_size: The size of the class object (derivative of #ObjectClass) ++ * for this object. If @class_size is 0, then the size of the class will be ++ * assumed to be the size of the parent class. This allows a type to avoid ++ * implementing an explicit class type if they are not adding additional ++ * virtual functions. ++ * @class_init: This function is called after all parent class initialization ++ * has occurred to allow a class to set its default virtual method pointers. ++ * This is also the function to use to override virtual methods from a parent ++ * class. ++ * @class_base_init: This function is called for all base classes after all ++ * parent class initialization has occurred, but before the class itself ++ * is initialized. This is the function to use to undo the effects of ++ * memcpy from the parent class to the descendants. ++ * @class_data: Data to pass to the @class_init, ++ * @class_base_init. This can be useful when building dynamic ++ * classes. ++ * @interfaces: The list of interfaces associated with this type. This ++ * should point to a static array that's terminated with a zero filled ++ * element. ++ */ ++struct TypeInfo ++{ ++ const char *name; ++ const char *parent; ++ ++ size_t instance_size; ++ size_t instance_align; ++ void (*instance_init)(Object *obj); ++ void (*instance_post_init)(Object *obj); ++ void (*instance_finalize)(Object *obj); ++ ++ bool abstract; ++ size_t class_size; ++ ++ void (*class_init)(ObjectClass *klass, void *data); ++ void (*class_base_init)(ObjectClass *klass, void *data); ++ void *class_data; ++ ++ InterfaceInfo *interfaces; ++}; ++ ++/** ++ * OBJECT: ++ * @obj: A derivative of #Object ++ * ++ * Converts an object to a #Object. Since all objects are #Objects, ++ * this function will always succeed. ++ */ ++#define OBJECT(obj) \ ++ ((Object *)(obj)) ++ ++/** ++ * OBJECT_CLASS: ++ * @class: A derivative of #ObjectClass. ++ * ++ * Converts a class to an #ObjectClass. Since all objects are #Objects, ++ * this function will always succeed. ++ */ ++#define OBJECT_CLASS(class) \ ++ ((ObjectClass *)(class)) ++ ++/** ++ * OBJECT_CHECK: ++ * @type: The C type to use for the return value. ++ * @obj: A derivative of @type to cast. ++ * @name: The QOM typename of @type ++ * ++ * A type safe version of @object_dynamic_cast_assert. Typically each class ++ * will define a macro based on this type to perform type safe dynamic_casts to ++ * this object type. ++ * ++ * If an invalid object is passed to this function, a run time assert will be ++ * generated. ++ */ ++#define OBJECT_CHECK(type, obj, name) \ ++ ((type *)object_dynamic_cast_assert(OBJECT(obj), (name), \ ++ __FILE__, __LINE__, __func__)) ++ ++/** ++ * OBJECT_CLASS_CHECK: ++ * @class_type: The C type to use for the return value. ++ * @class: A derivative class of @class_type to cast. ++ * @name: the QOM typename of @class_type. ++ * ++ * A type safe version of @object_class_dynamic_cast_assert. This macro is ++ * typically wrapped by each type to perform type safe casts of a class to a ++ * specific class type. ++ */ ++#define OBJECT_CLASS_CHECK(class_type, class, name) \ ++ ((class_type *)object_class_dynamic_cast_assert(OBJECT_CLASS(class), (name), \ ++ __FILE__, __LINE__, __func__)) ++ ++/** ++ * OBJECT_GET_CLASS: ++ * @class: The C type to use for the return value. ++ * @obj: The object to obtain the class for. ++ * @name: The QOM typename of @obj. ++ * ++ * This function will return a specific class for a given object. Its generally ++ * used by each type to provide a type safe macro to get a specific class type ++ * from an object. ++ */ ++#define OBJECT_GET_CLASS(class, obj, name) \ ++ OBJECT_CLASS_CHECK(class, object_get_class(OBJECT(obj)), name) ++ ++/** ++ * struct InterfaceInfo: ++ * @type: The name of the interface. ++ * ++ * The information associated with an interface. ++ */ ++struct InterfaceInfo { ++ const char *type; ++}; ++ ++/** ++ * struct InterfaceClass: ++ * @parent_class: the base class ++ * ++ * The class for all interfaces. Subclasses of this class should only add ++ * virtual methods. ++ */ ++struct InterfaceClass ++{ ++ ObjectClass parent_class; ++ /* private: */ ++ ObjectClass *concrete_class; ++ Type interface_type; ++}; ++ ++#define TYPE_INTERFACE "interface" ++ ++/** ++ * INTERFACE_CLASS: ++ * @klass: class to cast from ++ * Returns: An #InterfaceClass or raise an error if cast is invalid ++ */ ++#define INTERFACE_CLASS(klass) \ ++ OBJECT_CLASS_CHECK(InterfaceClass, klass, TYPE_INTERFACE) ++ ++/** ++ * INTERFACE_CHECK: ++ * @interface: the type to return ++ * @obj: the object to convert to an interface ++ * @name: the interface type name ++ * ++ * Returns: @obj casted to @interface if cast is valid, otherwise raise error. ++ */ ++#define INTERFACE_CHECK(interface, obj, name) \ ++ ((interface *)object_dynamic_cast_assert(OBJECT((obj)), (name), \ ++ __FILE__, __LINE__, __func__)) ++ ++/** ++ * object_new_with_class: ++ * @klass: The class to instantiate. ++ * ++ * This function will initialize a new object using heap allocated memory. ++ * The returned object has a reference count of 1, and will be freed when ++ * the last reference is dropped. ++ * ++ * Returns: The newly allocated and instantiated object. ++ */ ++Object *object_new_with_class(ObjectClass *klass); ++ ++/** ++ * object_new: ++ * @typename: The name of the type of the object to instantiate. ++ * ++ * This function will initialize a new object using heap allocated memory. ++ * The returned object has a reference count of 1, and will be freed when ++ * the last reference is dropped. ++ * ++ * Returns: The newly allocated and instantiated object. ++ */ ++Object *object_new(const char *typename); ++ ++/** ++ * object_new_with_props: ++ * @typename: The name of the type of the object to instantiate. ++ * @parent: the parent object ++ * @id: The unique ID of the object ++ * @errp: pointer to error object ++ * @...: list of property names and values ++ * ++ * This function will initialize a new object using heap allocated memory. ++ * The returned object has a reference count of 1, and will be freed when ++ * the last reference is dropped. ++ * ++ * The @id parameter will be used when registering the object as a ++ * child of @parent in the composition tree. ++ * ++ * The variadic parameters are a list of pairs of (propname, propvalue) ++ * strings. The propname of %NULL indicates the end of the property ++ * list. If the object implements the user creatable interface, the ++ * object will be marked complete once all the properties have been ++ * processed. ++ * ++ * .. code-block:: c ++ * :caption: Creating an object with properties ++ * ++ * Error *err = NULL; ++ * Object *obj; ++ * ++ * obj = object_new_with_props(TYPE_MEMORY_BACKEND_FILE, ++ * object_get_objects_root(), ++ * "hostmem0", ++ * &err, ++ * "share", "yes", ++ * "mem-path", "/dev/shm/somefile", ++ * "prealloc", "yes", ++ * "size", "1048576", ++ * NULL); ++ * ++ * if (!obj) { ++ * error_reportf_err(err, "Cannot create memory backend: "); ++ * } ++ * ++ * The returned object will have one stable reference maintained ++ * for as long as it is present in the object hierarchy. ++ * ++ * Returns: The newly allocated, instantiated & initialized object. ++ */ ++Object *object_new_with_props(const char *typename, ++ Object *parent, ++ const char *id, ++ Error **errp, ++ ...) G_GNUC_NULL_TERMINATED; ++ ++/** ++ * object_new_with_propv: ++ * @typename: The name of the type of the object to instantiate. ++ * @parent: the parent object ++ * @id: The unique ID of the object ++ * @errp: pointer to error object ++ * @vargs: list of property names and values ++ * ++ * See object_new_with_props() for documentation. ++ */ ++Object *object_new_with_propv(const char *typename, ++ Object *parent, ++ const char *id, ++ Error **errp, ++ va_list vargs); ++ ++bool object_apply_global_props(Object *obj, const GPtrArray *props, ++ Error **errp); ++void object_set_machine_compat_props(GPtrArray *compat_props); ++void object_set_accelerator_compat_props(GPtrArray *compat_props); ++void object_register_sugar_prop(const char *driver, const char *prop, ++ const char *value, bool optional); ++void object_apply_compat_props(Object *obj); ++ ++/** ++ * object_set_props: ++ * @obj: the object instance to set properties on ++ * @errp: pointer to error object ++ * @...: list of property names and values ++ * ++ * This function will set a list of properties on an existing object ++ * instance. ++ * ++ * The variadic parameters are a list of pairs of (propname, propvalue) ++ * strings. The propname of %NULL indicates the end of the property ++ * list. ++ * ++ * .. code-block:: c ++ * :caption: Update an object's properties ++ * ++ * Error *err = NULL; ++ * Object *obj = ...get / create object...; ++ * ++ * if (!object_set_props(obj, ++ * &err, ++ * "share", "yes", ++ * "mem-path", "/dev/shm/somefile", ++ * "prealloc", "yes", ++ * "size", "1048576", ++ * NULL)) { ++ * error_reportf_err(err, "Cannot set properties: "); ++ * } ++ * ++ * The returned object will have one stable reference maintained ++ * for as long as it is present in the object hierarchy. ++ * ++ * Returns: %true on success, %false on error. ++ */ ++bool object_set_props(Object *obj, Error **errp, ...) G_GNUC_NULL_TERMINATED; ++ ++/** ++ * object_set_propv: ++ * @obj: the object instance to set properties on ++ * @errp: pointer to error object ++ * @vargs: list of property names and values ++ * ++ * See object_set_props() for documentation. ++ * ++ * Returns: %true on success, %false on error. ++ */ ++bool object_set_propv(Object *obj, Error **errp, va_list vargs); ++ ++/** ++ * object_initialize: ++ * @obj: A pointer to the memory to be used for the object. ++ * @size: The maximum size available at @obj for the object. ++ * @typename: The name of the type of the object to instantiate. ++ * ++ * This function will initialize an object. The memory for the object should ++ * have already been allocated. The returned object has a reference count of 1, ++ * and will be finalized when the last reference is dropped. ++ */ ++void object_initialize(void *obj, size_t size, const char *typename); ++ ++/** ++ * object_initialize_child_with_props: ++ * @parentobj: The parent object to add a property to ++ * @propname: The name of the property ++ * @childobj: A pointer to the memory to be used for the object. ++ * @size: The maximum size available at @childobj for the object. ++ * @type: The name of the type of the object to instantiate. ++ * @errp: If an error occurs, a pointer to an area to store the error ++ * @...: list of property names and values ++ * ++ * This function will initialize an object. The memory for the object should ++ * have already been allocated. The object will then be added as child property ++ * to a parent with object_property_add_child() function. The returned object ++ * has a reference count of 1 (for the "child<...>" property from the parent), ++ * so the object will be finalized automatically when the parent gets removed. ++ * ++ * The variadic parameters are a list of pairs of (propname, propvalue) ++ * strings. The propname of %NULL indicates the end of the property list. ++ * If the object implements the user creatable interface, the object will ++ * be marked complete once all the properties have been processed. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_initialize_child_with_props(Object *parentobj, ++ const char *propname, ++ void *childobj, size_t size, const char *type, ++ Error **errp, ...) G_GNUC_NULL_TERMINATED; ++ ++/** ++ * object_initialize_child_with_propsv: ++ * @parentobj: The parent object to add a property to ++ * @propname: The name of the property ++ * @childobj: A pointer to the memory to be used for the object. ++ * @size: The maximum size available at @childobj for the object. ++ * @type: The name of the type of the object to instantiate. ++ * @errp: If an error occurs, a pointer to an area to store the error ++ * @vargs: list of property names and values ++ * ++ * See object_initialize_child() for documentation. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_initialize_child_with_propsv(Object *parentobj, ++ const char *propname, ++ void *childobj, size_t size, const char *type, ++ Error **errp, va_list vargs); ++ ++/** ++ * object_initialize_child: ++ * @parent: The parent object to add a property to ++ * @propname: The name of the property ++ * @child: A precisely typed pointer to the memory to be used for the ++ * object. ++ * @type: The name of the type of the object to instantiate. ++ * ++ * This is like:: ++ * ++ * object_initialize_child_with_props(parent, propname, ++ * child, sizeof(*child), type, ++ * &error_abort, NULL) ++ */ ++#define object_initialize_child(parent, propname, child, type) \ ++ object_initialize_child_internal((parent), (propname), \ ++ (child), sizeof(*(child)), (type)) ++void object_initialize_child_internal(Object *parent, const char *propname, ++ void *child, size_t size, ++ const char *type); ++ ++/** ++ * object_dynamic_cast: ++ * @obj: The object to cast. ++ * @typename: The @typename to cast to. ++ * ++ * This function will determine if @obj is-a @typename. @obj can refer to an ++ * object or an interface associated with an object. ++ * ++ * Returns: This function returns @obj on success or #NULL on failure. ++ */ ++Object *object_dynamic_cast(Object *obj, const char *typename); ++ ++/** ++ * object_dynamic_cast_assert: ++ * @obj: The object to cast. ++ * @typename: The @typename to cast to. ++ * @file: Source code file where function was called ++ * @line: Source code line where function was called ++ * @func: Name of function where this function was called ++ * ++ * See object_dynamic_cast() for a description of the parameters of this ++ * function. The only difference in behavior is that this function asserts ++ * instead of returning #NULL on failure if QOM cast debugging is enabled. ++ * This function is not meant to be called directly, but only through ++ * the wrapper macro OBJECT_CHECK. ++ */ ++Object *object_dynamic_cast_assert(Object *obj, const char *typename, ++ const char *file, int line, const char *func); ++ ++/** ++ * object_get_class: ++ * @obj: A derivative of #Object ++ * ++ * Returns: The #ObjectClass of the type associated with @obj. ++ */ ++ObjectClass *object_get_class(Object *obj); ++ ++/** ++ * object_get_typename: ++ * @obj: A derivative of #Object. ++ * ++ * Returns: The QOM typename of @obj. ++ */ ++const char *object_get_typename(const Object *obj); ++ ++/** ++ * type_register_static: ++ * @info: The #TypeInfo of the new type. ++ * ++ * @info and all of the strings it points to should exist for the life time ++ * that the type is registered. ++ * ++ * Returns: the new #Type. ++ */ ++Type type_register_static(const TypeInfo *info); ++ ++/** ++ * type_register: ++ * @info: The #TypeInfo of the new type ++ * ++ * Unlike type_register_static(), this call does not require @info or its ++ * string members to continue to exist after the call returns. ++ * ++ * Returns: the new #Type. ++ */ ++Type type_register(const TypeInfo *info); ++ ++/** ++ * type_register_static_array: ++ * @infos: The array of the new type #TypeInfo structures. ++ * @nr_infos: number of entries in @infos ++ * ++ * @infos and all of the strings it points to should exist for the life time ++ * that the type is registered. ++ */ ++void type_register_static_array(const TypeInfo *infos, int nr_infos); ++ ++/** ++ * DEFINE_TYPES: ++ * @type_array: The array containing #TypeInfo structures to register ++ * ++ * @type_array should be static constant that exists for the life time ++ * that the type is registered. ++ */ ++#define DEFINE_TYPES(type_array) \ ++static void do_qemu_init_ ## type_array(void) \ ++{ \ ++ type_register_static_array(type_array, ARRAY_SIZE(type_array)); \ ++} \ ++type_init(do_qemu_init_ ## type_array) ++ ++/** ++ * type_print_class_properties: ++ * @type: a QOM class name ++ * ++ * Print the object's class properties to stdout or the monitor. ++ * Return whether an object was found. ++ */ ++bool type_print_class_properties(const char *type); ++ ++/** ++ * object_set_properties_from_keyval: ++ * @obj: a QOM object ++ * @qdict: a dictionary with the properties to be set ++ * @from_json: true if leaf values of @qdict are typed, false if they ++ * are strings ++ * @errp: pointer to error object ++ * ++ * For each key in the dictionary, parse the value string if needed, ++ * then set the corresponding property in @obj. ++ */ ++void object_set_properties_from_keyval(Object *obj, const QDict *qdict, ++ bool from_json, Error **errp); ++ ++/** ++ * object_class_dynamic_cast_assert: ++ * @klass: The #ObjectClass to attempt to cast. ++ * @typename: The QOM typename of the class to cast to. ++ * @file: Source code file where function was called ++ * @line: Source code line where function was called ++ * @func: Name of function where this function was called ++ * ++ * See object_class_dynamic_cast() for a description of the parameters ++ * of this function. The only difference in behavior is that this function ++ * asserts instead of returning #NULL on failure if QOM cast debugging is ++ * enabled. This function is not meant to be called directly, but only through ++ * the wrapper macro OBJECT_CLASS_CHECK. ++ */ ++ObjectClass *object_class_dynamic_cast_assert(ObjectClass *klass, ++ const char *typename, ++ const char *file, int line, ++ const char *func); ++ ++/** ++ * object_class_dynamic_cast: ++ * @klass: The #ObjectClass to attempt to cast. ++ * @typename: The QOM typename of the class to cast to. ++ * ++ * Returns: If @typename is a class, this function returns @klass if ++ * @typename is a subtype of @klass, else returns #NULL. ++ * ++ * If @typename is an interface, this function returns the interface ++ * definition for @klass if @klass implements it unambiguously; #NULL ++ * is returned if @klass does not implement the interface or if multiple ++ * classes or interfaces on the hierarchy leading to @klass implement ++ * it. (FIXME: perhaps this can be detected at type definition time?) ++ */ ++ObjectClass *object_class_dynamic_cast(ObjectClass *klass, ++ const char *typename); ++ ++/** ++ * object_class_get_parent: ++ * @klass: The class to obtain the parent for. ++ * ++ * Returns: The parent for @klass or %NULL if none. ++ */ ++ObjectClass *object_class_get_parent(ObjectClass *klass); ++ ++/** ++ * object_class_get_name: ++ * @klass: The class to obtain the QOM typename for. ++ * ++ * Returns: The QOM typename for @klass. ++ */ ++const char *object_class_get_name(ObjectClass *klass); ++ ++/** ++ * object_class_is_abstract: ++ * @klass: The class to obtain the abstractness for. ++ * ++ * Returns: %true if @klass is abstract, %false otherwise. ++ */ ++bool object_class_is_abstract(ObjectClass *klass); ++ ++/** ++ * object_class_by_name: ++ * @typename: The QOM typename to obtain the class for. ++ * ++ * Returns: The class for @typename or %NULL if not found. ++ */ ++ObjectClass *object_class_by_name(const char *typename); ++ ++/** ++ * module_object_class_by_name: ++ * @typename: The QOM typename to obtain the class for. ++ * ++ * For objects which might be provided by a module. Behaves like ++ * object_class_by_name, but additionally tries to load the module ++ * needed in case the class is not available. ++ * ++ * Returns: The class for @typename or %NULL if not found. ++ */ ++ObjectClass *module_object_class_by_name(const char *typename); ++ ++void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque), ++ const char *implements_type, bool include_abstract, ++ void *opaque); ++ ++/** ++ * object_class_get_list: ++ * @implements_type: The type to filter for, including its derivatives. ++ * @include_abstract: Whether to include abstract classes. ++ * ++ * Returns: A singly-linked list of the classes in reverse hashtable order. ++ */ ++GSList *object_class_get_list(const char *implements_type, ++ bool include_abstract); ++ ++/** ++ * object_class_get_list_sorted: ++ * @implements_type: The type to filter for, including its derivatives. ++ * @include_abstract: Whether to include abstract classes. ++ * ++ * Returns: A singly-linked list of the classes in alphabetical ++ * case-insensitive order. ++ */ ++GSList *object_class_get_list_sorted(const char *implements_type, ++ bool include_abstract); ++ ++/** ++ * object_ref: ++ * @obj: the object ++ * ++ * Increase the reference count of a object. A object cannot be freed as long ++ * as its reference count is greater than zero. ++ * Returns: @obj ++ */ ++Object *object_ref(void *obj); ++ ++/** ++ * object_unref: ++ * @obj: the object ++ * ++ * Decrease the reference count of a object. A object cannot be freed as long ++ * as its reference count is greater than zero. ++ */ ++void object_unref(void *obj); ++ ++/** ++ * object_property_try_add: ++ * @obj: the object to add a property to ++ * @name: the name of the property. This can contain any character except for ++ * a forward slash. In general, you should use hyphens '-' instead of ++ * underscores '_' when naming properties. ++ * @type: the type name of the property. This namespace is pretty loosely ++ * defined. Sub namespaces are constructed by using a prefix and then ++ * to angle brackets. For instance, the type 'virtio-net-pci' in the ++ * 'link' namespace would be 'link'. ++ * @get: The getter to be called to read a property. If this is NULL, then ++ * the property cannot be read. ++ * @set: the setter to be called to write a property. If this is NULL, ++ * then the property cannot be written. ++ * @release: called when the property is removed from the object. This is ++ * meant to allow a property to free its opaque upon object ++ * destruction. This may be NULL. ++ * @opaque: an opaque pointer to pass to the callbacks for the property ++ * @errp: pointer to error object ++ * ++ * Returns: The #ObjectProperty; this can be used to set the @resolve ++ * callback for child and link properties. ++ */ ++ObjectProperty *object_property_try_add(Object *obj, const char *name, ++ const char *type, ++ ObjectPropertyAccessor *get, ++ ObjectPropertyAccessor *set, ++ ObjectPropertyRelease *release, ++ void *opaque, Error **errp); ++ ++/** ++ * object_property_add: ++ * Same as object_property_try_add() with @errp hardcoded to ++ * &error_abort. ++ * ++ * @obj: the object to add a property to ++ * @name: the name of the property. This can contain any character except for ++ * a forward slash. In general, you should use hyphens '-' instead of ++ * underscores '_' when naming properties. ++ * @type: the type name of the property. This namespace is pretty loosely ++ * defined. Sub namespaces are constructed by using a prefix and then ++ * to angle brackets. For instance, the type 'virtio-net-pci' in the ++ * 'link' namespace would be 'link'. ++ * @get: The getter to be called to read a property. If this is NULL, then ++ * the property cannot be read. ++ * @set: the setter to be called to write a property. If this is NULL, ++ * then the property cannot be written. ++ * @release: called when the property is removed from the object. This is ++ * meant to allow a property to free its opaque upon object ++ * destruction. This may be NULL. ++ * @opaque: an opaque pointer to pass to the callbacks for the property ++ */ ++ObjectProperty *object_property_add(Object *obj, const char *name, ++ const char *type, ++ ObjectPropertyAccessor *get, ++ ObjectPropertyAccessor *set, ++ ObjectPropertyRelease *release, ++ void *opaque); ++ ++void object_property_del(Object *obj, const char *name); ++ ++ObjectProperty *object_class_property_add(ObjectClass *klass, const char *name, ++ const char *type, ++ ObjectPropertyAccessor *get, ++ ObjectPropertyAccessor *set, ++ ObjectPropertyRelease *release, ++ void *opaque); ++ ++/** ++ * object_property_set_default_bool: ++ * @prop: the property to set ++ * @value: the value to be written to the property ++ * ++ * Set the property default value. ++ */ ++void object_property_set_default_bool(ObjectProperty *prop, bool value); ++ ++/** ++ * object_property_set_default_str: ++ * @prop: the property to set ++ * @value: the value to be written to the property ++ * ++ * Set the property default value. ++ */ ++void object_property_set_default_str(ObjectProperty *prop, const char *value); ++ ++/** ++ * object_property_set_default_list: ++ * @prop: the property to set ++ * ++ * Set the property default value to be an empty list. ++ */ ++void object_property_set_default_list(ObjectProperty *prop); ++ ++/** ++ * object_property_set_default_int: ++ * @prop: the property to set ++ * @value: the value to be written to the property ++ * ++ * Set the property default value. ++ */ ++void object_property_set_default_int(ObjectProperty *prop, int64_t value); ++ ++/** ++ * object_property_set_default_uint: ++ * @prop: the property to set ++ * @value: the value to be written to the property ++ * ++ * Set the property default value. ++ */ ++void object_property_set_default_uint(ObjectProperty *prop, uint64_t value); ++ ++/** ++ * object_property_find: ++ * @obj: the object ++ * @name: the name of the property ++ * ++ * Look up a property for an object. ++ * ++ * Return its #ObjectProperty if found, or NULL. ++ */ ++ObjectProperty *object_property_find(Object *obj, const char *name); ++ ++/** ++ * object_property_find_err: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Look up a property for an object. ++ * ++ * Return its #ObjectProperty if found, or NULL. ++ */ ++ObjectProperty *object_property_find_err(Object *obj, ++ const char *name, ++ Error **errp); ++ ++/** ++ * object_class_property_find: ++ * @klass: the object class ++ * @name: the name of the property ++ * ++ * Look up a property for an object class. ++ * ++ * Return its #ObjectProperty if found, or NULL. ++ */ ++ObjectProperty *object_class_property_find(ObjectClass *klass, ++ const char *name); ++ ++/** ++ * object_class_property_find_err: ++ * @klass: the object class ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Look up a property for an object class. ++ * ++ * Return its #ObjectProperty if found, or NULL. ++ */ ++ObjectProperty *object_class_property_find_err(ObjectClass *klass, ++ const char *name, ++ Error **errp); ++ ++typedef struct ObjectPropertyIterator { ++ ObjectClass *nextclass; ++ GHashTableIter iter; ++} ObjectPropertyIterator; ++ ++/** ++ * object_property_iter_init: ++ * @iter: the iterator instance ++ * @obj: the object ++ * ++ * Initializes an iterator for traversing all properties ++ * registered against an object instance, its class and all parent classes. ++ * ++ * It is forbidden to modify the property list while iterating, ++ * whether removing or adding properties. ++ * ++ * Typical usage pattern would be ++ * ++ * .. code-block:: c ++ * :caption: Using object property iterators ++ * ++ * ObjectProperty *prop; ++ * ObjectPropertyIterator iter; ++ * ++ * object_property_iter_init(&iter, obj); ++ * while ((prop = object_property_iter_next(&iter))) { ++ * ... do something with prop ... ++ * } ++ */ ++void object_property_iter_init(ObjectPropertyIterator *iter, ++ Object *obj); ++ ++/** ++ * object_class_property_iter_init: ++ * @iter: the iterator instance ++ * @klass: the class ++ * ++ * Initializes an iterator for traversing all properties ++ * registered against an object class and all parent classes. ++ * ++ * It is forbidden to modify the property list while iterating, ++ * whether removing or adding properties. ++ * ++ * This can be used on abstract classes as it does not create a temporary ++ * instance. ++ */ ++void object_class_property_iter_init(ObjectPropertyIterator *iter, ++ ObjectClass *klass); ++ ++/** ++ * object_property_iter_next: ++ * @iter: the iterator instance ++ * ++ * Return the next available property. If no further properties ++ * are available, a %NULL value will be returned and the @iter ++ * pointer should not be used again after this point without ++ * re-initializing it. ++ * ++ * Returns: the next property, or %NULL when all properties ++ * have been traversed. ++ */ ++ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter); ++ ++void object_unparent(Object *obj); ++ ++/** ++ * object_property_get: ++ * @obj: the object ++ * @name: the name of the property ++ * @v: the visitor that will receive the property value. This should be an ++ * Output visitor and the data will be written with @name as the name. ++ * @errp: returns an error if this function fails ++ * ++ * Reads a property from a object. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_get(Object *obj, const char *name, Visitor *v, ++ Error **errp); ++ ++/** ++ * object_property_set_str: ++ * @obj: the object ++ * @name: the name of the property ++ * @value: the value to be written to the property ++ * @errp: returns an error if this function fails ++ * ++ * Writes a string value to a property. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set_str(Object *obj, const char *name, ++ const char *value, Error **errp); ++ ++/** ++ * object_property_get_str: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, converted to a C string, or NULL if ++ * an error occurs (including when the property value is not a string). ++ * The caller should free the string. ++ */ ++char *object_property_get_str(Object *obj, const char *name, ++ Error **errp); ++ ++/** ++ * object_property_set_link: ++ * @obj: the object ++ * @name: the name of the property ++ * @value: the value to be written to the property ++ * @errp: returns an error if this function fails ++ * ++ * Writes an object's canonical path to a property. ++ * ++ * If the link property was created with ++ * %OBJ_PROP_LINK_STRONG bit, the old target object is ++ * unreferenced, and a reference is added to the new target object. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set_link(Object *obj, const char *name, ++ Object *value, Error **errp); ++ ++/** ++ * object_property_get_link: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, resolved from a path to an Object, ++ * or NULL if an error occurs (including when the property value is not a ++ * string or not a valid object path). ++ */ ++Object *object_property_get_link(Object *obj, const char *name, ++ Error **errp); ++ ++/** ++ * object_property_set_bool: ++ * @obj: the object ++ * @name: the name of the property ++ * @value: the value to be written to the property ++ * @errp: returns an error if this function fails ++ * ++ * Writes a bool value to a property. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set_bool(Object *obj, const char *name, ++ bool value, Error **errp); ++ ++/** ++ * object_property_get_bool: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, converted to a boolean, or false if ++ * an error occurs (including when the property value is not a bool). ++ */ ++bool object_property_get_bool(Object *obj, const char *name, ++ Error **errp); ++ ++/** ++ * object_property_set_int: ++ * @obj: the object ++ * @name: the name of the property ++ * @value: the value to be written to the property ++ * @errp: returns an error if this function fails ++ * ++ * Writes an integer value to a property. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set_int(Object *obj, const char *name, ++ int64_t value, Error **errp); ++ ++/** ++ * object_property_get_int: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, converted to an integer, or -1 if ++ * an error occurs (including when the property value is not an integer). ++ */ ++int64_t object_property_get_int(Object *obj, const char *name, ++ Error **errp); ++ ++/** ++ * object_property_set_uint: ++ * @obj: the object ++ * @name: the name of the property ++ * @value: the value to be written to the property ++ * @errp: returns an error if this function fails ++ * ++ * Writes an unsigned integer value to a property. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set_uint(Object *obj, const char *name, ++ uint64_t value, Error **errp); ++ ++/** ++ * object_property_get_uint: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, converted to an unsigned integer, or 0 ++ * an error occurs (including when the property value is not an integer). ++ */ ++uint64_t object_property_get_uint(Object *obj, const char *name, ++ Error **errp); ++ ++/** ++ * object_property_get_enum: ++ * @obj: the object ++ * @name: the name of the property ++ * @typename: the name of the enum data type ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, converted to an integer (which ++ * can't be negative), or -1 on error (including when the property ++ * value is not an enum). ++ */ ++int object_property_get_enum(Object *obj, const char *name, ++ const char *typename, Error **errp); ++ ++/** ++ * object_property_set: ++ * @obj: the object ++ * @name: the name of the property ++ * @v: the visitor that will be used to write the property value. This should ++ * be an Input visitor and the data will be first read with @name as the ++ * name and then written as the property value. ++ * @errp: returns an error if this function fails ++ * ++ * Writes a property to a object. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set(Object *obj, const char *name, Visitor *v, ++ Error **errp); ++ ++/** ++ * object_property_parse: ++ * @obj: the object ++ * @name: the name of the property ++ * @string: the string that will be used to parse the property value. ++ * @errp: returns an error if this function fails ++ * ++ * Parses a string and writes the result into a property of an object. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_parse(Object *obj, const char *name, ++ const char *string, Error **errp); ++ ++/** ++ * object_property_print: ++ * @obj: the object ++ * @name: the name of the property ++ * @human: if true, print for human consumption ++ * @errp: returns an error if this function fails ++ * ++ * Returns a string representation of the value of the property. The ++ * caller shall free the string. ++ */ ++char *object_property_print(Object *obj, const char *name, bool human, ++ Error **errp); ++ ++/** ++ * object_property_get_type: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: The type name of the property. ++ */ ++const char *object_property_get_type(Object *obj, const char *name, ++ Error **errp); ++ ++/** ++ * object_get_root: ++ * ++ * Returns: the root object of the composition tree ++ */ ++Object *object_get_root(void); ++ ++ ++/** ++ * object_get_objects_root: ++ * ++ * Get the container object that holds user created ++ * object instances. This is the object at path ++ * "/objects" ++ * ++ * Returns: the user object container ++ */ ++Object *object_get_objects_root(void); ++ ++/** ++ * object_get_internal_root: ++ * ++ * Get the container object that holds internally used object ++ * instances. Any object which is put into this container must not be ++ * user visible, and it will not be exposed in the QOM tree. ++ * ++ * Returns: the internal object container ++ */ ++Object *object_get_internal_root(void); ++ ++/** ++ * object_get_canonical_path_component: ++ * @obj: the object ++ * ++ * Returns: The final component in the object's canonical path. The canonical ++ * path is the path within the composition tree starting from the root. ++ * %NULL if the object doesn't have a parent (and thus a canonical path). ++ */ ++const char *object_get_canonical_path_component(const Object *obj); ++ ++/** ++ * object_get_canonical_path: ++ * @obj: the object ++ * ++ * Returns: The canonical path for a object, newly allocated. This is ++ * the path within the composition tree starting from the root. Use ++ * g_free() to free it. ++ */ ++char *object_get_canonical_path(const Object *obj); ++ ++/** ++ * object_resolve_path: ++ * @path: the path to resolve ++ * @ambiguous: returns true if the path resolution failed because of an ++ * ambiguous match ++ * ++ * There are two types of supported paths--absolute paths and partial paths. ++ * ++ * Absolute paths are derived from the root object and can follow child<> or ++ * link<> properties. Since they can follow link<> properties, they can be ++ * arbitrarily long. Absolute paths look like absolute filenames and are ++ * prefixed with a leading slash. ++ * ++ * Partial paths look like relative filenames. They do not begin with a ++ * prefix. The matching rules for partial paths are subtle but designed to make ++ * specifying objects easy. At each level of the composition tree, the partial ++ * path is matched as an absolute path. The first match is not returned. At ++ * least two matches are searched for. A successful result is only returned if ++ * only one match is found. If more than one match is found, a flag is ++ * returned to indicate that the match was ambiguous. ++ * ++ * Returns: The matched object or NULL on path lookup failure. ++ */ ++Object *object_resolve_path(const char *path, bool *ambiguous); ++ ++/** ++ * object_resolve_path_type: ++ * @path: the path to resolve ++ * @typename: the type to look for. ++ * @ambiguous: returns true if the path resolution failed because of an ++ * ambiguous match ++ * ++ * This is similar to object_resolve_path. However, when looking for a ++ * partial path only matches that implement the given type are considered. ++ * This restricts the search and avoids spuriously flagging matches as ++ * ambiguous. ++ * ++ * For both partial and absolute paths, the return value goes through ++ * a dynamic cast to @typename. This is important if either the link, ++ * or the typename itself are of interface types. ++ * ++ * Returns: The matched object or NULL on path lookup failure. ++ */ ++Object *object_resolve_path_type(const char *path, const char *typename, ++ bool *ambiguous); ++ ++/** ++ * object_resolve_type_unambiguous: ++ * @typename: the type to look for ++ * @errp: pointer to error object ++ * ++ * Return the only object in the QOM tree of type @typename. ++ * If no match or more than one match is found, an error is ++ * returned. ++ * ++ * Returns: The matched object or NULL on path lookup failure. ++ */ ++Object *object_resolve_type_unambiguous(const char *typename, Error **errp); ++ ++/** ++ * object_resolve_path_at: ++ * @parent: the object in which to resolve the path ++ * @path: the path to resolve ++ * ++ * This is like object_resolve_path(), except paths not starting with ++ * a slash are relative to @parent. ++ * ++ * Returns: The resolved object or NULL on path lookup failure. ++ */ ++Object *object_resolve_path_at(Object *parent, const char *path); ++ ++/** ++ * object_resolve_path_component: ++ * @parent: the object in which to resolve the path ++ * @part: the component to resolve. ++ * ++ * This is similar to object_resolve_path with an absolute path, but it ++ * only resolves one element (@part) and takes the others from @parent. ++ * ++ * Returns: The resolved object or NULL on path lookup failure. ++ */ ++Object *object_resolve_path_component(Object *parent, const char *part); ++ ++/** ++ * object_property_try_add_child: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @child: the child object ++ * @errp: pointer to error object ++ * ++ * Child properties form the composition tree. All objects need to be a child ++ * of another object. Objects can only be a child of one object. ++ * ++ * There is no way for a child to determine what its parent is. It is not ++ * a bidirectional relationship. This is by design. ++ * ++ * The value of a child property as a C string will be the child object's ++ * canonical path. It can be retrieved using object_property_get_str(). ++ * The child object itself can be retrieved using object_property_get_link(). ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_try_add_child(Object *obj, const char *name, ++ Object *child, Error **errp); ++ ++/** ++ * object_property_add_child: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @child: the child object ++ * ++ * Same as object_property_try_add_child() with @errp hardcoded to ++ * &error_abort ++ */ ++ObjectProperty *object_property_add_child(Object *obj, const char *name, ++ Object *child); ++ ++typedef enum { ++ /* Unref the link pointer when the property is deleted */ ++ OBJ_PROP_LINK_STRONG = 0x1, ++ ++ /* private */ ++ OBJ_PROP_LINK_DIRECT = 0x2, ++ OBJ_PROP_LINK_CLASS = 0x4, ++} ObjectPropertyLinkFlags; ++ ++/** ++ * object_property_allow_set_link: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @child: the child object ++ * @errp: pointer to error object ++ * ++ * The default implementation of the object_property_add_link() check() ++ * callback function. It allows the link property to be set and never returns ++ * an error. ++ */ ++void object_property_allow_set_link(const Object *obj, const char *name, ++ Object *child, Error **errp); ++ ++/** ++ * object_property_add_link: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @type: the qobj type of the link ++ * @targetp: a pointer to where the link object reference is stored ++ * @check: callback to veto setting or NULL if the property is read-only ++ * @flags: additional options for the link ++ * ++ * Links establish relationships between objects. Links are unidirectional ++ * although two links can be combined to form a bidirectional relationship ++ * between objects. ++ * ++ * Links form the graph in the object model. ++ * ++ * The @check() callback is invoked when ++ * object_property_set_link() is called and can raise an error to prevent the ++ * link being set. If @check is NULL, the property is read-only ++ * and cannot be set. ++ * ++ * Ownership of the pointer that @child points to is transferred to the ++ * link property. The reference count for *@child is ++ * managed by the property from after the function returns till the ++ * property is deleted with object_property_del(). If the ++ * @flags %OBJ_PROP_LINK_STRONG bit is set, ++ * the reference count is decremented when the property is deleted or ++ * modified. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_link(Object *obj, const char *name, ++ const char *type, Object **targetp, ++ void (*check)(const Object *obj, const char *name, ++ Object *val, Error **errp), ++ ObjectPropertyLinkFlags flags); ++ ++ObjectProperty *object_class_property_add_link(ObjectClass *oc, ++ const char *name, ++ const char *type, ptrdiff_t offset, ++ void (*check)(const Object *obj, const char *name, ++ Object *val, Error **errp), ++ ObjectPropertyLinkFlags flags); ++ ++/** ++ * object_property_add_str: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @get: the getter or NULL if the property is write-only. This function must ++ * return a string to be freed by g_free(). ++ * @set: the setter or NULL if the property is read-only ++ * ++ * Add a string property using getters/setters. This function will add a ++ * property of type 'string'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_str(Object *obj, const char *name, ++ char *(*get)(Object *, Error **), ++ void (*set)(Object *, const char *, Error **)); ++ ++ObjectProperty *object_class_property_add_str(ObjectClass *klass, ++ const char *name, ++ char *(*get)(Object *, Error **), ++ void (*set)(Object *, const char *, ++ Error **)); ++ ++/** ++ * object_property_add_bool: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @get: the getter or NULL if the property is write-only. ++ * @set: the setter or NULL if the property is read-only ++ * ++ * Add a bool property using getters/setters. This function will add a ++ * property of type 'bool'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_bool(Object *obj, const char *name, ++ bool (*get)(Object *, Error **), ++ void (*set)(Object *, bool, Error **)); ++ ++ObjectProperty *object_class_property_add_bool(ObjectClass *klass, ++ const char *name, ++ bool (*get)(Object *, Error **), ++ void (*set)(Object *, bool, Error **)); ++ ++/** ++ * object_property_add_enum: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @typename: the name of the enum data type ++ * @lookup: enum value namelookup table ++ * @get: the getter or %NULL if the property is write-only. ++ * @set: the setter or %NULL if the property is read-only ++ * ++ * Add an enum property using getters/setters. This function will add a ++ * property of type '@typename'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_enum(Object *obj, const char *name, ++ const char *typename, ++ const QEnumLookup *lookup, ++ int (*get)(Object *, Error **), ++ void (*set)(Object *, int, Error **)); ++ ++ObjectProperty *object_class_property_add_enum(ObjectClass *klass, ++ const char *name, ++ const char *typename, ++ const QEnumLookup *lookup, ++ int (*get)(Object *, Error **), ++ void (*set)(Object *, int, Error **)); ++ ++/** ++ * object_property_add_tm: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @get: the getter or NULL if the property is write-only. ++ * ++ * Add a read-only struct tm valued property using a getter function. ++ * This function will add a property of type 'struct tm'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_tm(Object *obj, const char *name, ++ void (*get)(Object *, struct tm *, Error **)); ++ ++ObjectProperty *object_class_property_add_tm(ObjectClass *klass, ++ const char *name, ++ void (*get)(Object *, struct tm *, Error **)); ++ ++typedef enum { ++ /* Automatically add a getter to the property */ ++ OBJ_PROP_FLAG_READ = 1 << 0, ++ /* Automatically add a setter to the property */ ++ OBJ_PROP_FLAG_WRITE = 1 << 1, ++ /* Automatically add a getter and a setter to the property */ ++ OBJ_PROP_FLAG_READWRITE = (OBJ_PROP_FLAG_READ | OBJ_PROP_FLAG_WRITE), ++} ObjectPropertyFlags; ++ ++/** ++ * object_property_add_uint8_ptr: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @v: pointer to value ++ * @flags: bitwise-or'd ObjectPropertyFlags ++ * ++ * Add an integer property in memory. This function will add a ++ * property of type 'uint8'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_uint8_ptr(Object *obj, const char *name, ++ const uint8_t *v, ++ ObjectPropertyFlags flags); ++ ++ObjectProperty *object_class_property_add_uint8_ptr(ObjectClass *klass, ++ const char *name, ++ const uint8_t *v, ++ ObjectPropertyFlags flags); ++ ++/** ++ * object_property_add_uint16_ptr: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @v: pointer to value ++ * @flags: bitwise-or'd ObjectPropertyFlags ++ * ++ * Add an integer property in memory. This function will add a ++ * property of type 'uint16'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_uint16_ptr(Object *obj, const char *name, ++ const uint16_t *v, ++ ObjectPropertyFlags flags); ++ ++ObjectProperty *object_class_property_add_uint16_ptr(ObjectClass *klass, ++ const char *name, ++ const uint16_t *v, ++ ObjectPropertyFlags flags); ++ ++/** ++ * object_property_add_uint32_ptr: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @v: pointer to value ++ * @flags: bitwise-or'd ObjectPropertyFlags ++ * ++ * Add an integer property in memory. This function will add a ++ * property of type 'uint32'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_uint32_ptr(Object *obj, const char *name, ++ const uint32_t *v, ++ ObjectPropertyFlags flags); ++ ++ObjectProperty *object_class_property_add_uint32_ptr(ObjectClass *klass, ++ const char *name, ++ const uint32_t *v, ++ ObjectPropertyFlags flags); ++ ++/** ++ * object_property_add_uint64_ptr: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @v: pointer to value ++ * @flags: bitwise-or'd ObjectPropertyFlags ++ * ++ * Add an integer property in memory. This function will add a ++ * property of type 'uint64'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_uint64_ptr(Object *obj, const char *name, ++ const uint64_t *v, ++ ObjectPropertyFlags flags); ++ ++ObjectProperty *object_class_property_add_uint64_ptr(ObjectClass *klass, ++ const char *name, ++ const uint64_t *v, ++ ObjectPropertyFlags flags); ++ ++/** ++ * object_property_add_alias: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @target_obj: the object to forward property access to ++ * @target_name: the name of the property on the forwarded object ++ * ++ * Add an alias for a property on an object. This function will add a property ++ * of the same type as the forwarded property. ++ * ++ * The caller must ensure that @target_obj stays alive as long as ++ * this property exists. In the case of a child object or an alias on the same ++ * object this will be the case. For aliases to other objects the caller is ++ * responsible for taking a reference. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_alias(Object *obj, const char *name, ++ Object *target_obj, const char *target_name); ++ ++/** ++ * object_property_add_const_link: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @target: the object to be referred by the link ++ * ++ * Add an unmodifiable link for a property on an object. This function will ++ * add a property of type link where TYPE is the type of @target. ++ * ++ * The caller must ensure that @target stays alive as long as ++ * this property exists. In the case @target is a child of @obj, ++ * this will be the case. Otherwise, the caller is responsible for ++ * taking a reference. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_const_link(Object *obj, const char *name, ++ Object *target); ++ ++/** ++ * object_property_set_description: ++ * @obj: the object owning the property ++ * @name: the name of the property ++ * @description: the description of the property on the object ++ * ++ * Set an object property's description. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++void object_property_set_description(Object *obj, const char *name, ++ const char *description); ++void object_class_property_set_description(ObjectClass *klass, const char *name, ++ const char *description); ++ ++/** ++ * object_child_foreach: ++ * @obj: the object whose children will be navigated ++ * @fn: the iterator function to be called ++ * @opaque: an opaque value that will be passed to the iterator ++ * ++ * Call @fn passing each child of @obj and @opaque to it, until @fn returns ++ * non-zero. ++ * ++ * It is forbidden to add or remove children from @obj from the @fn ++ * callback. ++ * ++ * Returns: The last value returned by @fn, or 0 if there is no child. ++ */ ++int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque), ++ void *opaque); ++ ++/** ++ * object_child_foreach_recursive: ++ * @obj: the object whose children will be navigated ++ * @fn: the iterator function to be called ++ * @opaque: an opaque value that will be passed to the iterator ++ * ++ * Call @fn passing each child of @obj and @opaque to it, until @fn returns ++ * non-zero. Calls recursively, all child nodes of @obj will also be passed ++ * all the way down to the leaf nodes of the tree. Depth first ordering. ++ * ++ * It is forbidden to add or remove children from @obj (or its ++ * child nodes) from the @fn callback. ++ * ++ * Returns: The last value returned by @fn, or 0 if there is no child. ++ */ ++int object_child_foreach_recursive(Object *obj, ++ int (*fn)(Object *child, void *opaque), ++ void *opaque); ++/** ++ * container_get: ++ * @root: root of the #path, e.g., object_get_root() ++ * @path: path to the container ++ * ++ * Return a container object whose path is @path. Create more containers ++ * along the path if necessary. ++ * ++ * Returns: the container object. ++ */ ++Object *container_get(Object *root, const char *path); ++ ++/** ++ * object_type_get_instance_size: ++ * @typename: Name of the Type whose instance_size is required ++ * ++ * Returns the instance_size of the given @typename. ++ */ ++size_t object_type_get_instance_size(const char *typename); ++ ++/** ++ * object_property_help: ++ * @name: the name of the property ++ * @type: the type of the property ++ * @defval: the default value ++ * @description: description of the property ++ * ++ * Returns: a user-friendly formatted string describing the property ++ * for help purposes. ++ */ ++char *object_property_help(const char *name, const char *type, ++ QObject *defval, const char *description); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(Object, object_unref) ++ ++#endif +diff --git a/include/qom/qom-qobject.h b/include/qom/qom-qobject.h +new file mode 100644 +index 00000000..73e4e0e4 +--- /dev/null ++++ b/include/qom/qom-qobject.h +@@ -0,0 +1,43 @@ ++/* ++ * QEMU Object Model - QObject wrappers ++ * ++ * Copyright (C) 2012 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_QOM_QOBJECT_H ++#define QEMU_QOM_QOBJECT_H ++ ++/* ++ * object_property_get_qobject: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, converted to QObject, or NULL if ++ * an error occurs. ++ */ ++struct QObject *object_property_get_qobject(Object *obj, const char *name, ++ struct Error **errp); ++ ++/** ++ * object_property_set_qobject: ++ * @obj: the object ++ * @name: the name of the property ++ * @value: The value that will be written to the property. ++ * @errp: returns an error if this function fails ++ * ++ * Writes a property to a object. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set_qobject(Object *obj, ++ const char *name, struct QObject *value, ++ struct Error **errp); ++ ++#endif +diff --git a/include/sysemu/block-backend-common.h b/include/sysemu/block-backend-common.h +new file mode 100644 +index 00000000..780cea73 +--- /dev/null ++++ b/include/sysemu/block-backend-common.h +@@ -0,0 +1,103 @@ ++/* ++ * QEMU Block backends ++ * ++ * Copyright (C) 2014-2016 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BLOCK_BACKEND_COMMON_H ++#define BLOCK_BACKEND_COMMON_H ++ ++#include "qemu/iov.h" ++#include "block/throttle-groups.h" ++ ++/* ++ * TODO Have to include block/block.h for a bunch of block layer ++ * types. Unfortunately, this pulls in the whole BlockDriverState ++ * API, which we don't want used by many BlockBackend users. Some of ++ * the types belong here, and the rest should be split into a common ++ * header and one for the BlockDriverState API. ++ */ ++#include "block/block.h" ++ ++/* Callbacks for block device models */ ++typedef struct BlockDevOps { ++ ++ /* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++ /* ++ * Runs when virtual media changed (monitor commands eject, change) ++ * Argument load is true on load and false on eject. ++ * Beware: doesn't run when a host device's physical media ++ * changes. Sure would be useful if it did. ++ * Device models with removable media must implement this callback. ++ */ ++ void (*change_media_cb)(void *opaque, bool load, Error **errp); ++ /* ++ * Runs when an eject request is issued from the monitor, the tray ++ * is closed, and the medium is locked. ++ * Device models that do not implement is_medium_locked will not need ++ * this callback. Device models that can lock the medium or tray might ++ * want to implement the callback and unlock the tray when "force" is ++ * true, even if they do not support eject requests. ++ */ ++ void (*eject_request_cb)(void *opaque, bool force); ++ ++ /* ++ * Is the virtual medium locked into the device? ++ * Device models implement this only when device has such a lock. ++ */ ++ bool (*is_medium_locked)(void *opaque); ++ ++ /* ++ * Runs when the backend receives a drain request. ++ */ ++ void (*drained_begin)(void *opaque); ++ /* ++ * Runs when the backend's last drain request ends. ++ */ ++ void (*drained_end)(void *opaque); ++ /* ++ * Is the device still busy? ++ */ ++ bool (*drained_poll)(void *opaque); ++ ++ /* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++ /* ++ * Is the virtual tray open? ++ * Device models implement this only when the device has a tray. ++ */ ++ bool (*is_tray_open)(void *opaque); ++ ++ /* ++ * Runs when the size changed (e.g. monitor command block_resize) ++ */ ++ void (*resize_cb)(void *opaque); ++} BlockDevOps; ++ ++/* ++ * This struct is embedded in (the private) BlockBackend struct and contains ++ * fields that must be public. This is in particular for QLIST_ENTRY() and ++ * friends so that BlockBackends can be kept in lists outside block-backend.c ++ */ ++typedef struct BlockBackendPublic { ++ ThrottleGroupMember throttle_group_member; ++} BlockBackendPublic; ++ ++#endif /* BLOCK_BACKEND_COMMON_H */ +diff --git a/include/sysemu/block-backend-global-state.h b/include/sysemu/block-backend-global-state.h +new file mode 100644 +index 00000000..49c12b0f +--- /dev/null ++++ b/include/sysemu/block-backend-global-state.h +@@ -0,0 +1,133 @@ ++/* ++ * QEMU Block backends ++ * ++ * Copyright (C) 2014-2016 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BLOCK_BACKEND_GLOBAL_STATE_H ++#define BLOCK_BACKEND_GLOBAL_STATE_H ++ ++#include "block-backend-common.h" ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm); ++ ++BlockBackend * no_coroutine_fn ++blk_new_with_bs(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, ++ Error **errp); ++ ++BlockBackend * coroutine_fn no_co_wrapper ++blk_co_new_with_bs(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, ++ Error **errp); ++ ++BlockBackend * no_coroutine_fn ++blk_new_open(const char *filename, const char *reference, QDict *options, ++ int flags, Error **errp); ++ ++BlockBackend * coroutine_fn no_co_wrapper ++blk_co_new_open(const char *filename, const char *reference, QDict *options, ++ int flags, Error **errp); ++ ++int blk_get_refcnt(BlockBackend *blk); ++void blk_ref(BlockBackend *blk); ++ ++void no_coroutine_fn blk_unref(BlockBackend *blk); ++void coroutine_fn no_co_wrapper blk_co_unref(BlockBackend *blk); ++ ++void blk_remove_all_bs(void); ++BlockBackend *blk_by_name(const char *name); ++BlockBackend *blk_next(BlockBackend *blk); ++BlockBackend *blk_all_next(BlockBackend *blk); ++bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp); ++void monitor_remove_blk(BlockBackend *blk); ++ ++BlockBackendPublic *blk_get_public(BlockBackend *blk); ++BlockBackend *blk_by_public(BlockBackendPublic *public); ++ ++void blk_remove_bs(BlockBackend *blk); ++int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp); ++int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp); ++bool GRAPH_RDLOCK bdrv_has_blk(BlockDriverState *bs); ++bool GRAPH_RDLOCK bdrv_is_root_node(BlockDriverState *bs); ++int GRAPH_UNLOCKED blk_set_perm(BlockBackend *blk, uint64_t perm, ++ uint64_t shared_perm, Error **errp); ++void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm); ++ ++void blk_iostatus_enable(BlockBackend *blk); ++BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk); ++void blk_iostatus_disable(BlockBackend *blk); ++void blk_iostatus_reset(BlockBackend *blk); ++int blk_attach_dev(BlockBackend *blk, DeviceState *dev); ++void blk_detach_dev(BlockBackend *blk, DeviceState *dev); ++DeviceState *blk_get_attached_dev(BlockBackend *blk); ++BlockBackend *blk_by_dev(void *dev); ++BlockBackend *blk_by_qdev_id(const char *id, Error **errp); ++void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void *opaque); ++ ++void blk_activate(BlockBackend *blk, Error **errp); ++ ++int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags); ++void blk_aio_cancel(BlockAIOCB *acb); ++int blk_commit_all(void); ++bool blk_in_drain(BlockBackend *blk); ++void blk_drain(BlockBackend *blk); ++void blk_drain_all(void); ++void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, ++ BlockdevOnError on_write_error); ++bool blk_supports_write_perm(BlockBackend *blk); ++bool blk_is_sg(BlockBackend *blk); ++void blk_set_enable_write_cache(BlockBackend *blk, bool wce); ++int blk_get_flags(BlockBackend *blk); ++bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp); ++void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason); ++void blk_op_block_all(BlockBackend *blk, Error *reason); ++void blk_op_unblock_all(BlockBackend *blk, Error *reason); ++int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, ++ Error **errp); ++void blk_add_aio_context_notifier(BlockBackend *blk, ++ void (*attached_aio_context)(AioContext *new_context, void *opaque), ++ void (*detach_aio_context)(void *opaque), void *opaque); ++void blk_remove_aio_context_notifier(BlockBackend *blk, ++ void (*attached_aio_context)(AioContext *, ++ void *), ++ void (*detach_aio_context)(void *), ++ void *opaque); ++void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify); ++void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify); ++BlockBackendRootState *blk_get_root_state(BlockBackend *blk); ++void blk_update_root_state(BlockBackend *blk); ++bool blk_get_detect_zeroes_from_root_state(BlockBackend *blk); ++int blk_get_open_flags_from_root_state(BlockBackend *blk); ++ ++int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, ++ int64_t pos, int size); ++int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size); ++int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz); ++int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo); ++ ++void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg); ++void blk_io_limits_disable(BlockBackend *blk); ++void blk_io_limits_enable(BlockBackend *blk, const char *group); ++void blk_io_limits_update_group(BlockBackend *blk, const char *group); ++void blk_set_force_allow_inactivate(BlockBackend *blk); ++ ++bool blk_register_buf(BlockBackend *blk, void *host, size_t size, Error **errp); ++void blk_unregister_buf(BlockBackend *blk, void *host, size_t size); ++ ++const BdrvChild *blk_root(BlockBackend *blk); ++ ++int blk_make_empty(BlockBackend *blk, Error **errp); ++ ++#endif /* BLOCK_BACKEND_GLOBAL_STATE_H */ +diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h +new file mode 100644 +index 00000000..d174275a +--- /dev/null ++++ b/include/sysemu/block-backend-io.h +@@ -0,0 +1,230 @@ ++/* ++ * QEMU Block backends ++ * ++ * Copyright (C) 2014-2016 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BLOCK_BACKEND_IO_H ++#define BLOCK_BACKEND_IO_H ++ ++#include "block-backend-common.h" ++#include "block/accounting.h" ++ ++/* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++const char *blk_name(const BlockBackend *blk); ++ ++BlockDriverState *blk_bs(BlockBackend *blk); ++ ++void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow); ++void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow); ++void blk_set_disable_request_queuing(BlockBackend *blk, bool disable); ++bool blk_iostatus_is_enabled(const BlockBackend *blk); ++ ++char *blk_get_attached_dev_id(BlockBackend *blk); ++ ++BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque); ++ ++BlockAIOCB *blk_aio_preadv(BlockBackend *blk, int64_t offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_aio_flush(BlockBackend *blk, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_aio_zone_report(BlockBackend *blk, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_aio_zone_mgmt(BlockBackend *blk, BlockZoneOp op, ++ int64_t offset, int64_t len, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_aio_zone_append(BlockBackend *blk, int64_t *offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes, ++ BlockCompletionFunc *cb, void *opaque); ++void blk_aio_cancel_async(BlockAIOCB *acb); ++BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, ++ BlockCompletionFunc *cb, void *opaque); ++ ++void blk_inc_in_flight(BlockBackend *blk); ++void blk_dec_in_flight(BlockBackend *blk); ++ ++bool coroutine_fn GRAPH_RDLOCK blk_co_is_inserted(BlockBackend *blk); ++bool co_wrapper_mixed_bdrv_rdlock blk_is_inserted(BlockBackend *blk); ++ ++bool coroutine_fn GRAPH_RDLOCK blk_co_is_available(BlockBackend *blk); ++bool co_wrapper_mixed_bdrv_rdlock blk_is_available(BlockBackend *blk); ++ ++void coroutine_fn blk_co_lock_medium(BlockBackend *blk, bool locked); ++void co_wrapper blk_lock_medium(BlockBackend *blk, bool locked); ++ ++void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag); ++void co_wrapper blk_eject(BlockBackend *blk, bool eject_flag); ++ ++int64_t coroutine_fn blk_co_getlength(BlockBackend *blk); ++int64_t co_wrapper_mixed blk_getlength(BlockBackend *blk); ++ ++void coroutine_fn blk_co_get_geometry(BlockBackend *blk, ++ uint64_t *nb_sectors_ptr); ++void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr); ++ ++int64_t coroutine_fn blk_co_nb_sectors(BlockBackend *blk); ++int64_t blk_nb_sectors(BlockBackend *blk); ++ ++void *blk_try_blockalign(BlockBackend *blk, size_t size); ++void *blk_blockalign(BlockBackend *blk, size_t size); ++bool blk_is_writable(BlockBackend *blk); ++bool blk_enable_write_cache(BlockBackend *blk); ++BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read); ++BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, ++ int error); ++void blk_error_action(BlockBackend *blk, BlockErrorAction action, ++ bool is_read, int error); ++void blk_iostatus_set_err(BlockBackend *blk, int error); ++int blk_get_max_iov(BlockBackend *blk); ++int blk_get_max_hw_iov(BlockBackend *blk); ++ ++AioContext *blk_get_aio_context(BlockBackend *blk); ++BlockAcctStats *blk_get_stats(BlockBackend *blk); ++void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, ++ BlockCompletionFunc *cb, ++ void *opaque, int ret); ++ ++uint32_t blk_get_request_alignment(BlockBackend *blk); ++uint32_t blk_get_max_transfer(BlockBackend *blk); ++uint64_t blk_get_max_hw_transfer(BlockBackend *blk); ++ ++int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, ++ BlockBackend *blk_out, int64_t off_out, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags); ++ ++int coroutine_fn blk_co_block_status_above(BlockBackend *blk, ++ BlockDriverState *base, ++ int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, ++ BlockDriverState **file); ++int coroutine_fn blk_co_is_allocated_above(BlockBackend *blk, ++ BlockDriverState *base, ++ bool include_base, int64_t offset, ++ int64_t bytes, int64_t *pnum); ++ ++/* ++ * "I/O or GS" API functions. These functions can run without ++ * the BQL, but only in one specific iothread/main loop. ++ * ++ * See include/block/block-io.h for more information about ++ * the "I/O or GS" API. ++ */ ++ ++int co_wrapper_mixed blk_pread(BlockBackend *blk, int64_t offset, ++ int64_t bytes, void *buf, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, int64_t bytes, ++ void *buf, BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_preadv(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_preadv_part(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ size_t qiov_offset, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_preadv_part(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ size_t qiov_offset, BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_pwrite(BlockBackend *blk, int64_t offset, ++ int64_t bytes, const void *buf, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, int64_t bytes, ++ const void *buf, BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_pwritev(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_pwritev_part(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ size_t qiov_offset, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, ++ int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_pwrite_compressed(BlockBackend *blk, ++ int64_t offset, int64_t bytes, ++ const void *buf); ++int coroutine_fn blk_co_pwrite_compressed(BlockBackend *blk, int64_t offset, ++ int64_t bytes, const void *buf); ++ ++int co_wrapper_mixed blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, ++ int64_t bytes, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags); ++ ++int coroutine_fn blk_co_zone_report(BlockBackend *blk, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones); ++int co_wrapper_mixed blk_zone_report(BlockBackend *blk, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones); ++int coroutine_fn blk_co_zone_mgmt(BlockBackend *blk, BlockZoneOp op, ++ int64_t offset, int64_t len); ++int co_wrapper_mixed blk_zone_mgmt(BlockBackend *blk, BlockZoneOp op, ++ int64_t offset, int64_t len); ++int coroutine_fn blk_co_zone_append(BlockBackend *blk, int64_t *offset, ++ QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++int co_wrapper_mixed blk_zone_append(BlockBackend *blk, int64_t *offset, ++ QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_pdiscard(BlockBackend *blk, int64_t offset, ++ int64_t bytes); ++int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, ++ int64_t bytes); ++ ++int co_wrapper_mixed blk_flush(BlockBackend *blk); ++int coroutine_fn blk_co_flush(BlockBackend *blk); ++ ++int co_wrapper_mixed blk_ioctl(BlockBackend *blk, unsigned long int req, ++ void *buf); ++int coroutine_fn blk_co_ioctl(BlockBackend *blk, unsigned long int req, ++ void *buf); ++ ++int co_wrapper_mixed blk_truncate(BlockBackend *blk, int64_t offset, ++ bool exact, PreallocMode prealloc, ++ BdrvRequestFlags flags, Error **errp); ++int coroutine_fn blk_co_truncate(BlockBackend *blk, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, ++ Error **errp); ++ ++#endif /* BLOCK_BACKEND_IO_H */ +diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h +new file mode 100644 +index 00000000..038be9fc +--- /dev/null ++++ b/include/sysemu/block-backend.h +@@ -0,0 +1,21 @@ ++/* ++ * QEMU Block backends ++ * ++ * Copyright (C) 2014-2016 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BLOCK_BACKEND_H ++#define BLOCK_BACKEND_H ++ ++#include "block-backend-global-state.h" ++#include "block-backend-io.h" ++ ++/* DO NOT ADD ANYTHING IN HERE. USE ONE OF THE HEADERS INCLUDED ABOVE */ ++ ++#endif +diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h +new file mode 100644 +index 00000000..3211b165 +--- /dev/null ++++ b/include/sysemu/blockdev.h +@@ -0,0 +1,64 @@ ++/* ++ * QEMU host block devices ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef BLOCKDEV_H ++#define BLOCKDEV_H ++ ++#include "block/block.h" ++#include "qemu/queue.h" ++ ++typedef enum { ++ IF_DEFAULT = -1, /* for use with drive_add() only */ ++ /* ++ * IF_NONE must be zero, because we want MachineClass member ++ * block_default_type to default-initialize to IF_NONE ++ */ ++ IF_NONE = 0, ++ IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN, ++ IF_COUNT ++} BlockInterfaceType; ++ ++struct DriveInfo { ++ BlockInterfaceType type; ++ int bus; ++ int unit; ++ int auto_del; /* see blockdev_mark_auto_del() */ ++ bool is_default; /* Added by default_drive() ? */ ++ int media_cd; ++ QemuOpts *opts; ++ QTAILQ_ENTRY(DriveInfo) next; ++}; ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++void blockdev_mark_auto_del(BlockBackend *blk); ++void blockdev_auto_del(BlockBackend *blk); ++ ++DriveInfo *blk_legacy_dinfo(BlockBackend *blk); ++DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo); ++BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo); ++ ++void override_max_devs(BlockInterfaceType type, int max_devs); ++ ++DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); ++void drive_check_orphaned(void); ++DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); ++int drive_get_max_bus(BlockInterfaceType type); ++ ++QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, ++ const char *optstr); ++DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type, ++ Error **errp); ++ ++#endif +diff --git a/include/sysemu/cpu-timers.h b/include/sysemu/cpu-timers.h +new file mode 100644 +index 00000000..7bfa960f +--- /dev/null ++++ b/include/sysemu/cpu-timers.h +@@ -0,0 +1,104 @@ ++/* ++ * CPU timers state API ++ * ++ * Copyright 2020 SUSE LLC ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++#ifndef SYSEMU_CPU_TIMERS_H ++#define SYSEMU_CPU_TIMERS_H ++ ++#include "qemu/timer.h" ++ ++/* init the whole cpu timers API, including icount, ticks, and cpu_throttle */ ++void cpu_timers_init(void); ++ ++/* icount - Instruction Counter API */ ++ ++/** ++ * ICountMode: icount enablement state: ++ * ++ * @ICOUNT_DISABLED: Disabled - Do not count executed instructions. ++ * @ICOUNT_PRECISE: Enabled - Fixed conversion of insn to ns via "shift" option ++ * @ICOUNT_ADAPTATIVE: Enabled - Runtime adaptive algorithm to compute shift ++ */ ++typedef enum { ++ ICOUNT_DISABLED = 0, ++ ICOUNT_PRECISE, ++ ICOUNT_ADAPTATIVE, ++} ICountMode; ++ ++#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) ++extern ICountMode use_icount; ++#define icount_enabled() (use_icount) ++#else ++#define icount_enabled() ICOUNT_DISABLED ++#endif ++ ++/* ++ * Update the icount with the executed instructions. Called by ++ * cpus-tcg vCPU thread so the main-loop can see time has moved forward. ++ */ ++void icount_update(CPUState *cpu); ++ ++/* get raw icount value */ ++int64_t icount_get_raw(void); ++ ++/* return the virtual CPU time in ns, based on the instruction counter. */ ++int64_t icount_get(void); ++/* ++ * convert an instruction counter value to ns, based on the icount shift. ++ * This shift is set as a fixed value with the icount "shift" option ++ * (precise mode), or it is constantly approximated and corrected at ++ * runtime in adaptive mode. ++ */ ++int64_t icount_to_ns(int64_t icount); ++ ++/** ++ * icount_configure: configure the icount options, including "shift" ++ * @opts: Options to parse ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Return: true on success, else false setting @errp with error ++ */ ++bool icount_configure(QemuOpts *opts, Error **errp); ++ ++/* used by tcg vcpu thread to calc icount budget */ ++int64_t icount_round(int64_t count); ++ ++/* if the CPUs are idle, start accounting real time to virtual clock. */ ++void icount_start_warp_timer(void); ++void icount_account_warp_timer(void); ++void icount_notify_exit(void); ++ ++/* ++ * CPU Ticks and Clock ++ */ ++ ++/* Caller must hold BQL */ ++void cpu_enable_ticks(void); ++/* Caller must hold BQL */ ++void cpu_disable_ticks(void); ++ ++/* ++ * return the time elapsed in VM between vm_start and vm_stop. ++ * cpu_get_ticks() uses units of the host CPU cycle counter. ++ */ ++int64_t cpu_get_ticks(void); ++ ++/* ++ * Returns the monotonic time elapsed in VM, i.e., ++ * the time between vm_start and vm_stop ++ */ ++int64_t cpu_get_clock(void); ++ ++void qemu_timer_notify_cb(void *opaque, QEMUClockType type); ++ ++/* get/set VIRTUAL clock and VM elapsed ticks via the cpus accel interface */ ++int64_t cpus_get_virtual_clock(void); ++void cpus_set_virtual_clock(int64_t new_time); ++int64_t cpus_get_elapsed_ticks(void); ++ ++#endif /* SYSEMU_CPU_TIMERS_H */ +diff --git a/include/sysemu/os-posix.h b/include/sysemu/os-posix.h +new file mode 100644 +index 00000000..b881ac6c +--- /dev/null ++++ b/include/sysemu/os-posix.h +@@ -0,0 +1,101 @@ ++/* ++ * posix specific declarations ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2010 Jes Sorensen ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_OS_POSIX_H ++#define QEMU_OS_POSIX_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_SYSMACROS ++#include ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++void os_set_line_buffering(void); ++void os_setup_early_signal_handling(void); ++void os_set_proc_name(const char *s); ++void os_setup_signal_handling(void); ++int os_set_daemonize(bool d); ++bool is_daemonized(void); ++void os_daemonize(void); ++bool os_set_runas(const char *user_id); ++void os_set_chroot(const char *path); ++void os_setup_limits(void); ++void os_setup_post(void); ++int os_mlock(void); ++ ++/** ++ * qemu_alloc_stack: ++ * @sz: pointer to a size_t holding the requested usable stack size ++ * ++ * Allocate memory that can be used as a stack, for instance for ++ * coroutines. If the memory cannot be allocated, this function ++ * will abort (like g_malloc()). This function also inserts an ++ * additional guard page to catch a potential stack overflow. ++ * Note that the memory required for the guard page and alignment ++ * and minimal stack size restrictions will increase the value of sz. ++ * ++ * The allocated stack must be freed with qemu_free_stack(). ++ * ++ * Returns: pointer to (the lowest address of) the stack memory. ++ */ ++void *qemu_alloc_stack(size_t *sz); ++ ++/** ++ * qemu_free_stack: ++ * @stack: stack to free ++ * @sz: size of stack in bytes ++ * ++ * Free a stack allocated via qemu_alloc_stack(). Note that sz must ++ * be exactly the adjusted stack size returned by qemu_alloc_stack. ++ */ ++void qemu_free_stack(void *stack, size_t sz); ++ ++/* POSIX and Mingw32 differ in the name of the stdio lock functions. */ ++ ++static inline void qemu_flockfile(FILE *f) ++{ ++ flockfile(f); ++} ++ ++static inline void qemu_funlockfile(FILE *f) ++{ ++ funlockfile(f); ++} ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/qcow2/LICENSE-gpl-2.0.txt b/qcow2/LICENSE-gpl-2.0.txt +new file mode 100644 +index 00000000..9efa6fbc +--- /dev/null ++++ b/qcow2/LICENSE-gpl-2.0.txt +@@ -0,0 +1,338 @@ ++ GNU GENERAL PUBLIC LICENSE ++ Version 2, June 1991 ++ ++ Copyright (C) 1989, 1991 Free Software Foundation, Inc., ++ ++ Everyone is permitted to copy and distribute verbatim copies ++ of this license document, but changing it is not allowed. ++ ++ Preamble ++ ++ The licenses for most software are designed to take away your ++freedom to share and change it. By contrast, the GNU General Public ++License is intended to guarantee your freedom to share and change free ++software--to make sure the software is free for all its users. This ++General Public License applies to most of the Free Software ++Foundation's software and to any other program whose authors commit to ++using it. (Some other Free Software Foundation software is covered by ++the GNU Lesser General Public License instead.) You can apply it to ++your programs, too. ++ ++ When we speak of free software, we are referring to freedom, not ++price. Our General Public Licenses are designed to make sure that you ++have the freedom to distribute copies of free software (and charge for ++this service if you wish), that you receive source code or can get it ++if you want it, that you can change the software or use pieces of it ++in new free programs; and that you know you can do these things. ++ ++ To protect your rights, we need to make restrictions that forbid ++anyone to deny you these rights or to ask you to surrender the rights. ++These restrictions translate to certain responsibilities for you if you ++distribute copies of the software, or if you modify it. ++ ++ For example, if you distribute copies of such a program, whether ++gratis or for a fee, you must give the recipients all the rights that ++you have. You must make sure that they, too, receive or can get the ++source code. And you must show them these terms so they know their ++rights. ++ ++ We protect your rights with two steps: (1) copyright the software, and ++(2) offer you this license which gives you legal permission to copy, ++distribute and/or modify the software. ++ ++ Also, for each author's protection and ours, we want to make certain ++that everyone understands that there is no warranty for this free ++software. If the software is modified by someone else and passed on, we ++want its recipients to know that what they have is not the original, so ++that any problems introduced by others will not reflect on the original ++authors' reputations. ++ ++ Finally, any free program is threatened constantly by software ++patents. We wish to avoid the danger that redistributors of a free ++program will individually obtain patent licenses, in effect making the ++program proprietary. To prevent this, we have made it clear that any ++patent must be licensed for everyone's free use or not licensed at all. ++ ++ The precise terms and conditions for copying, distribution and ++modification follow. ++ ++ GNU GENERAL PUBLIC LICENSE ++ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ++ ++ 0. This License applies to any program or other work which contains ++a notice placed by the copyright holder saying it may be distributed ++under the terms of this General Public License. The "Program", below, ++refers to any such program or work, and a "work based on the Program" ++means either the Program or any derivative work under copyright law: ++that is to say, a work containing the Program or a portion of it, ++either verbatim or with modifications and/or translated into another ++language. (Hereinafter, translation is included without limitation in ++the term "modification".) Each licensee is addressed as "you". ++ ++Activities other than copying, distribution and modification are not ++covered by this License; they are outside its scope. The act of ++running the Program is not restricted, and the output from the Program ++is covered only if its contents constitute a work based on the ++Program (independent of having been made by running the Program). ++Whether that is true depends on what the Program does. ++ ++ 1. You may copy and distribute verbatim copies of the Program's ++source code as you receive it, in any medium, provided that you ++conspicuously and appropriately publish on each copy an appropriate ++copyright notice and disclaimer of warranty; keep intact all the ++notices that refer to this License and to the absence of any warranty; ++and give any other recipients of the Program a copy of this License ++along with the Program. ++ ++You may charge a fee for the physical act of transferring a copy, and ++you may at your option offer warranty protection in exchange for a fee. ++ ++ 2. You may modify your copy or copies of the Program or any portion ++of it, thus forming a work based on the Program, and copy and ++distribute such modifications or work under the terms of Section 1 ++above, provided that you also meet all of these conditions: ++ ++ a) You must cause the modified files to carry prominent notices ++ stating that you changed the files and the date of any change. ++ ++ b) You must cause any work that you distribute or publish, that in ++ whole or in part contains or is derived from the Program or any ++ part thereof, to be licensed as a whole at no charge to all third ++ parties under the terms of this License. ++ ++ c) If the modified program normally reads commands interactively ++ when run, you must cause it, when started running for such ++ interactive use in the most ordinary way, to print or display an ++ announcement including an appropriate copyright notice and a ++ notice that there is no warranty (or else, saying that you provide ++ a warranty) and that users may redistribute the program under ++ these conditions, and telling the user how to view a copy of this ++ License. (Exception: if the Program itself is interactive but ++ does not normally print such an announcement, your work based on ++ the Program is not required to print an announcement.) ++ ++These requirements apply to the modified work as a whole. If ++identifiable sections of that work are not derived from the Program, ++and can be reasonably considered independent and separate works in ++themselves, then this License, and its terms, do not apply to those ++sections when you distribute them as separate works. But when you ++distribute the same sections as part of a whole which is a work based ++on the Program, the distribution of the whole must be on the terms of ++this License, whose permissions for other licensees extend to the ++entire whole, and thus to each and every part regardless of who wrote it. ++ ++Thus, it is not the intent of this section to claim rights or contest ++your rights to work written entirely by you; rather, the intent is to ++exercise the right to control the distribution of derivative or ++collective works based on the Program. ++ ++In addition, mere aggregation of another work not based on the Program ++with the Program (or with a work based on the Program) on a volume of ++a storage or distribution medium does not bring the other work under ++the scope of this License. ++ ++ 3. You may copy and distribute the Program (or a work based on it, ++under Section 2) in object code or executable form under the terms of ++Sections 1 and 2 above provided that you also do one of the following: ++ ++ a) Accompany it with the complete corresponding machine-readable ++ source code, which must be distributed under the terms of Sections ++ 1 and 2 above on a medium customarily used for software interchange; or, ++ ++ b) Accompany it with a written offer, valid for at least three ++ years, to give any third party, for a charge no more than your ++ cost of physically performing source distribution, a complete ++ machine-readable copy of the corresponding source code, to be ++ distributed under the terms of Sections 1 and 2 above on a medium ++ customarily used for software interchange; or, ++ ++ c) Accompany it with the information you received as to the offer ++ to distribute corresponding source code. (This alternative is ++ allowed only for noncommercial distribution and only if you ++ received the program in object code or executable form with such ++ an offer, in accord with Subsection b above.) ++ ++The source code for a work means the preferred form of the work for ++making modifications to it. For an executable work, complete source ++code means all the source code for all modules it contains, plus any ++associated interface definition files, plus the scripts used to ++control compilation and installation of the executable. However, as a ++special exception, the source code distributed need not include ++anything that is normally distributed (in either source or binary ++form) with the major components (compiler, kernel, and so on) of the ++operating system on which the executable runs, unless that component ++itself accompanies the executable. ++ ++If distribution of executable or object code is made by offering ++access to copy from a designated place, then offering equivalent ++access to copy the source code from the same place counts as ++distribution of the source code, even though third parties are not ++compelled to copy the source along with the object code. ++ ++ 4. You may not copy, modify, sublicense, or distribute the Program ++except as expressly provided under this License. Any attempt ++otherwise to copy, modify, sublicense or distribute the Program is ++void, and will automatically terminate your rights under this License. ++However, parties who have received copies, or rights, from you under ++this License will not have their licenses terminated so long as such ++parties remain in full compliance. ++ ++ 5. You are not required to accept this License, since you have not ++signed it. However, nothing else grants you permission to modify or ++distribute the Program or its derivative works. These actions are ++prohibited by law if you do not accept this License. Therefore, by ++modifying or distributing the Program (or any work based on the ++Program), you indicate your acceptance of this License to do so, and ++all its terms and conditions for copying, distributing or modifying ++the Program or works based on it. ++ ++ 6. Each time you redistribute the Program (or any work based on the ++Program), the recipient automatically receives a license from the ++original licensor to copy, distribute or modify the Program subject to ++these terms and conditions. You may not impose any further ++restrictions on the recipients' exercise of the rights granted herein. ++You are not responsible for enforcing compliance by third parties to ++this License. ++ ++ 7. If, as a consequence of a court judgment or allegation of patent ++infringement or for any other reason (not limited to patent issues), ++conditions are imposed on you (whether by court order, agreement or ++otherwise) that contradict the conditions of this License, they do not ++excuse you from the conditions of this License. If you cannot ++distribute so as to satisfy simultaneously your obligations under this ++License and any other pertinent obligations, then as a consequence you ++may not distribute the Program at all. For example, if a patent ++license would not permit royalty-free redistribution of the Program by ++all those who receive copies directly or indirectly through you, then ++the only way you could satisfy both it and this License would be to ++refrain entirely from distribution of the Program. ++ ++If any portion of this section is held invalid or unenforceable under ++any particular circumstance, the balance of the section is intended to ++apply and the section as a whole is intended to apply in other ++circumstances. ++ ++It is not the purpose of this section to induce you to infringe any ++patents or other property right claims or to contest validity of any ++such claims; this section has the sole purpose of protecting the ++integrity of the free software distribution system, which is ++implemented by public license practices. Many people have made ++generous contributions to the wide range of software distributed ++through that system in reliance on consistent application of that ++system; it is up to the author/donor to decide if he or she is willing ++to distribute software through any other system and a licensee cannot ++impose that choice. ++ ++This section is intended to make thoroughly clear what is believed to ++be a consequence of the rest of this License. ++ ++ 8. If the distribution and/or use of the Program is restricted in ++certain countries either by patents or by copyrighted interfaces, the ++original copyright holder who places the Program under this License ++may add an explicit geographical distribution limitation excluding ++those countries, so that distribution is permitted only in or among ++countries not thus excluded. In such case, this License incorporates ++the limitation as if written in the body of this License. ++ ++ 9. The Free Software Foundation may publish revised and/or new versions ++of the General Public License from time to time. Such new versions will ++be similar in spirit to the present version, but may differ in detail to ++address new problems or concerns. ++ ++Each version is given a distinguishing version number. If the Program ++specifies a version number of this License which applies to it and "any ++later version", you have the option of following the terms and conditions ++either of that version or of any later version published by the Free ++Software Foundation. If the Program does not specify a version number of ++this License, you may choose any version ever published by the Free Software ++Foundation. ++ ++ 10. If you wish to incorporate parts of the Program into other free ++programs whose distribution conditions are different, write to the author ++to ask for permission. For software which is copyrighted by the Free ++Software Foundation, write to the Free Software Foundation; we sometimes ++make exceptions for this. Our decision will be guided by the two goals ++of preserving the free status of all derivatives of our free software and ++of promoting the sharing and reuse of software generally. ++ ++ NO WARRANTY ++ ++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY ++FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN ++OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES ++PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED ++OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS ++TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE ++PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, ++REPAIR OR CORRECTION. ++ ++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING ++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR ++REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, ++INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING ++OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED ++TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY ++YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER ++PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGES. ++ ++ END OF TERMS AND CONDITIONS ++ ++ How to Apply These Terms to Your New Programs ++ ++ If you develop a new program, and you want it to be of the greatest ++possible use to the public, the best way to achieve this is to make it ++free software which everyone can redistribute and change under these terms. ++ ++ To do so, attach the following notices to the program. It is safest ++to attach them to the start of each source file to most effectively ++convey the exclusion of warranty; and each file should have at least ++the "copyright" line and a pointer to where the full notice is found. ++ ++ ++ Copyright (C) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License along ++ with this program; if not, see . ++ ++Also add information on how to contact you by electronic and paper mail. ++ ++If the program is interactive, make it output a short notice like this ++when it starts in an interactive mode: ++ ++ Gnomovision version 69, Copyright (C) year name of author ++ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. ++ This is free software, and you are welcome to redistribute it ++ under certain conditions; type `show c' for details. ++ ++The hypothetical commands `show w' and `show c' should show the appropriate ++parts of the General Public License. Of course, the commands you use may ++be called something other than `show w' and `show c'; they could even be ++mouse-clicks or menu items--whatever suits your program. ++ ++You should also get your employer (if you work as a programmer) or your ++school, if any, to sign a "copyright disclaimer" for the program, if ++necessary. Here is a sample; alter the names: ++ ++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program ++ `Gnomovision' (which makes passes at compilers) written by James Hacker. ++ ++ , 1 April 1989 ++ Moe Ghoul, President of Vice ++ ++This General Public License does not permit incorporating your program into ++proprietary programs. If your program is a subroutine library, you may ++consider it more useful to permit linking proprietary applications with the ++library. If this is what you want to do, use the GNU Lesser General ++Public License instead of this License. +diff --git a/qcow2/lib/authz/base.c b/qcow2/lib/authz/base.c +new file mode 100644 +index 00000000..f2b7fbe9 +--- /dev/null ++++ b/qcow2/lib/authz/base.c +@@ -0,0 +1,83 @@ ++/* ++ * QEMU authorization framework base class ++ * ++ * Copyright (c) 2018 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "authz/base.h" ++#include "qemu/module.h" ++#include "trace.h" ++ ++bool qauthz_is_allowed(QAuthZ *authz, ++ const char *identity, ++ Error **errp) ++{ ++ QAuthZClass *cls = QAUTHZ_GET_CLASS(authz); ++ bool allowed; ++ ++ allowed = cls->is_allowed(authz, identity, errp); ++ trace_qauthz_is_allowed(authz, identity, allowed); ++ ++ return allowed; ++} ++ ++ ++bool qauthz_is_allowed_by_id(const char *authzid, ++ const char *identity, ++ Error **errp) ++{ ++ QAuthZ *authz; ++ Object *obj; ++ Object *container; ++ ++ container = object_get_objects_root(); ++ obj = object_resolve_path_component(container, ++ authzid); ++ if (!obj) { ++ error_setg(errp, "Cannot find QAuthZ object ID %s", ++ authzid); ++ return false; ++ } ++ ++ if (!object_dynamic_cast(obj, TYPE_QAUTHZ)) { ++ error_setg(errp, "Object '%s' is not a QAuthZ subclass", ++ authzid); ++ return false; ++ } ++ ++ authz = QAUTHZ(obj); ++ ++ return qauthz_is_allowed(authz, identity, errp); ++} ++ ++ ++static const TypeInfo authz_info = { ++ .parent = TYPE_OBJECT, ++ .name = TYPE_QAUTHZ, ++ .instance_size = sizeof(QAuthZ), ++ .class_size = sizeof(QAuthZClass), ++ .abstract = true, ++}; ++ ++static void qauthz_register_types(void) ++{ ++ type_register_static(&authz_info); ++} ++ ++type_init(qauthz_register_types) ++ +diff --git a/qcow2/lib/block.c b/qcow2/lib/block.c +new file mode 100644 +index 00000000..c317de9e +--- /dev/null ++++ b/qcow2/lib/block.c +@@ -0,0 +1,8432 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * Copyright (c) 2020 Virtuozzo International GmbH. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/trace.h" ++#include "block/block_int.h" ++#include "block/blockjob.h" ++#include "block/dirty-bitmap.h" ++#include "block/fuse.h" ++#include "block/nbd.h" ++#include "block/qdict.h" ++#include "qemu/error-report.h" ++#include "block/module_block.h" ++#include "qemu/main-loop.h" ++#include "qemu/module.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qjson.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qstring.h" ++#include "qapi/qobject-output-visitor.h" ++#include "qapi/qapi-visit-block-core.h" ++#include "sysemu/block-backend.h" ++#include "qemu/notify.h" ++#include "qemu/option.h" ++#include "qemu/coroutine.h" ++#include "block/qapi.h" ++#include "qemu/timer.h" ++#include "qemu/cutils.h" ++#include "qemu/id.h" ++#include "qemu/range.h" ++#include "qemu/rcu.h" ++#include "block/coroutines.h" ++ ++#ifdef CONFIG_BSD ++#include ++#include ++#if defined(HAVE_SYS_DISK_H) ++#include ++#endif ++#endif ++ ++#ifdef _WIN32 ++#include ++#endif ++ ++#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ ++ ++/* Protected by BQL */ ++static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states = ++ QTAILQ_HEAD_INITIALIZER(graph_bdrv_states); ++ ++/* Protected by BQL */ ++static QTAILQ_HEAD(, BlockDriverState) all_bdrv_states = ++ QTAILQ_HEAD_INITIALIZER(all_bdrv_states); ++ ++/* Protected by BQL */ ++static QLIST_HEAD(, BlockDriver) bdrv_drivers = ++ QLIST_HEAD_INITIALIZER(bdrv_drivers); ++ ++static BlockDriverState *bdrv_open_inherit(const char *filename, ++ const char *reference, ++ QDict *options, int flags, ++ BlockDriverState *parent, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ bool parse_filename, ++ Error **errp); ++ ++static bool bdrv_recurse_has_child(BlockDriverState *bs, ++ BlockDriverState *child); ++ ++static void GRAPH_WRLOCK ++bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs); ++ ++static void GRAPH_WRLOCK ++bdrv_remove_child(BdrvChild *child, Transaction *tran); ++ ++static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, ++ BlockReopenQueue *queue, ++ Transaction *change_child_tran, Error **errp); ++static void bdrv_reopen_commit(BDRVReopenState *reopen_state); ++static void bdrv_reopen_abort(BDRVReopenState *reopen_state); ++ ++static bool bdrv_backing_overridden(BlockDriverState *bs); ++ ++static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp); ++ ++/* If non-zero, use only whitelisted block drivers */ ++static int use_bdrv_whitelist; ++ ++#ifdef _WIN32 ++static int is_windows_drive_prefix(const char *filename) ++{ ++ return (((filename[0] >= 'a' && filename[0] <= 'z') || ++ (filename[0] >= 'A' && filename[0] <= 'Z')) && ++ filename[1] == ':'); ++} ++ ++int is_windows_drive(const char *filename) ++{ ++ if (is_windows_drive_prefix(filename) && ++ filename[2] == '\0') ++ return 1; ++ if (strstart(filename, "\\\\.\\", NULL) || ++ strstart(filename, "//./", NULL)) ++ return 1; ++ return 0; ++} ++#endif ++ ++size_t bdrv_opt_mem_align(BlockDriverState *bs) ++{ ++ if (!bs || !bs->drv) { ++ /* page size or 4k (hdd sector size) should be on the safe side */ ++ return MAX(4096, qemu_real_host_page_size()); ++ } ++ IO_CODE(); ++ ++ return bs->bl.opt_mem_alignment; ++} ++ ++size_t bdrv_min_mem_align(BlockDriverState *bs) ++{ ++ if (!bs || !bs->drv) { ++ /* page size or 4k (hdd sector size) should be on the safe side */ ++ return MAX(4096, qemu_real_host_page_size()); ++ } ++ IO_CODE(); ++ ++ return bs->bl.min_mem_alignment; ++} ++ ++/* check if the path starts with ":" */ ++int path_has_protocol(const char *path) ++{ ++ const char *p; ++ ++#ifdef _WIN32 ++ if (is_windows_drive(path) || ++ is_windows_drive_prefix(path)) { ++ return 0; ++ } ++ p = path + strcspn(path, ":/\\"); ++#else ++ p = path + strcspn(path, ":/"); ++#endif ++ ++ return *p == ':'; ++} ++ ++int path_is_absolute(const char *path) ++{ ++#ifdef _WIN32 ++ /* specific case for names like: "\\.\d:" */ ++ if (is_windows_drive(path) || is_windows_drive_prefix(path)) { ++ return 1; ++ } ++ return (*path == '/' || *path == '\\'); ++#else ++ return (*path == '/'); ++#endif ++} ++ ++/* if filename is absolute, just return its duplicate. Otherwise, build a ++ path to it by considering it is relative to base_path. URL are ++ supported. */ ++char *path_combine(const char *base_path, const char *filename) ++{ ++ const char *protocol_stripped = NULL; ++ const char *p, *p1; ++ char *result; ++ int len; ++ ++ if (path_is_absolute(filename)) { ++ return g_strdup(filename); ++ } ++ ++ if (path_has_protocol(base_path)) { ++ protocol_stripped = strchr(base_path, ':'); ++ if (protocol_stripped) { ++ protocol_stripped++; ++ } ++ } ++ p = protocol_stripped ?: base_path; ++ ++ p1 = strrchr(base_path, '/'); ++#ifdef _WIN32 ++ { ++ const char *p2; ++ p2 = strrchr(base_path, '\\'); ++ if (!p1 || p2 > p1) { ++ p1 = p2; ++ } ++ } ++#endif ++ if (p1) { ++ p1++; ++ } else { ++ p1 = base_path; ++ } ++ if (p1 > p) { ++ p = p1; ++ } ++ len = p - base_path; ++ ++ result = g_malloc(len + strlen(filename) + 1); ++ memcpy(result, base_path, len); ++ strcpy(result + len, filename); ++ ++ return result; ++} ++ ++/* ++ * Helper function for bdrv_parse_filename() implementations to remove optional ++ * protocol prefixes (especially "file:") from a filename and for putting the ++ * stripped filename into the options QDict if there is such a prefix. ++ */ ++void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix, ++ QDict *options) ++{ ++ if (strstart(filename, prefix, &filename)) { ++ /* Stripping the explicit protocol prefix may result in a protocol ++ * prefix being (wrongly) detected (if the filename contains a colon) */ ++ if (path_has_protocol(filename)) { ++ GString *fat_filename; ++ ++ /* This means there is some colon before the first slash; therefore, ++ * this cannot be an absolute path */ ++ assert(!path_is_absolute(filename)); ++ ++ /* And we can thus fix the protocol detection issue by prefixing it ++ * by "./" */ ++ fat_filename = g_string_new("./"); ++ g_string_append(fat_filename, filename); ++ ++ assert(!path_has_protocol(fat_filename->str)); ++ ++ qdict_put(options, "filename", ++ qstring_from_gstring(fat_filename)); ++ } else { ++ /* If no protocol prefix was detected, we can use the shortened ++ * filename as-is */ ++ qdict_put_str(options, "filename", filename); ++ } ++ } ++} ++ ++ ++/* Returns whether the image file is opened as read-only. Note that this can ++ * return false and writing to the image file is still not possible because the ++ * image is inactivated. */ ++bool bdrv_is_read_only(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return !(bs->open_flags & BDRV_O_RDWR); ++} ++ ++static int GRAPH_RDLOCK ++bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, ++ bool ignore_allow_rdw, Error **errp) ++{ ++ IO_CODE(); ++ ++ /* Do not set read_only if copy_on_read is enabled */ ++ if (bs->copy_on_read && read_only) { ++ error_setg(errp, "Can't set node '%s' to r/o with copy-on-read enabled", ++ bdrv_get_device_or_node_name(bs)); ++ return -EINVAL; ++ } ++ ++ /* Do not clear read_only if it is prohibited */ ++ if (!read_only && !(bs->open_flags & BDRV_O_ALLOW_RDWR) && ++ !ignore_allow_rdw) ++ { ++ error_setg(errp, "Node '%s' is read only", ++ bdrv_get_device_or_node_name(bs)); ++ return -EPERM; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Called by a driver that can only provide a read-only image. ++ * ++ * Returns 0 if the node is already read-only or it could switch the node to ++ * read-only because BDRV_O_AUTO_RDONLY is set. ++ * ++ * Returns -EACCES if the node is read-write and BDRV_O_AUTO_RDONLY is not set ++ * or bdrv_can_set_read_only() forbids making the node read-only. If @errmsg ++ * is not NULL, it is used as the error message for the Error object. ++ */ ++int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, ++ Error **errp) ++{ ++ int ret = 0; ++ IO_CODE(); ++ ++ if (!(bs->open_flags & BDRV_O_RDWR)) { ++ return 0; ++ } ++ if (!(bs->open_flags & BDRV_O_AUTO_RDONLY)) { ++ goto fail; ++ } ++ ++ ret = bdrv_can_set_read_only(bs, true, false, NULL); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ bs->open_flags &= ~BDRV_O_RDWR; ++ ++ return 0; ++ ++fail: ++ error_setg(errp, "%s", errmsg ?: "Image is read-only"); ++ return -EACCES; ++} ++ ++/* ++ * If @backing is empty, this function returns NULL without setting ++ * @errp. In all other cases, NULL will only be returned with @errp ++ * set. ++ * ++ * Therefore, a return value of NULL without @errp set means that ++ * there is no backing file; if @errp is set, there is one but its ++ * absolute filename cannot be generated. ++ */ ++char *bdrv_get_full_backing_filename_from_filename(const char *backed, ++ const char *backing, ++ Error **errp) ++{ ++ if (backing[0] == '\0') { ++ return NULL; ++ } else if (path_has_protocol(backing) || path_is_absolute(backing)) { ++ return g_strdup(backing); ++ } else if (backed[0] == '\0' || strstart(backed, "json:", NULL)) { ++ error_setg(errp, "Cannot use relative backing file names for '%s'", ++ backed); ++ return NULL; ++ } else { ++ return path_combine(backed, backing); ++ } ++} ++ ++/* ++ * If @filename is empty or NULL, this function returns NULL without ++ * setting @errp. In all other cases, NULL will only be returned with ++ * @errp set. ++ */ ++static char * GRAPH_RDLOCK ++bdrv_make_absolute_filename(BlockDriverState *relative_to, ++ const char *filename, Error **errp) ++{ ++ char *dir, *full_name; ++ ++ if (!filename || filename[0] == '\0') { ++ return NULL; ++ } else if (path_has_protocol(filename) || path_is_absolute(filename)) { ++ return g_strdup(filename); ++ } ++ ++ dir = bdrv_dirname(relative_to, errp); ++ if (!dir) { ++ return NULL; ++ } ++ ++ full_name = g_strconcat(dir, filename, NULL); ++ g_free(dir); ++ return full_name; ++} ++ ++char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_make_absolute_filename(bs, bs->backing_file, errp); ++} ++ ++void bdrv_register(BlockDriver *bdrv) ++{ ++ assert(bdrv->format_name); ++ GLOBAL_STATE_CODE(); ++ QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list); ++} ++ ++BlockDriverState *bdrv_new(void) ++{ ++ BlockDriverState *bs; ++ int i; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bs = g_new0(BlockDriverState, 1); ++ QLIST_INIT(&bs->dirty_bitmaps); ++ for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { ++ QLIST_INIT(&bs->op_blockers[i]); ++ } ++ qemu_mutex_init(&bs->reqs_lock); ++ qemu_mutex_init(&bs->dirty_bitmap_mutex); ++ bs->refcnt = 1; ++ bs->aio_context = qemu_get_aio_context(); ++ ++ qemu_co_queue_init(&bs->flush_queue); ++ ++ qemu_co_mutex_init(&bs->bsc_modify_lock); ++ bs->block_status_cache = g_new0(BdrvBlockStatusCache, 1); ++ ++ for (i = 0; i < bdrv_drain_all_count; i++) { ++ bdrv_drained_begin(bs); ++ } ++ ++ QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list); ++ ++ return bs; ++} ++ ++static BlockDriver *bdrv_do_find_format(const char *format_name) ++{ ++ BlockDriver *drv1; ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH(drv1, &bdrv_drivers, list) { ++ if (!strcmp(drv1->format_name, format_name)) { ++ return drv1; ++ } ++ } ++ ++ return NULL; ++} ++ ++BlockDriver *bdrv_find_format(const char *format_name) ++{ ++ BlockDriver *drv1; ++ int i; ++ ++ GLOBAL_STATE_CODE(); ++ ++ drv1 = bdrv_do_find_format(format_name); ++ if (drv1) { ++ return drv1; ++ } ++ ++ /* The driver isn't registered, maybe we need to load a module */ ++ for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) { ++ if (!strcmp(block_driver_modules[i].format_name, format_name)) { ++ Error *local_err = NULL; ++ int rv = block_module_load(block_driver_modules[i].library_name, ++ &local_err); ++ if (rv > 0) { ++ return bdrv_do_find_format(format_name); ++ } else if (rv < 0) { ++ error_report_err(local_err); ++ } ++ break; ++ } ++ } ++ return NULL; ++} ++ ++static int bdrv_format_is_whitelisted(const char *format_name, bool read_only) ++{ ++ static const char *whitelist_rw[] = { ++ CONFIG_BDRV_RW_WHITELIST ++ NULL ++ }; ++ static const char *whitelist_ro[] = { ++ CONFIG_BDRV_RO_WHITELIST ++ NULL ++ }; ++ const char **p; ++ ++ if (!whitelist_rw[0] && !whitelist_ro[0]) { ++ return 1; /* no whitelist, anything goes */ ++ } ++ ++ for (p = whitelist_rw; *p; p++) { ++ if (!strcmp(format_name, *p)) { ++ return 1; ++ } ++ } ++ if (read_only) { ++ for (p = whitelist_ro; *p; p++) { ++ if (!strcmp(format_name, *p)) { ++ return 1; ++ } ++ } ++ } ++ return 0; ++} ++ ++int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_format_is_whitelisted(drv->format_name, read_only); ++} ++ ++bool bdrv_uses_whitelist(void) ++{ ++ return use_bdrv_whitelist; ++} ++ ++typedef struct CreateCo { ++ BlockDriver *drv; ++ char *filename; ++ QemuOpts *opts; ++ int ret; ++ Error *err; ++} CreateCo; ++ ++int coroutine_fn bdrv_co_create(BlockDriver *drv, const char *filename, ++ QemuOpts *opts, Error **errp) ++{ ++ ERRP_GUARD(); ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv->bdrv_co_create_opts) { ++ error_setg(errp, "Driver '%s' does not support image creation", ++ drv->format_name); ++ return -ENOTSUP; ++ } ++ ++ ret = drv->bdrv_co_create_opts(drv, filename, opts, errp); ++ if (ret < 0 && !*errp) { ++ error_setg_errno(errp, -ret, "Could not create image"); ++ } ++ ++ return ret; ++} ++ ++/** ++ * Helper function for bdrv_create_file_fallback(): Resize @blk to at ++ * least the given @minimum_size. ++ * ++ * On success, return @blk's actual length. ++ * Otherwise, return -errno. ++ */ ++static int64_t coroutine_fn GRAPH_UNLOCKED ++create_file_fallback_truncate(BlockBackend *blk, int64_t minimum_size, ++ Error **errp) ++{ ++ Error *local_err = NULL; ++ int64_t size; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ ret = blk_co_truncate(blk, minimum_size, false, PREALLOC_MODE_OFF, 0, ++ &local_err); ++ if (ret < 0 && ret != -ENOTSUP) { ++ error_propagate(errp, local_err); ++ return ret; ++ } ++ ++ size = blk_co_getlength(blk); ++ if (size < 0) { ++ error_free(local_err); ++ error_setg_errno(errp, -size, ++ "Failed to inquire the new image file's length"); ++ return size; ++ } ++ ++ if (size < minimum_size) { ++ /* Need to grow the image, but we failed to do that */ ++ error_propagate(errp, local_err); ++ return -ENOTSUP; ++ } ++ ++ error_free(local_err); ++ local_err = NULL; ++ ++ return size; ++} ++ ++/** ++ * Helper function for bdrv_create_file_fallback(): Zero the first ++ * sector to remove any potentially pre-existing image header. ++ */ ++static int coroutine_fn ++create_file_fallback_zero_first_sector(BlockBackend *blk, ++ int64_t current_size, ++ Error **errp) ++{ ++ int64_t bytes_to_clear; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bytes_to_clear = MIN(current_size, BDRV_SECTOR_SIZE); ++ if (bytes_to_clear) { ++ ret = blk_co_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to clear the new image's first sector"); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * Simple implementation of bdrv_co_create_opts for protocol drivers ++ * which only support creation via opening a file ++ * (usually existing raw storage device) ++ */ ++int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, ++ const char *filename, ++ QemuOpts *opts, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ BlockBackend *blk; ++ QDict *options; ++ int64_t size = 0; ++ char *buf = NULL; ++ PreallocMode prealloc; ++ Error *local_err = NULL; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); ++ buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); ++ prealloc = qapi_enum_parse(&PreallocMode_lookup, buf, ++ PREALLOC_MODE_OFF, &local_err); ++ g_free(buf); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ ++ if (prealloc != PREALLOC_MODE_OFF) { ++ error_setg(errp, "Unsupported preallocation mode '%s'", ++ PreallocMode_str(prealloc)); ++ return -ENOTSUP; ++ } ++ ++ options = qdict_new(); ++ qdict_put_str(options, "driver", drv->format_name); ++ ++ blk = blk_co_new_open(filename, NULL, options, ++ BDRV_O_RDWR | BDRV_O_RESIZE, errp); ++ if (!blk) { ++ error_prepend(errp, "Protocol driver '%s' does not support creating " ++ "new images, so an existing image must be selected as " ++ "the target; however, opening the given target as an " ++ "existing image failed: ", ++ drv->format_name); ++ return -EINVAL; ++ } ++ ++ size = create_file_fallback_truncate(blk, size, errp); ++ if (size < 0) { ++ ret = size; ++ goto out; ++ } ++ ++ ret = create_file_fallback_zero_first_sector(blk, size, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = 0; ++out: ++ blk_co_unref(blk); ++ return ret; ++} ++ ++int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts, ++ Error **errp) ++{ ++ QemuOpts *protocol_opts; ++ BlockDriver *drv; ++ QDict *qdict; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ drv = bdrv_find_protocol(filename, true, errp); ++ if (drv == NULL) { ++ return -ENOENT; ++ } ++ ++ if (!drv->create_opts) { ++ error_setg(errp, "Driver '%s' does not support image creation", ++ drv->format_name); ++ return -ENOTSUP; ++ } ++ ++ /* ++ * 'opts' contains a QemuOptsList with a combination of format and protocol ++ * default values. ++ * ++ * The format properly removes its options, but the default values remain ++ * in 'opts->list'. So if the protocol has options with the same name ++ * (e.g. rbd has 'cluster_size' as qcow2), it will see the default values ++ * of the format, since for overlapping options, the format wins. ++ * ++ * To avoid this issue, lets convert QemuOpts to QDict, in this way we take ++ * only the set options, and then convert it back to QemuOpts, using the ++ * create_opts of the protocol. So the new QemuOpts, will contain only the ++ * protocol defaults. ++ */ ++ qdict = qemu_opts_to_qdict(opts, NULL); ++ protocol_opts = qemu_opts_from_qdict(drv->create_opts, qdict, errp); ++ if (protocol_opts == NULL) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ret = bdrv_co_create(drv, filename, protocol_opts, errp); ++out: ++ qemu_opts_del(protocol_opts); ++ qobject_unref(qdict); ++ return ret; ++} ++ ++int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp) ++{ ++ Error *local_err = NULL; ++ int ret; ++ ++ IO_CODE(); ++ assert(bs != NULL); ++ assert_bdrv_graph_readable(); ++ ++ if (!bs->drv) { ++ error_setg(errp, "Block node '%s' is not opened", bs->filename); ++ return -ENOMEDIUM; ++ } ++ ++ if (!bs->drv->bdrv_co_delete_file) { ++ error_setg(errp, "Driver '%s' does not support image deletion", ++ bs->drv->format_name); ++ return -ENOTSUP; ++ } ++ ++ ret = bs->drv->bdrv_co_delete_file(bs, &local_err); ++ if (ret < 0) { ++ error_propagate(errp, local_err); ++ } ++ ++ return ret; ++} ++ ++void coroutine_fn bdrv_co_delete_file_noerr(BlockDriverState *bs) ++{ ++ Error *local_err = NULL; ++ int ret; ++ IO_CODE(); ++ ++ if (!bs) { ++ return; ++ } ++ ++ ret = bdrv_co_delete_file(bs, &local_err); ++ /* ++ * ENOTSUP will happen if the block driver doesn't support ++ * the 'bdrv_co_delete_file' interface. This is a predictable ++ * scenario and shouldn't be reported back to the user. ++ */ ++ if (ret == -ENOTSUP) { ++ error_free(local_err); ++ } else if (ret < 0) { ++ error_report_err(local_err); ++ } ++} ++ ++/** ++ * Try to get @bs's logical and physical block size. ++ * On success, store them in @bsz struct and return 0. ++ * On failure return -errno. ++ * @bs must not be empty. ++ */ ++int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *filtered = bdrv_filter_bs(bs); ++ GLOBAL_STATE_CODE(); ++ ++ if (drv && drv->bdrv_probe_blocksizes) { ++ return drv->bdrv_probe_blocksizes(bs, bsz); ++ } else if (filtered) { ++ return bdrv_probe_blocksizes(filtered, bsz); ++ } ++ ++ return -ENOTSUP; ++} ++ ++/** ++ * Try to get @bs's geometry (cyls, heads, sectors). ++ * On success, store them in @geo struct and return 0. ++ * On failure return -errno. ++ * @bs must not be empty. ++ */ ++int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *filtered; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (drv && drv->bdrv_probe_geometry) { ++ return drv->bdrv_probe_geometry(bs, geo); ++ } ++ ++ filtered = bdrv_filter_bs(bs); ++ if (filtered) { ++ return bdrv_probe_geometry(filtered, geo); ++ } ++ ++ return -ENOTSUP; ++} ++ ++/* ++ * Create a uniquely-named empty temporary file. ++ * Return the actual file name used upon success, otherwise NULL. ++ * This string should be freed with g_free() when not needed any longer. ++ * ++ * Note: creating a temporary file for the caller to (re)open is ++ * inherently racy. Use g_file_open_tmp() instead whenever practical. ++ */ ++char *create_tmp_file(Error **errp) ++{ ++ int fd; ++ const char *tmpdir; ++ g_autofree char *filename = NULL; ++ ++ tmpdir = g_get_tmp_dir(); ++#ifndef _WIN32 ++ /* ++ * See commit 69bef79 ("block: use /var/tmp instead of /tmp for -snapshot") ++ * ++ * This function is used to create temporary disk images (like -snapshot), ++ * so the files can become very large. /tmp is often a tmpfs where as ++ * /var/tmp is usually on a disk, so more appropriate for disk images. ++ */ ++ if (!g_strcmp0(tmpdir, "/tmp")) { ++ tmpdir = "/var/tmp"; ++ } ++#endif ++ ++ filename = g_strdup_printf("%s/vl.XXXXXX", tmpdir); ++ fd = g_mkstemp(filename); ++ if (fd < 0) { ++ error_setg_errno(errp, errno, "Could not open temporary file '%s'", ++ filename); ++ return NULL; ++ } ++ close(fd); ++ ++ return g_steal_pointer(&filename); ++} ++ ++/* ++ * Detect host devices. By convention, /dev/cdrom[N] is always ++ * recognized as a host CDROM. ++ */ ++static BlockDriver *find_hdev_driver(const char *filename) ++{ ++ int score_max = 0, score; ++ BlockDriver *drv = NULL, *d; ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH(d, &bdrv_drivers, list) { ++ if (d->bdrv_probe_device) { ++ score = d->bdrv_probe_device(filename); ++ if (score > score_max) { ++ score_max = score; ++ drv = d; ++ } ++ } ++ } ++ ++ return drv; ++} ++ ++static BlockDriver *bdrv_do_find_protocol(const char *protocol) ++{ ++ BlockDriver *drv1; ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH(drv1, &bdrv_drivers, list) { ++ if (drv1->protocol_name && !strcmp(drv1->protocol_name, protocol)) { ++ return drv1; ++ } ++ } ++ ++ return NULL; ++} ++ ++BlockDriver *bdrv_find_protocol(const char *filename, ++ bool allow_protocol_prefix, ++ Error **errp) ++{ ++ BlockDriver *drv1; ++ char protocol[128]; ++ int len; ++ const char *p; ++ int i; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * XXX(hch): we really should not let host device detection ++ * override an explicit protocol specification, but moving this ++ * later breaks access to device names with colons in them. ++ * Thanks to the brain-dead persistent naming schemes on udev- ++ * based Linux systems those actually are quite common. ++ */ ++ drv1 = find_hdev_driver(filename); ++ if (drv1) { ++ return drv1; ++ } ++ ++ if (!path_has_protocol(filename) || !allow_protocol_prefix) { ++ return &bdrv_file; ++ } ++ ++ p = strchr(filename, ':'); ++ assert(p != NULL); ++ len = p - filename; ++ if (len > sizeof(protocol) - 1) ++ len = sizeof(protocol) - 1; ++ memcpy(protocol, filename, len); ++ protocol[len] = '\0'; ++ ++ drv1 = bdrv_do_find_protocol(protocol); ++ if (drv1) { ++ return drv1; ++ } ++ ++ for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) { ++ if (block_driver_modules[i].protocol_name && ++ !strcmp(block_driver_modules[i].protocol_name, protocol)) { ++ int rv = block_module_load(block_driver_modules[i].library_name, errp); ++ if (rv > 0) { ++ drv1 = bdrv_do_find_protocol(protocol); ++ } else if (rv < 0) { ++ return NULL; ++ } ++ break; ++ } ++ } ++ ++ if (!drv1) { ++ error_setg(errp, "Unknown protocol '%s'", protocol); ++ } ++ return drv1; ++} ++ ++/* ++ * Guess image format by probing its contents. ++ * This is not a good idea when your image is raw (CVE-2008-2004), but ++ * we do it anyway for backward compatibility. ++ * ++ * @buf contains the image's first @buf_size bytes. ++ * @buf_size is the buffer size in bytes (generally BLOCK_PROBE_BUF_SIZE, ++ * but can be smaller if the image file is smaller) ++ * @filename is its filename. ++ * ++ * For all block drivers, call the bdrv_probe() method to get its ++ * probing score. ++ * Return the first block driver with the highest probing score. ++ */ ++BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, ++ const char *filename) ++{ ++ int score_max = 0, score; ++ BlockDriver *drv = NULL, *d; ++ IO_CODE(); ++ ++ QLIST_FOREACH(d, &bdrv_drivers, list) { ++ if (d->bdrv_probe) { ++ score = d->bdrv_probe(buf, buf_size, filename); ++ if (score > score_max) { ++ score_max = score; ++ drv = d; ++ } ++ } ++ } ++ ++ return drv; ++} ++ ++static int find_image_format(BlockBackend *file, const char *filename, ++ BlockDriver **pdrv, Error **errp) ++{ ++ BlockDriver *drv; ++ uint8_t buf[BLOCK_PROBE_BUF_SIZE]; ++ int ret = 0; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* Return the raw BlockDriver * to scsi-generic devices or empty drives */ ++ if (blk_is_sg(file) || !blk_is_inserted(file) || blk_getlength(file) == 0) { ++ *pdrv = &bdrv_raw; ++ return ret; ++ } ++ ++ ret = blk_pread(file, 0, sizeof(buf), buf, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not read image for determining its " ++ "format"); ++ *pdrv = NULL; ++ return ret; ++ } ++ ++ drv = bdrv_probe_all(buf, sizeof(buf), filename); ++ if (!drv) { ++ error_setg(errp, "Could not determine image format: No compatible " ++ "driver found"); ++ *pdrv = NULL; ++ return -ENOENT; ++ } ++ ++ *pdrv = drv; ++ return 0; ++} ++ ++/** ++ * Set the current 'total_sectors' value ++ * Return 0 on success, -errno on error. ++ */ ++int coroutine_fn bdrv_co_refresh_total_sectors(BlockDriverState *bs, ++ int64_t hint) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ /* Do not attempt drv->bdrv_co_getlength() on scsi-generic devices */ ++ if (bdrv_is_sg(bs)) ++ return 0; ++ ++ /* query actual device if possible, otherwise just trust the hint */ ++ if (drv->bdrv_co_getlength) { ++ int64_t length = drv->bdrv_co_getlength(bs); ++ if (length < 0) { ++ return length; ++ } ++ hint = DIV_ROUND_UP(length, BDRV_SECTOR_SIZE); ++ } ++ ++ bs->total_sectors = hint; ++ ++ if (bs->total_sectors * BDRV_SECTOR_SIZE > BDRV_MAX_LENGTH) { ++ return -EFBIG; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Combines a QDict of new block driver @options with any missing options taken ++ * from @old_options, so that leaving out an option defaults to its old value. ++ */ ++static void bdrv_join_options(BlockDriverState *bs, QDict *options, ++ QDict *old_options) ++{ ++ GLOBAL_STATE_CODE(); ++ if (bs->drv && bs->drv->bdrv_join_options) { ++ bs->drv->bdrv_join_options(options, old_options); ++ } else { ++ qdict_join(options, old_options, false); ++ } ++} ++ ++static BlockdevDetectZeroesOptions bdrv_parse_detect_zeroes(QemuOpts *opts, ++ int open_flags, ++ Error **errp) ++{ ++ Error *local_err = NULL; ++ char *value = qemu_opt_get_del(opts, "detect-zeroes"); ++ BlockdevDetectZeroesOptions detect_zeroes = ++ qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup, value, ++ BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, &local_err); ++ GLOBAL_STATE_CODE(); ++ g_free(value); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return detect_zeroes; ++ } ++ ++ if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && ++ !(open_flags & BDRV_O_UNMAP)) ++ { ++ error_setg(errp, "setting detect-zeroes to unmap is not allowed " ++ "without setting discard operation to unmap"); ++ } ++ ++ return detect_zeroes; ++} ++ ++/** ++ * Set open flags for aio engine ++ * ++ * Return 0 on success, -1 if the engine specified is invalid ++ */ ++int bdrv_parse_aio(const char *mode, int *flags) ++{ ++ if (!strcmp(mode, "threads")) { ++ /* do nothing, default */ ++ } else if (!strcmp(mode, "native")) { ++ *flags |= BDRV_O_NATIVE_AIO; ++#ifdef CONFIG_LINUX_IO_URING ++ } else if (!strcmp(mode, "io_uring")) { ++ *flags |= BDRV_O_IO_URING; ++#endif ++ } else { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Set open flags for a given discard mode ++ * ++ * Return 0 on success, -1 if the discard mode was invalid. ++ */ ++int bdrv_parse_discard_flags(const char *mode, int *flags) ++{ ++ *flags &= ~BDRV_O_UNMAP; ++ ++ if (!strcmp(mode, "off") || !strcmp(mode, "ignore")) { ++ /* do nothing */ ++ } else if (!strcmp(mode, "on") || !strcmp(mode, "unmap")) { ++ *flags |= BDRV_O_UNMAP; ++ } else { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Set open flags for a given cache mode ++ * ++ * Return 0 on success, -1 if the cache mode was invalid. ++ */ ++int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough) ++{ ++ *flags &= ~BDRV_O_CACHE_MASK; ++ ++ if (!strcmp(mode, "off") || !strcmp(mode, "none")) { ++ *writethrough = false; ++ *flags |= BDRV_O_NOCACHE; ++ } else if (!strcmp(mode, "directsync")) { ++ *writethrough = true; ++ *flags |= BDRV_O_NOCACHE; ++ } else if (!strcmp(mode, "writeback")) { ++ *writethrough = false; ++ } else if (!strcmp(mode, "unsafe")) { ++ *writethrough = false; ++ *flags |= BDRV_O_NO_FLUSH; ++ } else if (!strcmp(mode, "writethrough")) { ++ *writethrough = true; ++ } else { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static char *bdrv_child_get_parent_desc(BdrvChild *c) ++{ ++ BlockDriverState *parent = c->opaque; ++ return g_strdup_printf("node '%s'", bdrv_get_node_name(parent)); ++} ++ ++static void GRAPH_RDLOCK bdrv_child_cb_drained_begin(BdrvChild *child) ++{ ++ BlockDriverState *bs = child->opaque; ++ bdrv_do_drained_begin_quiesce(bs, NULL); ++} ++ ++static bool GRAPH_RDLOCK bdrv_child_cb_drained_poll(BdrvChild *child) ++{ ++ BlockDriverState *bs = child->opaque; ++ return bdrv_drain_poll(bs, NULL, false); ++} ++ ++static void GRAPH_RDLOCK bdrv_child_cb_drained_end(BdrvChild *child) ++{ ++ BlockDriverState *bs = child->opaque; ++ bdrv_drained_end(bs); ++} ++ ++static int bdrv_child_cb_inactivate(BdrvChild *child) ++{ ++ BlockDriverState *bs = child->opaque; ++ GLOBAL_STATE_CODE(); ++ assert(bs->open_flags & BDRV_O_INACTIVE); ++ return 0; ++} ++ ++static bool bdrv_child_cb_change_aio_ctx(BdrvChild *child, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp) ++{ ++ BlockDriverState *bs = child->opaque; ++ return bdrv_change_aio_context(bs, ctx, visited, tran, errp); ++} ++ ++/* ++ * Returns the options and flags that a temporary snapshot should get, based on ++ * the originally requested flags (the originally requested image will have ++ * flags like a backing file) ++ */ ++static void bdrv_temp_snapshot_options(int *child_flags, QDict *child_options, ++ int parent_flags, QDict *parent_options) ++{ ++ GLOBAL_STATE_CODE(); ++ *child_flags = (parent_flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY; ++ ++ /* For temporary files, unconditional cache=unsafe is fine */ ++ qdict_set_default_str(child_options, BDRV_OPT_CACHE_DIRECT, "off"); ++ qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on"); ++ ++ /* Copy the read-only and discard options from the parent */ ++ qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY); ++ qdict_copy_default(child_options, parent_options, BDRV_OPT_DISCARD); ++ ++ /* aio=native doesn't work for cache.direct=off, so disable it for the ++ * temporary snapshot */ ++ *child_flags &= ~BDRV_O_NATIVE_AIO; ++} ++ ++static void GRAPH_WRLOCK bdrv_backing_attach(BdrvChild *c) ++{ ++ BlockDriverState *parent = c->opaque; ++ BlockDriverState *backing_hd = c->bs; ++ ++ GLOBAL_STATE_CODE(); ++ assert(!parent->backing_blocker); ++ error_setg(&parent->backing_blocker, ++ "node is used as backing hd of '%s'", ++ bdrv_get_device_or_node_name(parent)); ++ ++ bdrv_refresh_filename(backing_hd); ++ ++ parent->open_flags &= ~BDRV_O_NO_BACKING; ++ ++ bdrv_op_block_all(backing_hd, parent->backing_blocker); ++ /* Otherwise we won't be able to commit or stream */ ++ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET, ++ parent->backing_blocker); ++ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_STREAM, ++ parent->backing_blocker); ++ /* ++ * We do backup in 3 ways: ++ * 1. drive backup ++ * The target bs is new opened, and the source is top BDS ++ * 2. blockdev backup ++ * Both the source and the target are top BDSes. ++ * 3. internal backup(used for block replication) ++ * Both the source and the target are backing file ++ * ++ * In case 1 and 2, neither the source nor the target is the backing file. ++ * In case 3, we will block the top BDS, so there is only one block job ++ * for the top BDS and its backing chain. ++ */ ++ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_SOURCE, ++ parent->backing_blocker); ++ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET, ++ parent->backing_blocker); ++} ++ ++static void bdrv_backing_detach(BdrvChild *c) ++{ ++ BlockDriverState *parent = c->opaque; ++ ++ GLOBAL_STATE_CODE(); ++ assert(parent->backing_blocker); ++ bdrv_op_unblock_all(c->bs, parent->backing_blocker); ++ error_free(parent->backing_blocker); ++ parent->backing_blocker = NULL; ++} ++ ++static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, ++ const char *filename, ++ bool backing_mask_protocol, ++ Error **errp) ++{ ++ BlockDriverState *parent = c->opaque; ++ bool read_only = bdrv_is_read_only(parent); ++ int ret; ++ const char *format_name; ++ GLOBAL_STATE_CODE(); ++ ++ if (read_only) { ++ ret = bdrv_reopen_set_read_only(parent, false, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ if (base->drv) { ++ /* ++ * If the new base image doesn't have a format driver layer, which we ++ * detect by the fact that @base is a protocol driver, we record ++ * 'raw' as the format instead of putting the protocol name as the ++ * backing format ++ */ ++ if (backing_mask_protocol && base->drv->protocol_name) { ++ format_name = "raw"; ++ } else { ++ format_name = base->drv->format_name; ++ } ++ } else { ++ format_name = ""; ++ } ++ ++ ret = bdrv_change_backing_file(parent, filename, format_name, false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not update backing file link"); ++ } ++ ++ if (read_only) { ++ bdrv_reopen_set_read_only(parent, true, NULL); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Returns the options and flags that a generic child of a BDS should ++ * get, based on the given options and flags for the parent BDS. ++ */ ++static void bdrv_inherited_options(BdrvChildRole role, bool parent_is_format, ++ int *child_flags, QDict *child_options, ++ int parent_flags, QDict *parent_options) ++{ ++ int flags = parent_flags; ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * First, decide whether to set, clear, or leave BDRV_O_PROTOCOL. ++ * Generally, the question to answer is: Should this child be ++ * format-probed by default? ++ */ ++ ++ /* ++ * Pure and non-filtered data children of non-format nodes should ++ * be probed by default (even when the node itself has BDRV_O_PROTOCOL ++ * set). This only affects a very limited set of drivers (namely ++ * quorum and blkverify when this comment was written). ++ * Force-clear BDRV_O_PROTOCOL then. ++ */ ++ if (!parent_is_format && ++ (role & BDRV_CHILD_DATA) && ++ !(role & (BDRV_CHILD_METADATA | BDRV_CHILD_FILTERED))) ++ { ++ flags &= ~BDRV_O_PROTOCOL; ++ } ++ ++ /* ++ * All children of format nodes (except for COW children) and all ++ * metadata children in general should never be format-probed. ++ * Force-set BDRV_O_PROTOCOL then. ++ */ ++ if ((parent_is_format && !(role & BDRV_CHILD_COW)) || ++ (role & BDRV_CHILD_METADATA)) ++ { ++ flags |= BDRV_O_PROTOCOL; ++ } ++ ++ /* ++ * If the cache mode isn't explicitly set, inherit direct and no-flush from ++ * the parent. ++ */ ++ qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT); ++ qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH); ++ qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE); ++ ++ if (role & BDRV_CHILD_COW) { ++ /* backing files are opened read-only by default */ ++ qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on"); ++ qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off"); ++ } else { ++ /* Inherit the read-only option from the parent if it's not set */ ++ qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY); ++ qdict_copy_default(child_options, parent_options, ++ BDRV_OPT_AUTO_READ_ONLY); ++ } ++ ++ /* ++ * bdrv_co_pdiscard() respects unmap policy for the parent, so we ++ * can default to enable it on lower layers regardless of the ++ * parent option. ++ */ ++ qdict_set_default_str(child_options, BDRV_OPT_DISCARD, "unmap"); ++ ++ /* Clear flags that only apply to the top layer */ ++ flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ); ++ ++ if (role & BDRV_CHILD_METADATA) { ++ flags &= ~BDRV_O_NO_IO; ++ } ++ if (role & BDRV_CHILD_COW) { ++ flags &= ~BDRV_O_TEMPORARY; ++ } ++ ++ *child_flags = flags; ++} ++ ++static void GRAPH_WRLOCK bdrv_child_cb_attach(BdrvChild *child) ++{ ++ BlockDriverState *bs = child->opaque; ++ ++ assert_bdrv_graph_writable(); ++ QLIST_INSERT_HEAD(&bs->children, child, next); ++ if (bs->drv->is_filter || (child->role & BDRV_CHILD_FILTERED)) { ++ /* ++ * Here we handle filters and block/raw-format.c when it behave like ++ * filter. They generally have a single PRIMARY child, which is also the ++ * FILTERED child, and that they may have multiple more children, which ++ * are neither PRIMARY nor FILTERED. And never we have a COW child here. ++ * So bs->file will be the PRIMARY child, unless the PRIMARY child goes ++ * into bs->backing on exceptional cases; and bs->backing will be ++ * nothing else. ++ */ ++ assert(!(child->role & BDRV_CHILD_COW)); ++ if (child->role & BDRV_CHILD_PRIMARY) { ++ assert(child->role & BDRV_CHILD_FILTERED); ++ assert(!bs->backing); ++ assert(!bs->file); ++ ++ if (bs->drv->filtered_child_is_backing) { ++ bs->backing = child; ++ } else { ++ bs->file = child; ++ } ++ } else { ++ assert(!(child->role & BDRV_CHILD_FILTERED)); ++ } ++ } else if (child->role & BDRV_CHILD_COW) { ++ assert(bs->drv->supports_backing); ++ assert(!(child->role & BDRV_CHILD_PRIMARY)); ++ assert(!bs->backing); ++ bs->backing = child; ++ bdrv_backing_attach(child); ++ } else if (child->role & BDRV_CHILD_PRIMARY) { ++ assert(!bs->file); ++ bs->file = child; ++ } ++} ++ ++static void GRAPH_WRLOCK bdrv_child_cb_detach(BdrvChild *child) ++{ ++ BlockDriverState *bs = child->opaque; ++ ++ if (child->role & BDRV_CHILD_COW) { ++ bdrv_backing_detach(child); ++ } ++ ++ assert_bdrv_graph_writable(); ++ QLIST_REMOVE(child, next); ++ if (child == bs->backing) { ++ assert(child != bs->file); ++ bs->backing = NULL; ++ } else if (child == bs->file) { ++ bs->file = NULL; ++ } ++} ++ ++static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base, ++ const char *filename, ++ bool backing_mask_protocol, ++ Error **errp) ++{ ++ if (c->role & BDRV_CHILD_COW) { ++ return bdrv_backing_update_filename(c, base, filename, ++ backing_mask_protocol, ++ errp); ++ } ++ return 0; ++} ++ ++AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c) ++{ ++ BlockDriverState *bs = c->opaque; ++ IO_CODE(); ++ ++ return bdrv_get_aio_context(bs); ++} ++ ++const BdrvChildClass child_of_bds = { ++ .parent_is_bds = true, ++ .get_parent_desc = bdrv_child_get_parent_desc, ++ .inherit_options = bdrv_inherited_options, ++ .drained_begin = bdrv_child_cb_drained_begin, ++ .drained_poll = bdrv_child_cb_drained_poll, ++ .drained_end = bdrv_child_cb_drained_end, ++ .attach = bdrv_child_cb_attach, ++ .detach = bdrv_child_cb_detach, ++ .inactivate = bdrv_child_cb_inactivate, ++ .change_aio_ctx = bdrv_child_cb_change_aio_ctx, ++ .update_filename = bdrv_child_cb_update_filename, ++ .get_parent_aio_context = child_of_bds_get_parent_aio_context, ++}; ++ ++AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c) ++{ ++ IO_CODE(); ++ return c->klass->get_parent_aio_context(c); ++} ++ ++static int bdrv_open_flags(BlockDriverState *bs, int flags) ++{ ++ int open_flags = flags; ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * Clear flags that are internal to the block layer before opening the ++ * image. ++ */ ++ open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_PROTOCOL); ++ ++ return open_flags; ++} ++ ++static void update_flags_from_options(int *flags, QemuOpts *opts) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ *flags &= ~(BDRV_O_CACHE_MASK | BDRV_O_RDWR | BDRV_O_AUTO_RDONLY); ++ ++ if (qemu_opt_get_bool_del(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) { ++ *flags |= BDRV_O_NO_FLUSH; ++ } ++ ++ if (qemu_opt_get_bool_del(opts, BDRV_OPT_CACHE_DIRECT, false)) { ++ *flags |= BDRV_O_NOCACHE; ++ } ++ ++ if (!qemu_opt_get_bool_del(opts, BDRV_OPT_READ_ONLY, false)) { ++ *flags |= BDRV_O_RDWR; ++ } ++ ++ if (qemu_opt_get_bool_del(opts, BDRV_OPT_AUTO_READ_ONLY, false)) { ++ *flags |= BDRV_O_AUTO_RDONLY; ++ } ++} ++ ++static void update_options_from_flags(QDict *options, int flags) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!qdict_haskey(options, BDRV_OPT_CACHE_DIRECT)) { ++ qdict_put_bool(options, BDRV_OPT_CACHE_DIRECT, flags & BDRV_O_NOCACHE); ++ } ++ if (!qdict_haskey(options, BDRV_OPT_CACHE_NO_FLUSH)) { ++ qdict_put_bool(options, BDRV_OPT_CACHE_NO_FLUSH, ++ flags & BDRV_O_NO_FLUSH); ++ } ++ if (!qdict_haskey(options, BDRV_OPT_READ_ONLY)) { ++ qdict_put_bool(options, BDRV_OPT_READ_ONLY, !(flags & BDRV_O_RDWR)); ++ } ++ if (!qdict_haskey(options, BDRV_OPT_AUTO_READ_ONLY)) { ++ qdict_put_bool(options, BDRV_OPT_AUTO_READ_ONLY, ++ flags & BDRV_O_AUTO_RDONLY); ++ } ++} ++ ++static void bdrv_assign_node_name(BlockDriverState *bs, ++ const char *node_name, ++ Error **errp) ++{ ++ char *gen_node_name = NULL; ++ GLOBAL_STATE_CODE(); ++ ++ if (!node_name) { ++ node_name = gen_node_name = id_generate(ID_BLOCK); ++ } else if (!id_wellformed(node_name)) { ++ /* ++ * Check for empty string or invalid characters, but not if it is ++ * generated (generated names use characters not available to the user) ++ */ ++ error_setg(errp, "Invalid node-name: '%s'", node_name); ++ return; ++ } ++ ++ /* takes care of avoiding namespaces collisions */ ++ if (blk_by_name(node_name)) { ++ error_setg(errp, "node-name=%s is conflicting with a device id", ++ node_name); ++ goto out; ++ } ++ ++ /* takes care of avoiding duplicates node names */ ++ if (bdrv_find_node(node_name)) { ++ error_setg(errp, "Duplicate nodes with node-name='%s'", node_name); ++ goto out; ++ } ++ ++ /* Make sure that the node name isn't truncated */ ++ if (strlen(node_name) >= sizeof(bs->node_name)) { ++ error_setg(errp, "Node name too long"); ++ goto out; ++ } ++ ++ /* copy node name into the bs and insert it into the graph list */ ++ pstrcpy(bs->node_name, sizeof(bs->node_name), node_name); ++ QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list); ++out: ++ g_free(gen_node_name); ++} ++ ++static int no_coroutine_fn GRAPH_UNLOCKED ++bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, ++ QDict *options, int open_flags, Error **errp) ++{ ++ Error *local_err = NULL; ++ int i, ret; ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_assign_node_name(bs, node_name, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ ++ bs->drv = drv; ++ bs->opaque = g_malloc0(drv->instance_size); ++ ++ assert(!drv->bdrv_needs_filename || bs->filename[0]); ++ if (drv->bdrv_open) { ++ ret = drv->bdrv_open(bs, options, open_flags, &local_err); ++ } else { ++ ret = 0; ++ } ++ ++ if (ret < 0) { ++ if (local_err) { ++ error_propagate(errp, local_err); ++ } else if (bs->filename[0]) { ++ error_setg_errno(errp, -ret, "Could not open '%s'", bs->filename); ++ } else { ++ error_setg_errno(errp, -ret, "Could not open image"); ++ } ++ goto open_failed; ++ } ++ ++ assert(!(bs->supported_read_flags & ~BDRV_REQ_MASK)); ++ assert(!(bs->supported_write_flags & ~BDRV_REQ_MASK)); ++ ++ /* ++ * Always allow the BDRV_REQ_REGISTERED_BUF optimization hint. This saves ++ * drivers that pass read/write requests through to a child the trouble of ++ * declaring support explicitly. ++ * ++ * Drivers must not propagate this flag accidentally when they initiate I/O ++ * to a bounce buffer. That case should be rare though. ++ */ ++ bs->supported_read_flags |= BDRV_REQ_REGISTERED_BUF; ++ bs->supported_write_flags |= BDRV_REQ_REGISTERED_BUF; ++ ++ ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not refresh total sector count"); ++ return ret; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_refresh_limits(bs, NULL, &local_err); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ ++ assert(bdrv_opt_mem_align(bs) != 0); ++ assert(bdrv_min_mem_align(bs) != 0); ++ assert(is_power_of_2(bs->bl.request_alignment)); ++ ++ for (i = 0; i < bs->quiesce_counter; i++) { ++ if (drv->bdrv_drain_begin) { ++ drv->bdrv_drain_begin(bs); ++ } ++ } ++ ++ return 0; ++open_failed: ++ bs->drv = NULL; ++ ++ bdrv_graph_wrlock(); ++ if (bs->file != NULL) { ++ bdrv_unref_child(bs, bs->file); ++ assert(!bs->file); ++ } ++ bdrv_graph_wrunlock(); ++ ++ g_free(bs->opaque); ++ bs->opaque = NULL; ++ return ret; ++} ++ ++/* ++ * Create and open a block node. ++ * ++ * @options is a QDict of options to pass to the block drivers, or NULL for an ++ * empty set of options. The reference to the QDict belongs to the block layer ++ * after the call (even on failure), so if the caller intends to reuse the ++ * dictionary, it needs to use qobject_ref() before calling bdrv_open. ++ */ ++BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, ++ const char *node_name, ++ QDict *options, int flags, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bs = bdrv_new(); ++ bs->open_flags = flags; ++ bs->options = options ?: qdict_new(); ++ bs->explicit_options = qdict_clone_shallow(bs->options); ++ bs->opaque = NULL; ++ ++ update_options_from_flags(bs->options, flags); ++ ++ ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp); ++ if (ret < 0) { ++ qobject_unref(bs->explicit_options); ++ bs->explicit_options = NULL; ++ qobject_unref(bs->options); ++ bs->options = NULL; ++ bdrv_unref(bs); ++ return NULL; ++ } ++ ++ return bs; ++} ++ ++/* Create and open a block node. */ ++BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, ++ int flags, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_new_open_driver_opts(drv, node_name, NULL, flags, errp); ++} ++ ++QemuOptsList bdrv_runtime_opts = { ++ .name = "bdrv_common", ++ .head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head), ++ .desc = { ++ { ++ .name = "node-name", ++ .type = QEMU_OPT_STRING, ++ .help = "Node name of the block device node", ++ }, ++ { ++ .name = "driver", ++ .type = QEMU_OPT_STRING, ++ .help = "Block driver to use for the node", ++ }, ++ { ++ .name = BDRV_OPT_CACHE_DIRECT, ++ .type = QEMU_OPT_BOOL, ++ .help = "Bypass software writeback cache on the host", ++ }, ++ { ++ .name = BDRV_OPT_CACHE_NO_FLUSH, ++ .type = QEMU_OPT_BOOL, ++ .help = "Ignore flush requests", ++ }, ++ { ++ .name = BDRV_OPT_READ_ONLY, ++ .type = QEMU_OPT_BOOL, ++ .help = "Node is opened in read-only mode", ++ }, ++ { ++ .name = BDRV_OPT_AUTO_READ_ONLY, ++ .type = QEMU_OPT_BOOL, ++ .help = "Node can become read-only if opening read-write fails", ++ }, ++ { ++ .name = "detect-zeroes", ++ .type = QEMU_OPT_STRING, ++ .help = "try to optimize zero writes (off, on, unmap)", ++ }, ++ { ++ .name = BDRV_OPT_DISCARD, ++ .type = QEMU_OPT_STRING, ++ .help = "discard operation (ignore/off, unmap/on)", ++ }, ++ { ++ .name = BDRV_OPT_FORCE_SHARE, ++ .type = QEMU_OPT_BOOL, ++ .help = "always accept other writers (default: off)", ++ }, ++ { /* end of list */ } ++ }, ++}; ++ ++QemuOptsList bdrv_create_opts_simple = { ++ .name = "simple-create-opts", ++ .head = QTAILQ_HEAD_INITIALIZER(bdrv_create_opts_simple.head), ++ .desc = { ++ { ++ .name = BLOCK_OPT_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Virtual disk size" ++ }, ++ { ++ .name = BLOCK_OPT_PREALLOC, ++ .type = QEMU_OPT_STRING, ++ .help = "Preallocation mode (allowed values: off)" ++ }, ++ { /* end of list */ } ++ } ++}; ++ ++/* ++ * Common part for opening disk images and files ++ * ++ * Removes all processed options from *options. ++ */ ++static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, ++ QDict *options, Error **errp) ++{ ++ int ret, open_flags; ++ const char *filename; ++ const char *driver_name = NULL; ++ const char *node_name = NULL; ++ const char *discard; ++ QemuOpts *opts; ++ BlockDriver *drv; ++ Error *local_err = NULL; ++ bool ro; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_graph_rdlock_main_loop(); ++ assert(bs->file == NULL); ++ assert(options != NULL && bs->options != options); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_absorb_qdict(opts, options, errp)) { ++ ret = -EINVAL; ++ goto fail_opts; ++ } ++ ++ update_flags_from_options(&bs->open_flags, opts); ++ ++ driver_name = qemu_opt_get(opts, "driver"); ++ drv = bdrv_find_format(driver_name); ++ assert(drv != NULL); ++ ++ bs->force_share = qemu_opt_get_bool(opts, BDRV_OPT_FORCE_SHARE, false); ++ ++ if (bs->force_share && (bs->open_flags & BDRV_O_RDWR)) { ++ error_setg(errp, ++ BDRV_OPT_FORCE_SHARE ++ "=on can only be used with read-only images"); ++ ret = -EINVAL; ++ goto fail_opts; ++ } ++ ++ if (file != NULL) { ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_refresh_filename(blk_bs(file)); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ filename = blk_bs(file)->filename; ++ } else { ++ /* ++ * Caution: while qdict_get_try_str() is fine, getting ++ * non-string types would require more care. When @options ++ * come from -blockdev or blockdev_add, its members are typed ++ * according to the QAPI schema, but when they come from ++ * -drive, they're all QString. ++ */ ++ filename = qdict_get_try_str(options, "filename"); ++ } ++ ++ if (drv->bdrv_needs_filename && (!filename || !filename[0])) { ++ error_setg(errp, "The '%s' block driver requires a file name", ++ drv->format_name); ++ ret = -EINVAL; ++ goto fail_opts; ++ } ++ ++ trace_bdrv_open_common(bs, filename ?: "", bs->open_flags, ++ drv->format_name); ++ ++ ro = bdrv_is_read_only(bs); ++ ++ if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, ro)) { ++ if (!ro && bdrv_is_whitelisted(drv, true)) { ++ bdrv_graph_rdlock_main_loop(); ++ ret = bdrv_apply_auto_read_only(bs, NULL, NULL); ++ bdrv_graph_rdunlock_main_loop(); ++ } else { ++ ret = -ENOTSUP; ++ } ++ if (ret < 0) { ++ error_setg(errp, ++ !ro && bdrv_is_whitelisted(drv, true) ++ ? "Driver '%s' can only be used for read-only devices" ++ : "Driver '%s' is not whitelisted", ++ drv->format_name); ++ goto fail_opts; ++ } ++ } ++ ++ /* bdrv_new() and bdrv_close() make it so */ ++ assert(qatomic_read(&bs->copy_on_read) == 0); ++ ++ if (bs->open_flags & BDRV_O_COPY_ON_READ) { ++ if (!ro) { ++ bdrv_enable_copy_on_read(bs); ++ } else { ++ error_setg(errp, "Can't use copy-on-read on read-only device"); ++ ret = -EINVAL; ++ goto fail_opts; ++ } ++ } ++ ++ discard = qemu_opt_get(opts, BDRV_OPT_DISCARD); ++ if (discard != NULL) { ++ if (bdrv_parse_discard_flags(discard, &bs->open_flags) != 0) { ++ error_setg(errp, "Invalid discard option"); ++ ret = -EINVAL; ++ goto fail_opts; ++ } ++ } ++ ++ bs->detect_zeroes = ++ bdrv_parse_detect_zeroes(opts, bs->open_flags, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ ret = -EINVAL; ++ goto fail_opts; ++ } ++ ++ if (filename != NULL) { ++ pstrcpy(bs->filename, sizeof(bs->filename), filename); ++ } else { ++ bs->filename[0] = '\0'; ++ } ++ pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->filename); ++ ++ /* Open the image, either directly or using a protocol */ ++ open_flags = bdrv_open_flags(bs, bs->open_flags); ++ node_name = qemu_opt_get(opts, "node-name"); ++ ++ assert(!drv->protocol_name || file == NULL); ++ ret = bdrv_open_driver(bs, drv, node_name, options, open_flags, errp); ++ if (ret < 0) { ++ goto fail_opts; ++ } ++ ++ qemu_opts_del(opts); ++ return 0; ++ ++fail_opts: ++ qemu_opts_del(opts); ++ return ret; ++} ++ ++static QDict *parse_json_filename(const char *filename, Error **errp) ++{ ++ ERRP_GUARD(); ++ QObject *options_obj; ++ QDict *options; ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ ret = strstart(filename, "json:", &filename); ++ assert(ret); ++ ++ options_obj = qobject_from_json(filename, errp); ++ if (!options_obj) { ++ error_prepend(errp, "Could not parse the JSON options: "); ++ return NULL; ++ } ++ ++ options = qobject_to(QDict, options_obj); ++ if (!options) { ++ qobject_unref(options_obj); ++ error_setg(errp, "Invalid JSON object given"); ++ return NULL; ++ } ++ ++ qdict_flatten(options); ++ ++ return options; ++} ++ ++static void parse_json_protocol(QDict *options, const char **pfilename, ++ Error **errp) ++{ ++ QDict *json_options; ++ Error *local_err = NULL; ++ GLOBAL_STATE_CODE(); ++ ++ /* Parse json: pseudo-protocol */ ++ if (!*pfilename || !g_str_has_prefix(*pfilename, "json:")) { ++ return; ++ } ++ ++ json_options = parse_json_filename(*pfilename, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ /* Options given in the filename have lower priority than options ++ * specified directly */ ++ qdict_join(options, json_options, false); ++ qobject_unref(json_options); ++ *pfilename = NULL; ++} ++ ++/* ++ * Fills in default options for opening images and converts the legacy ++ * filename/flags pair to option QDict entries. ++ * The BDRV_O_PROTOCOL flag in *flags will be set or cleared accordingly if a ++ * block driver has been specified explicitly. ++ */ ++static int bdrv_fill_options(QDict **options, const char *filename, ++ int *flags, bool allow_parse_filename, ++ Error **errp) ++{ ++ const char *drvname; ++ bool protocol = *flags & BDRV_O_PROTOCOL; ++ bool parse_filename = false; ++ BlockDriver *drv = NULL; ++ Error *local_err = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * Caution: while qdict_get_try_str() is fine, getting non-string ++ * types would require more care. When @options come from ++ * -blockdev or blockdev_add, its members are typed according to ++ * the QAPI schema, but when they come from -drive, they're all ++ * QString. ++ */ ++ drvname = qdict_get_try_str(*options, "driver"); ++ if (drvname) { ++ drv = bdrv_find_format(drvname); ++ if (!drv) { ++ error_setg(errp, "Unknown driver '%s'", drvname); ++ return -ENOENT; ++ } ++ /* If the user has explicitly specified the driver, this choice should ++ * override the BDRV_O_PROTOCOL flag */ ++ protocol = drv->protocol_name; ++ } ++ ++ if (protocol) { ++ *flags |= BDRV_O_PROTOCOL; ++ } else { ++ *flags &= ~BDRV_O_PROTOCOL; ++ } ++ ++ /* Translate cache options from flags into options */ ++ update_options_from_flags(*options, *flags); ++ ++ /* Fetch the file name from the options QDict if necessary */ ++ if (protocol && filename) { ++ if (!qdict_haskey(*options, "filename")) { ++ qdict_put_str(*options, "filename", filename); ++ parse_filename = allow_parse_filename; ++ } else { ++ error_setg(errp, "Can't specify 'file' and 'filename' options at " ++ "the same time"); ++ return -EINVAL; ++ } ++ } ++ ++ /* Find the right block driver */ ++ /* See cautionary note on accessing @options above */ ++ filename = qdict_get_try_str(*options, "filename"); ++ ++ if (!drvname && protocol) { ++ if (filename) { ++ drv = bdrv_find_protocol(filename, parse_filename, errp); ++ if (!drv) { ++ return -EINVAL; ++ } ++ ++ drvname = drv->format_name; ++ qdict_put_str(*options, "driver", drvname); ++ } else { ++ error_setg(errp, "Must specify either driver or file"); ++ return -EINVAL; ++ } ++ } ++ ++ assert(drv || !protocol); ++ ++ /* Driver-specific filename parsing */ ++ if (drv && drv->bdrv_parse_filename && parse_filename) { ++ drv->bdrv_parse_filename(filename, *options, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ ++ if (!drv->bdrv_needs_filename) { ++ qdict_del(*options, "filename"); ++ } ++ } ++ ++ return 0; ++} ++ ++typedef struct BlockReopenQueueEntry { ++ bool prepared; ++ BDRVReopenState state; ++ QTAILQ_ENTRY(BlockReopenQueueEntry) entry; ++} BlockReopenQueueEntry; ++ ++/* ++ * Return the flags that @bs will have after the reopens in @q have ++ * successfully completed. If @q is NULL (or @bs is not contained in @q), ++ * return the current flags. ++ */ ++static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs) ++{ ++ BlockReopenQueueEntry *entry; ++ ++ if (q != NULL) { ++ QTAILQ_FOREACH(entry, q, entry) { ++ if (entry->state.bs == bs) { ++ return entry->state.flags; ++ } ++ } ++ } ++ ++ return bs->open_flags; ++} ++ ++/* Returns whether the image file can be written to after the reopen queue @q ++ * has been successfully applied, or right now if @q is NULL. */ ++static bool bdrv_is_writable_after_reopen(BlockDriverState *bs, ++ BlockReopenQueue *q) ++{ ++ int flags = bdrv_reopen_get_flags(q, bs); ++ ++ return (flags & (BDRV_O_RDWR | BDRV_O_INACTIVE)) == BDRV_O_RDWR; ++} ++ ++/* ++ * Return whether the BDS can be written to. This is not necessarily ++ * the same as !bdrv_is_read_only(bs), as inactivated images may not ++ * be written to but do not count as read-only images. ++ */ ++bool bdrv_is_writable(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bdrv_is_writable_after_reopen(bs, NULL); ++} ++ ++static char *bdrv_child_user_desc(BdrvChild *c) ++{ ++ GLOBAL_STATE_CODE(); ++ return c->klass->get_parent_desc(c); ++} ++ ++/* ++ * Check that @a allows everything that @b needs. @a and @b must reference same ++ * child node. ++ */ ++static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp) ++{ ++ const char *child_bs_name; ++ g_autofree char *a_user = NULL; ++ g_autofree char *b_user = NULL; ++ g_autofree char *perms = NULL; ++ ++ assert(a->bs); ++ assert(a->bs == b->bs); ++ GLOBAL_STATE_CODE(); ++ ++ if ((b->perm & a->shared_perm) == b->perm) { ++ return true; ++ } ++ ++ child_bs_name = bdrv_get_node_name(b->bs); ++ a_user = bdrv_child_user_desc(a); ++ b_user = bdrv_child_user_desc(b); ++ perms = bdrv_perm_names(b->perm & ~a->shared_perm); ++ ++ error_setg(errp, "Permission conflict on node '%s': permissions '%s' are " ++ "both required by %s (uses node '%s' as '%s' child) and " ++ "unshared by %s (uses node '%s' as '%s' child).", ++ child_bs_name, perms, ++ b_user, child_bs_name, b->name, ++ a_user, child_bs_name, a->name); ++ ++ return false; ++} ++ ++static bool GRAPH_RDLOCK ++bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp) ++{ ++ BdrvChild *a, *b; ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * During the loop we'll look at each pair twice. That's correct because ++ * bdrv_a_allow_b() is asymmetric and we should check each pair in both ++ * directions. ++ */ ++ QLIST_FOREACH(a, &bs->parents, next_parent) { ++ QLIST_FOREACH(b, &bs->parents, next_parent) { ++ if (a == b) { ++ continue; ++ } ++ ++ if (!bdrv_a_allow_b(a, b, errp)) { ++ return true; ++ } ++ } ++ } ++ ++ return false; ++} ++ ++static void GRAPH_RDLOCK ++bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, ++ BdrvChild *c, BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t parent_perm, uint64_t parent_shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ assert(bs->drv && bs->drv->bdrv_child_perm); ++ GLOBAL_STATE_CODE(); ++ bs->drv->bdrv_child_perm(bs, c, role, reopen_queue, ++ parent_perm, parent_shared, ++ nperm, nshared); ++ /* TODO Take force_share from reopen_queue */ ++ if (child_bs && child_bs->force_share) { ++ *nshared = BLK_PERM_ALL; ++ } ++} ++ ++/* ++ * Adds the whole subtree of @bs (including @bs itself) to the @list (except for ++ * nodes that are already in the @list, of course) so that final list is ++ * topologically sorted. Return the result (GSList @list object is updated, so ++ * don't use old reference after function call). ++ * ++ * On function start @list must be already topologically sorted and for any node ++ * in the @list the whole subtree of the node must be in the @list as well. The ++ * simplest way to satisfy this criteria: use only result of ++ * bdrv_topological_dfs() or NULL as @list parameter. ++ */ ++static GSList * GRAPH_RDLOCK ++bdrv_topological_dfs(GSList *list, GHashTable *found, BlockDriverState *bs) ++{ ++ BdrvChild *child; ++ g_autoptr(GHashTable) local_found = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!found) { ++ assert(!list); ++ found = local_found = g_hash_table_new(NULL, NULL); ++ } ++ ++ if (g_hash_table_contains(found, bs)) { ++ return list; ++ } ++ g_hash_table_add(found, bs); ++ ++ QLIST_FOREACH(child, &bs->children, next) { ++ list = bdrv_topological_dfs(list, found, child->bs); ++ } ++ ++ return g_slist_prepend(list, bs); ++} ++ ++typedef struct BdrvChildSetPermState { ++ BdrvChild *child; ++ uint64_t old_perm; ++ uint64_t old_shared_perm; ++} BdrvChildSetPermState; ++ ++static void bdrv_child_set_perm_abort(void *opaque) ++{ ++ BdrvChildSetPermState *s = opaque; ++ ++ GLOBAL_STATE_CODE(); ++ ++ s->child->perm = s->old_perm; ++ s->child->shared_perm = s->old_shared_perm; ++} ++ ++static TransactionActionDrv bdrv_child_set_pem_drv = { ++ .abort = bdrv_child_set_perm_abort, ++ .clean = g_free, ++}; ++ ++static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, ++ uint64_t shared, Transaction *tran) ++{ ++ BdrvChildSetPermState *s = g_new(BdrvChildSetPermState, 1); ++ GLOBAL_STATE_CODE(); ++ ++ *s = (BdrvChildSetPermState) { ++ .child = c, ++ .old_perm = c->perm, ++ .old_shared_perm = c->shared_perm, ++ }; ++ ++ c->perm = perm; ++ c->shared_perm = shared; ++ ++ tran_add(tran, &bdrv_child_set_pem_drv, s); ++} ++ ++static void GRAPH_RDLOCK bdrv_drv_set_perm_commit(void *opaque) ++{ ++ BlockDriverState *bs = opaque; ++ uint64_t cumulative_perms, cumulative_shared_perms; ++ GLOBAL_STATE_CODE(); ++ ++ if (bs->drv->bdrv_set_perm) { ++ bdrv_get_cumulative_perm(bs, &cumulative_perms, ++ &cumulative_shared_perms); ++ bs->drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms); ++ } ++} ++ ++static void GRAPH_RDLOCK bdrv_drv_set_perm_abort(void *opaque) ++{ ++ BlockDriverState *bs = opaque; ++ GLOBAL_STATE_CODE(); ++ ++ if (bs->drv->bdrv_abort_perm_update) { ++ bs->drv->bdrv_abort_perm_update(bs); ++ } ++} ++ ++TransactionActionDrv bdrv_drv_set_perm_drv = { ++ .abort = bdrv_drv_set_perm_abort, ++ .commit = bdrv_drv_set_perm_commit, ++}; ++ ++/* ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a reader lock for the graph. ++ */ ++static int GRAPH_RDLOCK ++bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, ++ Transaction *tran, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!bs->drv) { ++ return 0; ++ } ++ ++ if (bs->drv->bdrv_check_perm) { ++ int ret = bs->drv->bdrv_check_perm(bs, perm, shared_perm, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ if (tran) { ++ tran_add(tran, &bdrv_drv_set_perm_drv, bs); ++ } ++ ++ return 0; ++} ++ ++typedef struct BdrvReplaceChildState { ++ BdrvChild *child; ++ BlockDriverState *old_bs; ++} BdrvReplaceChildState; ++ ++static void GRAPH_WRLOCK bdrv_replace_child_commit(void *opaque) ++{ ++ BdrvReplaceChildState *s = opaque; ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_schedule_unref(s->old_bs); ++} ++ ++static void GRAPH_WRLOCK bdrv_replace_child_abort(void *opaque) ++{ ++ BdrvReplaceChildState *s = opaque; ++ BlockDriverState *new_bs = s->child->bs; ++ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_writable(); ++ ++ /* old_bs reference is transparently moved from @s to @s->child */ ++ if (!s->child->bs) { ++ /* ++ * The parents were undrained when removing old_bs from the child. New ++ * requests can't have been made, though, because the child was empty. ++ * ++ * TODO Make bdrv_replace_child_noperm() transactionable to avoid ++ * undraining the parent in the first place. Once this is done, having ++ * new_bs drained when calling bdrv_replace_child_tran() is not a ++ * requirement any more. ++ */ ++ bdrv_parent_drained_begin_single(s->child); ++ assert(!bdrv_parent_drained_poll_single(s->child)); ++ } ++ assert(s->child->quiesced_parent); ++ bdrv_replace_child_noperm(s->child, s->old_bs); ++ ++ bdrv_unref(new_bs); ++} ++ ++static TransactionActionDrv bdrv_replace_child_drv = { ++ .commit = bdrv_replace_child_commit, ++ .abort = bdrv_replace_child_abort, ++ .clean = g_free, ++}; ++ ++/* ++ * bdrv_replace_child_tran ++ * ++ * Note: real unref of old_bs is done only on commit. ++ * ++ * Both @child->bs and @new_bs (if non-NULL) must be drained. @new_bs must be ++ * kept drained until the transaction is completed. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ * ++ * The function doesn't update permissions, caller is responsible for this. ++ */ ++static void GRAPH_WRLOCK ++bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, ++ Transaction *tran) ++{ ++ BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1); ++ ++ assert(child->quiesced_parent); ++ assert(!new_bs || new_bs->quiesce_counter); ++ ++ *s = (BdrvReplaceChildState) { ++ .child = child, ++ .old_bs = child->bs, ++ }; ++ tran_add(tran, &bdrv_replace_child_drv, s); ++ ++ if (new_bs) { ++ bdrv_ref(new_bs); ++ } ++ ++ bdrv_replace_child_noperm(child, new_bs); ++ /* old_bs reference is transparently moved from @child to @s */ ++} ++ ++/* ++ * Refresh permissions in @bs subtree. The function is intended to be called ++ * after some graph modification that was done without permission update. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a reader lock for the graph. ++ */ ++static int GRAPH_RDLOCK ++bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q, ++ Transaction *tran, Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ BdrvChild *c; ++ int ret; ++ uint64_t cumulative_perms, cumulative_shared_perms; ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms); ++ ++ /* Write permissions never work with read-only images */ ++ if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && ++ !bdrv_is_writable_after_reopen(bs, q)) ++ { ++ if (!bdrv_is_writable_after_reopen(bs, NULL)) { ++ error_setg(errp, "Block node is read-only"); ++ } else { ++ error_setg(errp, "Read-only block node '%s' cannot support " ++ "read-write users", bdrv_get_node_name(bs)); ++ } ++ ++ return -EPERM; ++ } ++ ++ /* ++ * Unaligned requests will automatically be aligned to bl.request_alignment ++ * and without RESIZE we can't extend requests to write to space beyond the ++ * end of the image, so it's required that the image size is aligned. ++ */ ++ if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && ++ !(cumulative_perms & BLK_PERM_RESIZE)) ++ { ++ if ((bs->total_sectors * BDRV_SECTOR_SIZE) % bs->bl.request_alignment) { ++ error_setg(errp, "Cannot get 'write' permission without 'resize': " ++ "Image size is not a multiple of request " ++ "alignment"); ++ return -EPERM; ++ } ++ } ++ ++ /* Check this node */ ++ if (!drv) { ++ return 0; ++ } ++ ++ ret = bdrv_drv_set_perm(bs, cumulative_perms, cumulative_shared_perms, tran, ++ errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Drivers that never have children can omit .bdrv_child_perm() */ ++ if (!drv->bdrv_child_perm) { ++ assert(QLIST_EMPTY(&bs->children)); ++ return 0; ++ } ++ ++ /* Check all children */ ++ QLIST_FOREACH(c, &bs->children, next) { ++ uint64_t cur_perm, cur_shared; ++ ++ bdrv_child_perm(bs, c->bs, c, c->role, q, ++ cumulative_perms, cumulative_shared_perms, ++ &cur_perm, &cur_shared); ++ bdrv_child_set_perm(c, cur_perm, cur_shared, tran); ++ } ++ ++ return 0; ++} ++ ++/* ++ * @list is a product of bdrv_topological_dfs() (may be called several times) - ++ * a topologically sorted subgraph. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a reader lock for the graph. ++ */ ++static int GRAPH_RDLOCK ++bdrv_do_refresh_perms(GSList *list, BlockReopenQueue *q, Transaction *tran, ++ Error **errp) ++{ ++ int ret; ++ BlockDriverState *bs; ++ GLOBAL_STATE_CODE(); ++ ++ for ( ; list; list = list->next) { ++ bs = list->data; ++ ++ if (bdrv_parent_perms_conflict(bs, errp)) { ++ return -EINVAL; ++ } ++ ++ ret = bdrv_node_refresh_perm(bs, q, tran, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * @list is any list of nodes. List is completed by all subtrees and ++ * topologically sorted. It's not a problem if some node occurs in the @list ++ * several times. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a reader lock for the graph. ++ */ ++static int GRAPH_RDLOCK ++bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, Transaction *tran, ++ Error **errp) ++{ ++ g_autoptr(GHashTable) found = g_hash_table_new(NULL, NULL); ++ g_autoptr(GSList) refresh_list = NULL; ++ ++ for ( ; list; list = list->next) { ++ refresh_list = bdrv_topological_dfs(refresh_list, found, list->data); ++ } ++ ++ return bdrv_do_refresh_perms(refresh_list, q, tran, errp); ++} ++ ++void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, ++ uint64_t *shared_perm) ++{ ++ BdrvChild *c; ++ uint64_t cumulative_perms = 0; ++ uint64_t cumulative_shared_perms = BLK_PERM_ALL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ cumulative_perms |= c->perm; ++ cumulative_shared_perms &= c->shared_perm; ++ } ++ ++ *perm = cumulative_perms; ++ *shared_perm = cumulative_shared_perms; ++} ++ ++char *bdrv_perm_names(uint64_t perm) ++{ ++ struct perm_name { ++ uint64_t perm; ++ const char *name; ++ } permissions[] = { ++ { BLK_PERM_CONSISTENT_READ, "consistent read" }, ++ { BLK_PERM_WRITE, "write" }, ++ { BLK_PERM_WRITE_UNCHANGED, "write unchanged" }, ++ { BLK_PERM_RESIZE, "resize" }, ++ { 0, NULL } ++ }; ++ ++ GString *result = g_string_sized_new(30); ++ struct perm_name *p; ++ ++ for (p = permissions; p->name; p++) { ++ if (perm & p->perm) { ++ if (result->len > 0) { ++ g_string_append(result, ", "); ++ } ++ g_string_append(result, p->name); ++ } ++ } ++ ++ return g_string_free(result, FALSE); ++} ++ ++ ++/* ++ * @tran is allowed to be NULL. In this case no rollback is possible. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a reader lock for the graph. ++ */ ++static int GRAPH_RDLOCK ++bdrv_refresh_perms(BlockDriverState *bs, Transaction *tran, Error **errp) ++{ ++ int ret; ++ Transaction *local_tran = NULL; ++ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); ++ GLOBAL_STATE_CODE(); ++ ++ if (!tran) { ++ tran = local_tran = tran_new(); ++ } ++ ++ ret = bdrv_do_refresh_perms(list, NULL, tran, errp); ++ ++ if (local_tran) { ++ tran_finalize(local_tran, ret); ++ } ++ ++ return ret; ++} ++ ++int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, ++ Error **errp) ++{ ++ Error *local_err = NULL; ++ Transaction *tran = tran_new(); ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_child_set_perm(c, perm, shared, tran); ++ ++ ret = bdrv_refresh_perms(c->bs, tran, &local_err); ++ ++ tran_finalize(tran, ret); ++ ++ if (ret < 0) { ++ if ((perm & ~c->perm) || (c->shared_perm & ~shared)) { ++ /* tighten permissions */ ++ error_propagate(errp, local_err); ++ } else { ++ /* ++ * Our caller may intend to only loosen restrictions and ++ * does not expect this function to fail. Errors are not ++ * fatal in such a case, so we can just hide them from our ++ * caller. ++ */ ++ error_free(local_err); ++ ret = 0; ++ } ++ } ++ ++ return ret; ++} ++ ++int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp) ++{ ++ uint64_t parent_perms, parent_shared; ++ uint64_t perms, shared; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_get_cumulative_perm(bs, &parent_perms, &parent_shared); ++ bdrv_child_perm(bs, c->bs, c, c->role, NULL, ++ parent_perms, parent_shared, &perms, &shared); ++ ++ return bdrv_child_try_set_perm(c, perms, shared, errp); ++} ++ ++/* ++ * Default implementation for .bdrv_child_perm() for block filters: ++ * Forward CONSISTENT_READ, WRITE, WRITE_UNCHANGED, and RESIZE to the ++ * filtered child. ++ */ ++static void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ GLOBAL_STATE_CODE(); ++ *nperm = perm & DEFAULT_PERM_PASSTHROUGH; ++ *nshared = (shared & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED; ++} ++ ++static void bdrv_default_perms_for_cow(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ assert(role & BDRV_CHILD_COW); ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * We want consistent read from backing files if the parent needs it. ++ * No other operations are performed on backing files. ++ */ ++ perm &= BLK_PERM_CONSISTENT_READ; ++ ++ /* ++ * If the parent can deal with changing data, we're okay with a ++ * writable and resizable backing file. ++ * TODO Require !(perm & BLK_PERM_CONSISTENT_READ), too? ++ */ ++ if (shared & BLK_PERM_WRITE) { ++ shared = BLK_PERM_WRITE | BLK_PERM_RESIZE; ++ } else { ++ shared = 0; ++ } ++ ++ shared |= BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED; ++ ++ if (bs->open_flags & BDRV_O_INACTIVE) { ++ shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE; ++ } ++ ++ *nperm = perm; ++ *nshared = shared; ++} ++ ++static void bdrv_default_perms_for_storage(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ int flags; ++ ++ GLOBAL_STATE_CODE(); ++ assert(role & (BDRV_CHILD_METADATA | BDRV_CHILD_DATA)); ++ ++ flags = bdrv_reopen_get_flags(reopen_queue, bs); ++ ++ /* ++ * Apart from the modifications below, the same permissions are ++ * forwarded and left alone as for filters ++ */ ++ bdrv_filter_default_perms(bs, c, role, reopen_queue, ++ perm, shared, &perm, &shared); ++ ++ if (role & BDRV_CHILD_METADATA) { ++ /* Format drivers may touch metadata even if the guest doesn't write */ ++ if (bdrv_is_writable_after_reopen(bs, reopen_queue)) { ++ perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE; ++ } ++ ++ /* ++ * bs->file always needs to be consistent because of the ++ * metadata. We can never allow other users to resize or write ++ * to it. ++ */ ++ if (!(flags & BDRV_O_NO_IO)) { ++ perm |= BLK_PERM_CONSISTENT_READ; ++ } ++ shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); ++ } ++ ++ if (role & BDRV_CHILD_DATA) { ++ /* ++ * Technically, everything in this block is a subset of the ++ * BDRV_CHILD_METADATA path taken above, and so this could ++ * be an "else if" branch. However, that is not obvious, and ++ * this function is not performance critical, therefore we let ++ * this be an independent "if". ++ */ ++ ++ /* ++ * We cannot allow other users to resize the file because the ++ * format driver might have some assumptions about the size ++ * (e.g. because it is stored in metadata, or because the file ++ * is split into fixed-size data files). ++ */ ++ shared &= ~BLK_PERM_RESIZE; ++ ++ /* ++ * WRITE_UNCHANGED often cannot be performed as such on the ++ * data file. For example, the qcow2 driver may still need to ++ * write copied clusters on copy-on-read. ++ */ ++ if (perm & BLK_PERM_WRITE_UNCHANGED) { ++ perm |= BLK_PERM_WRITE; ++ } ++ ++ /* ++ * If the data file is written to, the format driver may ++ * expect to be able to resize it by writing beyond the EOF. ++ */ ++ if (perm & BLK_PERM_WRITE) { ++ perm |= BLK_PERM_RESIZE; ++ } ++ } ++ ++ if (bs->open_flags & BDRV_O_INACTIVE) { ++ shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE; ++ } ++ ++ *nperm = perm; ++ *nshared = shared; ++} ++ ++void bdrv_default_perms(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ GLOBAL_STATE_CODE(); ++ if (role & BDRV_CHILD_FILTERED) { ++ assert(!(role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA | ++ BDRV_CHILD_COW))); ++ bdrv_filter_default_perms(bs, c, role, reopen_queue, ++ perm, shared, nperm, nshared); ++ } else if (role & BDRV_CHILD_COW) { ++ assert(!(role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA))); ++ bdrv_default_perms_for_cow(bs, c, role, reopen_queue, ++ perm, shared, nperm, nshared); ++ } else if (role & (BDRV_CHILD_METADATA | BDRV_CHILD_DATA)) { ++ bdrv_default_perms_for_storage(bs, c, role, reopen_queue, ++ perm, shared, nperm, nshared); ++ } else { ++ g_assert_not_reached(); ++ } ++} ++ ++uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm) ++{ ++ static const uint64_t permissions[] = { ++ [BLOCK_PERMISSION_CONSISTENT_READ] = BLK_PERM_CONSISTENT_READ, ++ [BLOCK_PERMISSION_WRITE] = BLK_PERM_WRITE, ++ [BLOCK_PERMISSION_WRITE_UNCHANGED] = BLK_PERM_WRITE_UNCHANGED, ++ [BLOCK_PERMISSION_RESIZE] = BLK_PERM_RESIZE, ++ }; ++ ++ QEMU_BUILD_BUG_ON(ARRAY_SIZE(permissions) != BLOCK_PERMISSION__MAX); ++ QEMU_BUILD_BUG_ON(1UL << ARRAY_SIZE(permissions) != BLK_PERM_ALL + 1); ++ ++ assert(qapi_perm < BLOCK_PERMISSION__MAX); ++ ++ return permissions[qapi_perm]; ++} ++ ++/* ++ * Replaces the node that a BdrvChild points to without updating permissions. ++ * ++ * If @new_bs is non-NULL, the parent of @child must already be drained through ++ * @child. ++ */ ++static void GRAPH_WRLOCK ++bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs) ++{ ++ BlockDriverState *old_bs = child->bs; ++ int new_bs_quiesce_counter; ++ ++ assert(!child->frozen); ++ ++ /* ++ * If we want to change the BdrvChild to point to a drained node as its new ++ * child->bs, we need to make sure that its new parent is drained, too. In ++ * other words, either child->quiesce_parent must already be true or we must ++ * be able to set it and keep the parent's quiesce_counter consistent with ++ * that, but without polling or starting new requests (this function ++ * guarantees that it doesn't poll, and starting new requests would be ++ * against the invariants of drain sections). ++ * ++ * To keep things simple, we pick the first option (child->quiesce_parent ++ * must already be true). We also generalise the rule a bit to make it ++ * easier to verify in callers and more likely to be covered in test cases: ++ * The parent must be quiesced through this child even if new_bs isn't ++ * currently drained. ++ * ++ * The only exception is for callers that always pass new_bs == NULL. In ++ * this case, we obviously never need to consider the case of a drained ++ * new_bs, so we can keep the callers simpler by allowing them not to drain ++ * the parent. ++ */ ++ assert(!new_bs || child->quiesced_parent); ++ assert(old_bs != new_bs); ++ GLOBAL_STATE_CODE(); ++ ++ if (old_bs && new_bs) { ++ assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); ++ } ++ ++ if (old_bs) { ++ if (child->klass->detach) { ++ child->klass->detach(child); ++ } ++ QLIST_REMOVE(child, next_parent); ++ } ++ ++ child->bs = new_bs; ++ ++ if (new_bs) { ++ QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); ++ if (child->klass->attach) { ++ child->klass->attach(child); ++ } ++ } ++ ++ /* ++ * If the parent was drained through this BdrvChild previously, but new_bs ++ * is not drained, allow requests to come in only after the new node has ++ * been attached. ++ */ ++ new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0); ++ if (!new_bs_quiesce_counter && child->quiesced_parent) { ++ bdrv_parent_drained_end_single(child); ++ } ++} ++ ++/** ++ * Free the given @child. ++ * ++ * The child must be empty (i.e. `child->bs == NULL`) and it must be ++ * unused (i.e. not in a children list). ++ */ ++static void bdrv_child_free(BdrvChild *child) ++{ ++ assert(!child->bs); ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ assert(!child->next.le_prev); /* not in children list */ ++ ++ g_free(child->name); ++ g_free(child); ++} ++ ++typedef struct BdrvAttachChildCommonState { ++ BdrvChild *child; ++ AioContext *old_parent_ctx; ++ AioContext *old_child_ctx; ++} BdrvAttachChildCommonState; ++ ++static void GRAPH_WRLOCK bdrv_attach_child_common_abort(void *opaque) ++{ ++ BdrvAttachChildCommonState *s = opaque; ++ BlockDriverState *bs = s->child->bs; ++ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_writable(); ++ ++ bdrv_replace_child_noperm(s->child, NULL); ++ ++ if (bdrv_get_aio_context(bs) != s->old_child_ctx) { ++ bdrv_try_change_aio_context(bs, s->old_child_ctx, NULL, &error_abort); ++ } ++ ++ if (bdrv_child_get_parent_aio_context(s->child) != s->old_parent_ctx) { ++ Transaction *tran; ++ GHashTable *visited; ++ bool ret; ++ ++ tran = tran_new(); ++ ++ /* No need to visit `child`, because it has been detached already */ ++ visited = g_hash_table_new(NULL, NULL); ++ ret = s->child->klass->change_aio_ctx(s->child, s->old_parent_ctx, ++ visited, tran, &error_abort); ++ g_hash_table_destroy(visited); ++ ++ /* transaction is supposed to always succeed */ ++ assert(ret == true); ++ tran_commit(tran); ++ } ++ ++ bdrv_schedule_unref(bs); ++ bdrv_child_free(s->child); ++} ++ ++static TransactionActionDrv bdrv_attach_child_common_drv = { ++ .abort = bdrv_attach_child_common_abort, ++ .clean = g_free, ++}; ++ ++/* ++ * Common part of attaching bdrv child to bs or to blk or to job ++ * ++ * Function doesn't update permissions, caller is responsible for this. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ * ++ * Returns new created child. ++ * ++ * Both @parent_bs and @child_bs can move to a different AioContext in this ++ * function. ++ */ ++static BdrvChild * GRAPH_WRLOCK ++bdrv_attach_child_common(BlockDriverState *child_bs, ++ const char *child_name, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ uint64_t perm, uint64_t shared_perm, ++ void *opaque, ++ Transaction *tran, Error **errp) ++{ ++ BdrvChild *new_child; ++ AioContext *parent_ctx; ++ AioContext *child_ctx = bdrv_get_aio_context(child_bs); ++ ++ assert(child_class->get_parent_desc); ++ GLOBAL_STATE_CODE(); ++ ++ new_child = g_new(BdrvChild, 1); ++ *new_child = (BdrvChild) { ++ .bs = NULL, ++ .name = g_strdup(child_name), ++ .klass = child_class, ++ .role = child_role, ++ .perm = perm, ++ .shared_perm = shared_perm, ++ .opaque = opaque, ++ }; ++ ++ /* ++ * If the AioContexts don't match, first try to move the subtree of ++ * child_bs into the AioContext of the new parent. If this doesn't work, ++ * try moving the parent into the AioContext of child_bs instead. ++ */ ++ parent_ctx = bdrv_child_get_parent_aio_context(new_child); ++ if (child_ctx != parent_ctx) { ++ Error *local_err = NULL; ++ int ret = bdrv_try_change_aio_context(child_bs, parent_ctx, NULL, ++ &local_err); ++ ++ if (ret < 0 && child_class->change_aio_ctx) { ++ Transaction *aio_ctx_tran = tran_new(); ++ GHashTable *visited = g_hash_table_new(NULL, NULL); ++ bool ret_child; ++ ++ g_hash_table_add(visited, new_child); ++ ret_child = child_class->change_aio_ctx(new_child, child_ctx, ++ visited, aio_ctx_tran, ++ NULL); ++ if (ret_child == true) { ++ error_free(local_err); ++ ret = 0; ++ } ++ tran_finalize(aio_ctx_tran, ret_child == true ? 0 : -1); ++ g_hash_table_destroy(visited); ++ } ++ ++ if (ret < 0) { ++ error_propagate(errp, local_err); ++ bdrv_child_free(new_child); ++ return NULL; ++ } ++ } ++ ++ bdrv_ref(child_bs); ++ /* ++ * Let every new BdrvChild start with a drained parent. Inserting the child ++ * in the graph with bdrv_replace_child_noperm() will undrain it if ++ * @child_bs is not drained. ++ * ++ * The child was only just created and is not yet visible in global state ++ * until bdrv_replace_child_noperm() inserts it into the graph, so nobody ++ * could have sent requests and polling is not necessary. ++ * ++ * Note that this means that the parent isn't fully drained yet, we only ++ * stop new requests from coming in. This is fine, we don't care about the ++ * old requests here, they are not for this child. If another place enters a ++ * drain section for the same parent, but wants it to be fully quiesced, it ++ * will not run most of the the code in .drained_begin() again (which is not ++ * a problem, we already did this), but it will still poll until the parent ++ * is fully quiesced, so it will not be negatively affected either. ++ */ ++ bdrv_parent_drained_begin_single(new_child); ++ bdrv_replace_child_noperm(new_child, child_bs); ++ ++ BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1); ++ *s = (BdrvAttachChildCommonState) { ++ .child = new_child, ++ .old_parent_ctx = parent_ctx, ++ .old_child_ctx = child_ctx, ++ }; ++ tran_add(tran, &bdrv_attach_child_common_drv, s); ++ ++ return new_child; ++} ++ ++/* ++ * Function doesn't update permissions, caller is responsible for this. ++ * ++ * Both @parent_bs and @child_bs can move to a different AioContext in this ++ * function. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ */ ++static BdrvChild * GRAPH_WRLOCK ++bdrv_attach_child_noperm(BlockDriverState *parent_bs, ++ BlockDriverState *child_bs, ++ const char *child_name, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ Transaction *tran, ++ Error **errp) ++{ ++ uint64_t perm, shared_perm; ++ ++ assert(parent_bs->drv); ++ GLOBAL_STATE_CODE(); ++ ++ if (bdrv_recurse_has_child(child_bs, parent_bs)) { ++ error_setg(errp, "Making '%s' a %s child of '%s' would create a cycle", ++ child_bs->node_name, child_name, parent_bs->node_name); ++ return NULL; ++ } ++ ++ bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm); ++ bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL, ++ perm, shared_perm, &perm, &shared_perm); ++ ++ return bdrv_attach_child_common(child_bs, child_name, child_class, ++ child_role, perm, shared_perm, parent_bs, ++ tran, errp); ++} ++ ++/* ++ * This function steals the reference to child_bs from the caller. ++ * That reference is later dropped by bdrv_root_unref_child(). ++ * ++ * On failure NULL is returned, errp is set and the reference to ++ * child_bs is also dropped. ++ */ ++BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, ++ const char *child_name, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ uint64_t perm, uint64_t shared_perm, ++ void *opaque, Error **errp) ++{ ++ int ret; ++ BdrvChild *child; ++ Transaction *tran = tran_new(); ++ ++ GLOBAL_STATE_CODE(); ++ ++ child = bdrv_attach_child_common(child_bs, child_name, child_class, ++ child_role, perm, shared_perm, opaque, ++ tran, errp); ++ if (!child) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ret = bdrv_refresh_perms(child_bs, tran, errp); ++ ++out: ++ tran_finalize(tran, ret); ++ ++ bdrv_schedule_unref(child_bs); ++ ++ return ret < 0 ? NULL : child; ++} ++ ++/* ++ * This function transfers the reference to child_bs from the caller ++ * to parent_bs. That reference is later dropped by parent_bs on ++ * bdrv_close() or if someone calls bdrv_unref_child(). ++ * ++ * On failure NULL is returned, errp is set and the reference to ++ * child_bs is also dropped. ++ */ ++BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, ++ BlockDriverState *child_bs, ++ const char *child_name, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ Error **errp) ++{ ++ int ret; ++ BdrvChild *child; ++ Transaction *tran = tran_new(); ++ ++ GLOBAL_STATE_CODE(); ++ ++ child = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, ++ child_class, child_role, tran, errp); ++ if (!child) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ret = bdrv_refresh_perms(parent_bs, tran, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ ++out: ++ tran_finalize(tran, ret); ++ ++ bdrv_schedule_unref(child_bs); ++ ++ return ret < 0 ? NULL : child; ++} ++ ++/* Callers must ensure that child->frozen is false. */ ++void bdrv_root_unref_child(BdrvChild *child) ++{ ++ BlockDriverState *child_bs = child->bs; ++ ++ GLOBAL_STATE_CODE(); ++ bdrv_replace_child_noperm(child, NULL); ++ bdrv_child_free(child); ++ ++ if (child_bs) { ++ /* ++ * Update permissions for old node. We're just taking a parent away, so ++ * we're loosening restrictions. Errors of permission update are not ++ * fatal in this case, ignore them. ++ */ ++ bdrv_refresh_perms(child_bs, NULL, NULL); ++ ++ /* ++ * When the parent requiring a non-default AioContext is removed, the ++ * node moves back to the main AioContext ++ */ ++ bdrv_try_change_aio_context(child_bs, qemu_get_aio_context(), NULL, ++ NULL); ++ } ++ ++ bdrv_schedule_unref(child_bs); ++} ++ ++typedef struct BdrvSetInheritsFrom { ++ BlockDriverState *bs; ++ BlockDriverState *old_inherits_from; ++} BdrvSetInheritsFrom; ++ ++static void bdrv_set_inherits_from_abort(void *opaque) ++{ ++ BdrvSetInheritsFrom *s = opaque; ++ ++ s->bs->inherits_from = s->old_inherits_from; ++} ++ ++static TransactionActionDrv bdrv_set_inherits_from_drv = { ++ .abort = bdrv_set_inherits_from_abort, ++ .clean = g_free, ++}; ++ ++/* @tran is allowed to be NULL. In this case no rollback is possible */ ++static void bdrv_set_inherits_from(BlockDriverState *bs, ++ BlockDriverState *new_inherits_from, ++ Transaction *tran) ++{ ++ if (tran) { ++ BdrvSetInheritsFrom *s = g_new(BdrvSetInheritsFrom, 1); ++ ++ *s = (BdrvSetInheritsFrom) { ++ .bs = bs, ++ .old_inherits_from = bs->inherits_from, ++ }; ++ ++ tran_add(tran, &bdrv_set_inherits_from_drv, s); ++ } ++ ++ bs->inherits_from = new_inherits_from; ++} ++ ++/** ++ * Clear all inherits_from pointers from children and grandchildren of ++ * @root that point to @root, where necessary. ++ * @tran is allowed to be NULL. In this case no rollback is possible ++ */ ++static void GRAPH_WRLOCK ++bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child, ++ Transaction *tran) ++{ ++ BdrvChild *c; ++ ++ if (child->bs->inherits_from == root) { ++ /* ++ * Remove inherits_from only when the last reference between root and ++ * child->bs goes away. ++ */ ++ QLIST_FOREACH(c, &root->children, next) { ++ if (c != child && c->bs == child->bs) { ++ break; ++ } ++ } ++ if (c == NULL) { ++ bdrv_set_inherits_from(child->bs, NULL, tran); ++ } ++ } ++ ++ QLIST_FOREACH(c, &child->bs->children, next) { ++ bdrv_unset_inherits_from(root, c, tran); ++ } ++} ++ ++/* Callers must ensure that child->frozen is false. */ ++void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) ++{ ++ GLOBAL_STATE_CODE(); ++ if (child == NULL) { ++ return; ++ } ++ ++ bdrv_unset_inherits_from(parent, child, NULL); ++ bdrv_root_unref_child(child); ++} ++ ++ ++static void GRAPH_RDLOCK ++bdrv_parent_cb_change_media(BlockDriverState *bs, bool load) ++{ ++ BdrvChild *c; ++ GLOBAL_STATE_CODE(); ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ if (c->klass->change_media) { ++ c->klass->change_media(c, load); ++ } ++ } ++} ++ ++/* Return true if you can reach parent going through child->inherits_from ++ * recursively. If parent or child are NULL, return false */ ++static bool bdrv_inherits_from_recursive(BlockDriverState *child, ++ BlockDriverState *parent) ++{ ++ while (child && child != parent) { ++ child = child->inherits_from; ++ } ++ ++ return child != NULL; ++} ++ ++/* ++ * Return the BdrvChildRole for @bs's backing child. bs->backing is ++ * mostly used for COW backing children (role = COW), but also for ++ * filtered children (role = FILTERED | PRIMARY). ++ */ ++static BdrvChildRole bdrv_backing_role(BlockDriverState *bs) ++{ ++ if (bs->drv && bs->drv->is_filter) { ++ return BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY; ++ } else { ++ return BDRV_CHILD_COW; ++ } ++} ++ ++/* ++ * Sets the bs->backing or bs->file link of a BDS. A new reference is created; ++ * callers which don't need their own reference any more must call bdrv_unref(). ++ * ++ * If the respective child is already present (i.e. we're detaching a node), ++ * that child node must be drained. ++ * ++ * Function doesn't update permissions, caller is responsible for this. ++ * ++ * Both @parent_bs and @child_bs can move to a different AioContext in this ++ * function. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ */ ++static int GRAPH_WRLOCK ++bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, ++ BlockDriverState *child_bs, ++ bool is_backing, ++ Transaction *tran, Error **errp) ++{ ++ bool update_inherits_from = ++ bdrv_inherits_from_recursive(child_bs, parent_bs); ++ BdrvChild *child = is_backing ? parent_bs->backing : parent_bs->file; ++ BdrvChildRole role; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!parent_bs->drv) { ++ /* ++ * Node without drv is an object without a class :/. TODO: finally fix ++ * qcow2 driver to never clear bs->drv and implement format corruption ++ * handling in other way. ++ */ ++ error_setg(errp, "Node corrupted"); ++ return -EINVAL; ++ } ++ ++ if (child && child->frozen) { ++ error_setg(errp, "Cannot change frozen '%s' link from '%s' to '%s'", ++ child->name, parent_bs->node_name, child->bs->node_name); ++ return -EPERM; ++ } ++ ++ if (is_backing && !parent_bs->drv->is_filter && ++ !parent_bs->drv->supports_backing) ++ { ++ error_setg(errp, "Driver '%s' of node '%s' does not support backing " ++ "files", parent_bs->drv->format_name, parent_bs->node_name); ++ return -EINVAL; ++ } ++ ++ if (parent_bs->drv->is_filter) { ++ role = BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY; ++ } else if (is_backing) { ++ role = BDRV_CHILD_COW; ++ } else { ++ /* ++ * We only can use same role as it is in existing child. We don't have ++ * infrastructure to determine role of file child in generic way ++ */ ++ if (!child) { ++ error_setg(errp, "Cannot set file child to format node without " ++ "file child"); ++ return -EINVAL; ++ } ++ role = child->role; ++ } ++ ++ if (child) { ++ assert(child->bs->quiesce_counter); ++ bdrv_unset_inherits_from(parent_bs, child, tran); ++ bdrv_remove_child(child, tran); ++ } ++ ++ if (!child_bs) { ++ goto out; ++ } ++ ++ child = bdrv_attach_child_noperm(parent_bs, child_bs, ++ is_backing ? "backing" : "file", ++ &child_of_bds, role, ++ tran, errp); ++ if (!child) { ++ return -EINVAL; ++ } ++ ++ ++ /* ++ * If inherits_from pointed recursively to bs then let's update it to ++ * point directly to bs (else it will become NULL). ++ */ ++ if (update_inherits_from) { ++ bdrv_set_inherits_from(child_bs, parent_bs, tran); ++ } ++ ++out: ++ bdrv_refresh_limits(parent_bs, tran, NULL); ++ ++ return 0; ++} ++ ++/* ++ * Both @bs and @backing_hd can move to a different AioContext in this ++ * function. ++ * ++ * If a backing child is already present (i.e. we're detaching a node), that ++ * child node must be drained. ++ */ ++int bdrv_set_backing_hd_drained(BlockDriverState *bs, ++ BlockDriverState *backing_hd, ++ Error **errp) ++{ ++ int ret; ++ Transaction *tran = tran_new(); ++ ++ GLOBAL_STATE_CODE(); ++ assert(bs->quiesce_counter > 0); ++ if (bs->backing) { ++ assert(bs->backing->bs->quiesce_counter > 0); ++ } ++ ++ ret = bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = bdrv_refresh_perms(bs, tran, errp); ++out: ++ tran_finalize(tran, ret); ++ return ret; ++} ++ ++int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, ++ Error **errp) ++{ ++ BlockDriverState *drain_bs; ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_graph_rdlock_main_loop(); ++ drain_bs = bs->backing ? bs->backing->bs : bs; ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_ref(drain_bs); ++ bdrv_drained_begin(drain_bs); ++ bdrv_graph_wrlock(); ++ ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(drain_bs); ++ bdrv_unref(drain_bs); ++ ++ return ret; ++} ++ ++/* ++ * Opens the backing file for a BlockDriverState if not yet open ++ * ++ * bdref_key specifies the key for the image's BlockdevRef in the options QDict. ++ * That QDict has to be flattened; therefore, if the BlockdevRef is a QDict ++ * itself, all options starting with "${bdref_key}." are considered part of the ++ * BlockdevRef. ++ * ++ * TODO Can this be unified with bdrv_open_image()? ++ */ ++int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, ++ const char *bdref_key, Error **errp) ++{ ++ ERRP_GUARD(); ++ char *backing_filename = NULL; ++ char *bdref_key_dot; ++ const char *reference = NULL; ++ int ret = 0; ++ bool implicit_backing = false; ++ BlockDriverState *backing_hd; ++ QDict *options; ++ QDict *tmp_parent_options = NULL; ++ Error *local_err = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bs->backing != NULL) { ++ goto free_exit; ++ } ++ ++ /* NULL means an empty set of options */ ++ if (parent_options == NULL) { ++ tmp_parent_options = qdict_new(); ++ parent_options = tmp_parent_options; ++ } ++ ++ bs->open_flags &= ~BDRV_O_NO_BACKING; ++ ++ bdref_key_dot = g_strdup_printf("%s.", bdref_key); ++ qdict_extract_subqdict(parent_options, &options, bdref_key_dot); ++ g_free(bdref_key_dot); ++ ++ /* ++ * Caution: while qdict_get_try_str() is fine, getting non-string ++ * types would require more care. When @parent_options come from ++ * -blockdev or blockdev_add, its members are typed according to ++ * the QAPI schema, but when they come from -drive, they're all ++ * QString. ++ */ ++ reference = qdict_get_try_str(parent_options, bdref_key); ++ if (reference || qdict_haskey(options, "file.filename")) { ++ /* keep backing_filename NULL */ ++ } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { ++ qobject_unref(options); ++ goto free_exit; ++ } else { ++ if (qdict_size(options) == 0) { ++ /* If the user specifies options that do not modify the ++ * backing file's behavior, we might still consider it the ++ * implicit backing file. But it's easier this way, and ++ * just specifying some of the backing BDS's options is ++ * only possible with -drive anyway (otherwise the QAPI ++ * schema forces the user to specify everything). */ ++ implicit_backing = !strcmp(bs->auto_backing_file, bs->backing_file); ++ } ++ ++ backing_filename = bdrv_get_full_backing_filename(bs, &local_err); ++ if (local_err) { ++ ret = -EINVAL; ++ error_propagate(errp, local_err); ++ qobject_unref(options); ++ goto free_exit; ++ } ++ } ++ ++ if (!bs->drv || !bs->drv->supports_backing) { ++ ret = -EINVAL; ++ error_setg(errp, "Driver doesn't support backing files"); ++ qobject_unref(options); ++ goto free_exit; ++ } ++ ++ if (!reference && ++ bs->backing_format[0] != '\0' && !qdict_haskey(options, "driver")) { ++ qdict_put_str(options, "driver", bs->backing_format); ++ } ++ ++ backing_hd = bdrv_open_inherit(backing_filename, reference, options, 0, bs, ++ &child_of_bds, bdrv_backing_role(bs), true, ++ errp); ++ if (!backing_hd) { ++ bs->open_flags |= BDRV_O_NO_BACKING; ++ error_prepend(errp, "Could not open backing file: "); ++ ret = -EINVAL; ++ goto free_exit; ++ } ++ ++ if (implicit_backing) { ++ bdrv_refresh_filename(backing_hd); ++ pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), ++ backing_hd->filename); ++ } ++ ++ /* Hook up the backing file link; drop our reference, bs owns the ++ * backing_hd reference now */ ++ ret = bdrv_set_backing_hd(bs, backing_hd, errp); ++ bdrv_unref(backing_hd); ++ ++ if (ret < 0) { ++ goto free_exit; ++ } ++ ++ qdict_del(parent_options, bdref_key); ++ ++free_exit: ++ g_free(backing_filename); ++ qobject_unref(tmp_parent_options); ++ return ret; ++} ++ ++static BlockDriverState * ++bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, ++ BlockDriverState *parent, const BdrvChildClass *child_class, ++ BdrvChildRole child_role, bool allow_none, ++ bool parse_filename, Error **errp) ++{ ++ BlockDriverState *bs = NULL; ++ QDict *image_options; ++ char *bdref_key_dot; ++ const char *reference; ++ ++ assert(child_class != NULL); ++ ++ bdref_key_dot = g_strdup_printf("%s.", bdref_key); ++ qdict_extract_subqdict(options, &image_options, bdref_key_dot); ++ g_free(bdref_key_dot); ++ ++ /* ++ * Caution: while qdict_get_try_str() is fine, getting non-string ++ * types would require more care. When @options come from ++ * -blockdev or blockdev_add, its members are typed according to ++ * the QAPI schema, but when they come from -drive, they're all ++ * QString. ++ */ ++ reference = qdict_get_try_str(options, bdref_key); ++ if (!filename && !reference && !qdict_size(image_options)) { ++ if (!allow_none) { ++ error_setg(errp, "A block device must be specified for \"%s\"", ++ bdref_key); ++ } ++ qobject_unref(image_options); ++ goto done; ++ } ++ ++ bs = bdrv_open_inherit(filename, reference, image_options, 0, ++ parent, child_class, child_role, parse_filename, ++ errp); ++ if (!bs) { ++ goto done; ++ } ++ ++done: ++ qdict_del(options, bdref_key); ++ return bs; ++} ++ ++static BdrvChild *bdrv_open_child_common(const char *filename, ++ QDict *options, const char *bdref_key, ++ BlockDriverState *parent, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ bool allow_none, bool parse_filename, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_class, ++ child_role, allow_none, parse_filename, errp); ++ if (bs == NULL) { ++ return NULL; ++ } ++ ++ bdrv_graph_wrlock(); ++ child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, ++ errp); ++ bdrv_graph_wrunlock(); ++ ++ return child; ++} ++ ++/* ++ * Opens a disk image whose options are given as BlockdevRef in another block ++ * device's options. ++ * ++ * If allow_none is true, no image will be opened if filename is false and no ++ * BlockdevRef is given. NULL will be returned, but errp remains unset. ++ * ++ * bdrev_key specifies the key for the image's BlockdevRef in the options QDict. ++ * That QDict has to be flattened; therefore, if the BlockdevRef is a QDict ++ * itself, all options starting with "${bdref_key}." are considered part of the ++ * BlockdevRef. ++ * ++ * The BlockdevRef will be removed from the options QDict. ++ * ++ * @parent can move to a different AioContext in this function. ++ */ ++BdrvChild *bdrv_open_child(const char *filename, ++ QDict *options, const char *bdref_key, ++ BlockDriverState *parent, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ bool allow_none, Error **errp) ++{ ++ return bdrv_open_child_common(filename, options, bdref_key, parent, ++ child_class, child_role, allow_none, false, ++ errp); ++} ++ ++/* ++ * This does mostly the same as bdrv_open_child(), but for opening the primary ++ * child of a node. A notable difference from bdrv_open_child() is that it ++ * enables filename parsing for protocol names (including json:). ++ * ++ * @parent can move to a different AioContext in this function. ++ */ ++int bdrv_open_file_child(const char *filename, ++ QDict *options, const char *bdref_key, ++ BlockDriverState *parent, Error **errp) ++{ ++ BdrvChildRole role; ++ ++ /* commit_top and mirror_top don't use this function */ ++ assert(!parent->drv->filtered_child_is_backing); ++ role = parent->drv->is_filter ? ++ (BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY) : BDRV_CHILD_IMAGE; ++ ++ if (!bdrv_open_child_common(filename, options, bdref_key, parent, ++ &child_of_bds, role, false, true, errp)) ++ { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* ++ * TODO Future callers may need to specify parent/child_class in order for ++ * option inheritance to work. Existing callers use it for the root node. ++ */ ++BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) ++{ ++ BlockDriverState *bs = NULL; ++ QObject *obj = NULL; ++ QDict *qdict = NULL; ++ const char *reference = NULL; ++ Visitor *v = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (ref->type == QTYPE_QSTRING) { ++ reference = ref->u.reference; ++ } else { ++ BlockdevOptions *options = &ref->u.definition; ++ assert(ref->type == QTYPE_QDICT); ++ ++ v = qobject_output_visitor_new(&obj); ++ visit_type_BlockdevOptions(v, NULL, &options, &error_abort); ++ visit_complete(v, &obj); ++ ++ qdict = qobject_to(QDict, obj); ++ qdict_flatten(qdict); ++ ++ /* bdrv_open_inherit() defaults to the values in bdrv_flags (for ++ * compatibility with other callers) rather than what we want as the ++ * real defaults. Apply the defaults here instead. */ ++ qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); ++ qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); ++ qdict_set_default_str(qdict, BDRV_OPT_READ_ONLY, "off"); ++ qdict_set_default_str(qdict, BDRV_OPT_AUTO_READ_ONLY, "off"); ++ ++ } ++ ++ bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, 0, false, ++ errp); ++ obj = NULL; ++ qobject_unref(obj); ++ visit_free(v); ++ return bs; ++} ++ ++static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, ++ int flags, ++ QDict *snapshot_options, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ g_autofree char *tmp_filename = NULL; ++ int64_t total_size; ++ QemuOpts *opts = NULL; ++ BlockDriverState *bs_snapshot = NULL; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* if snapshot, we create a temporary backing file and open it ++ instead of opening 'filename' directly */ ++ ++ /* Get the required size from the image */ ++ total_size = bdrv_getlength(bs); ++ ++ if (total_size < 0) { ++ error_setg_errno(errp, -total_size, "Could not get image size"); ++ goto out; ++ } ++ ++ /* Create the temporary image */ ++ tmp_filename = create_tmp_file(errp); ++ if (!tmp_filename) { ++ goto out; ++ } ++ ++ opts = qemu_opts_create(bdrv_qcow2.create_opts, NULL, 0, ++ &error_abort); ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_size, &error_abort); ++ ret = bdrv_create(&bdrv_qcow2, tmp_filename, opts, errp); ++ qemu_opts_del(opts); ++ if (ret < 0) { ++ error_prepend(errp, "Could not create temporary overlay '%s': ", ++ tmp_filename); ++ goto out; ++ } ++ ++ /* Prepare options QDict for the temporary file */ ++ qdict_put_str(snapshot_options, "file.driver", "file"); ++ qdict_put_str(snapshot_options, "file.filename", tmp_filename); ++ qdict_put_str(snapshot_options, "driver", "qcow2"); ++ ++ bs_snapshot = bdrv_open(NULL, NULL, snapshot_options, flags, errp); ++ snapshot_options = NULL; ++ if (!bs_snapshot) { ++ goto out; ++ } ++ ++ ret = bdrv_append(bs_snapshot, bs, errp); ++ if (ret < 0) { ++ bs_snapshot = NULL; ++ goto out; ++ } ++ ++out: ++ qobject_unref(snapshot_options); ++ return bs_snapshot; ++} ++ ++/* ++ * Opens a disk image (raw, qcow2, vmdk, ...) ++ * ++ * options is a QDict of options to pass to the block drivers, or NULL for an ++ * empty set of options. The reference to the QDict belongs to the block layer ++ * after the call (even on failure), so if the caller intends to reuse the ++ * dictionary, it needs to use qobject_ref() before calling bdrv_open. ++ * ++ * If *pbs is NULL, a new BDS will be created with a pointer to it stored there. ++ * If it is not NULL, the referenced BDS will be reused. ++ * ++ * The reference parameter may be used to specify an existing block device which ++ * should be opened. If specified, neither options nor a filename may be given, ++ * nor can an existing BDS be reused (that is, *pbs has to be NULL). ++ */ ++static BlockDriverState * no_coroutine_fn ++bdrv_open_inherit(const char *filename, const char *reference, QDict *options, ++ int flags, BlockDriverState *parent, ++ const BdrvChildClass *child_class, BdrvChildRole child_role, ++ bool parse_filename, Error **errp) ++{ ++ int ret; ++ BlockBackend *file = NULL; ++ BlockDriverState *bs; ++ BlockDriver *drv = NULL; ++ BdrvChild *child; ++ const char *drvname; ++ const char *backing; ++ Error *local_err = NULL; ++ QDict *snapshot_options = NULL; ++ int snapshot_flags = 0; ++ ++ assert(!child_class || !flags); ++ assert(!child_class == !parent); ++ GLOBAL_STATE_CODE(); ++ assert(!qemu_in_coroutine()); ++ ++ /* TODO We'll eventually have to take a writer lock in this function */ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (reference) { ++ bool options_non_empty = options ? qdict_size(options) : false; ++ qobject_unref(options); ++ ++ if (filename || options_non_empty) { ++ error_setg(errp, "Cannot reference an existing block device with " ++ "additional options or a new filename"); ++ return NULL; ++ } ++ ++ bs = bdrv_lookup_bs(reference, reference, errp); ++ if (!bs) { ++ return NULL; ++ } ++ ++ bdrv_ref(bs); ++ return bs; ++ } ++ ++ bs = bdrv_new(); ++ ++ /* NULL means an empty set of options */ ++ if (options == NULL) { ++ options = qdict_new(); ++ } ++ ++ /* json: syntax counts as explicit options, as if in the QDict */ ++ if (parse_filename) { ++ parse_json_protocol(options, &filename, &local_err); ++ if (local_err) { ++ goto fail; ++ } ++ } ++ ++ bs->explicit_options = qdict_clone_shallow(options); ++ ++ if (child_class) { ++ bool parent_is_format; ++ ++ if (parent->drv) { ++ parent_is_format = parent->drv->is_format; ++ } else { ++ /* ++ * parent->drv is not set yet because this node is opened for ++ * (potential) format probing. That means that @parent is going ++ * to be a format node. ++ */ ++ parent_is_format = true; ++ } ++ ++ bs->inherits_from = parent; ++ child_class->inherit_options(child_role, parent_is_format, ++ &flags, options, ++ parent->open_flags, parent->options); ++ } ++ ++ ret = bdrv_fill_options(&options, filename, &flags, parse_filename, ++ &local_err); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* ++ * Set the BDRV_O_RDWR and BDRV_O_ALLOW_RDWR flags. ++ * Caution: getting a boolean member of @options requires care. ++ * When @options come from -blockdev or blockdev_add, members are ++ * typed according to the QAPI schema, but when they come from ++ * -drive, they're all QString. ++ */ ++ if (g_strcmp0(qdict_get_try_str(options, BDRV_OPT_READ_ONLY), "on") && ++ !qdict_get_try_bool(options, BDRV_OPT_READ_ONLY, false)) { ++ flags |= (BDRV_O_RDWR | BDRV_O_ALLOW_RDWR); ++ } else { ++ flags &= ~BDRV_O_RDWR; ++ } ++ ++ if (flags & BDRV_O_SNAPSHOT) { ++ snapshot_options = qdict_new(); ++ bdrv_temp_snapshot_options(&snapshot_flags, snapshot_options, ++ flags, options); ++ /* Let bdrv_backing_options() override "read-only" */ ++ qdict_del(options, BDRV_OPT_READ_ONLY); ++ bdrv_inherited_options(BDRV_CHILD_COW, true, ++ &flags, options, flags, options); ++ } ++ ++ bs->open_flags = flags; ++ bs->options = options; ++ options = qdict_clone_shallow(options); ++ ++ /* Find the right image format driver */ ++ /* See cautionary note on accessing @options above */ ++ drvname = qdict_get_try_str(options, "driver"); ++ if (drvname) { ++ drv = bdrv_find_format(drvname); ++ if (!drv) { ++ error_setg(errp, "Unknown driver: '%s'", drvname); ++ goto fail; ++ } ++ } ++ ++ assert(drvname || !(flags & BDRV_O_PROTOCOL)); ++ ++ /* See cautionary note on accessing @options above */ ++ backing = qdict_get_try_str(options, "backing"); ++ if (qobject_to(QNull, qdict_get(options, "backing")) != NULL || ++ (backing && *backing == '\0')) ++ { ++ if (backing) { ++ warn_report("Use of \"backing\": \"\" is deprecated; " ++ "use \"backing\": null instead"); ++ } ++ flags |= BDRV_O_NO_BACKING; ++ qdict_del(bs->explicit_options, "backing"); ++ qdict_del(bs->options, "backing"); ++ qdict_del(options, "backing"); ++ } ++ ++ /* Open image file without format layer. This BlockBackend is only used for ++ * probing, the block drivers will do their own bdrv_open_child() for the ++ * same BDS, which is why we put the node name back into options. */ ++ if ((flags & BDRV_O_PROTOCOL) == 0) { ++ BlockDriverState *file_bs; ++ ++ file_bs = bdrv_open_child_bs(filename, options, "file", bs, ++ &child_of_bds, BDRV_CHILD_IMAGE, ++ true, true, &local_err); ++ if (local_err) { ++ goto fail; ++ } ++ if (file_bs != NULL) { ++ /* Not requesting BLK_PERM_CONSISTENT_READ because we're only ++ * looking at the header to guess the image format. This works even ++ * in cases where a guest would not see a consistent state. */ ++ AioContext *ctx = bdrv_get_aio_context(file_bs); ++ file = blk_new(ctx, 0, BLK_PERM_ALL); ++ blk_insert_bs(file, file_bs, &local_err); ++ bdrv_unref(file_bs); ++ ++ if (local_err) { ++ goto fail; ++ } ++ ++ qdict_put_str(options, "file", bdrv_get_node_name(file_bs)); ++ } ++ } ++ ++ /* Image format probing */ ++ bs->probed = !drv; ++ if (!drv && file) { ++ ret = find_image_format(file, filename, &drv, &local_err); ++ if (ret < 0) { ++ goto fail; ++ } ++ /* ++ * This option update would logically belong in bdrv_fill_options(), ++ * but we first need to open bs->file for the probing to work, while ++ * opening bs->file already requires the (mostly) final set of options ++ * so that cache mode etc. can be inherited. ++ * ++ * Adding the driver later is somewhat ugly, but it's not an option ++ * that would ever be inherited, so it's correct. We just need to make ++ * sure to update both bs->options (which has the full effective ++ * options for bs) and options (which has file.* already removed). ++ */ ++ qdict_put_str(bs->options, "driver", drv->format_name); ++ qdict_put_str(options, "driver", drv->format_name); ++ } else if (!drv) { ++ error_setg(errp, "Must specify either driver or file"); ++ goto fail; ++ } ++ ++ /* BDRV_O_PROTOCOL must be set iff a protocol BDS is about to be created */ ++ assert(!!(flags & BDRV_O_PROTOCOL) == !!drv->protocol_name); ++ /* file must be NULL if a protocol BDS is about to be created ++ * (the inverse results in an error message from bdrv_open_common()) */ ++ assert(!(flags & BDRV_O_PROTOCOL) || !file); ++ ++ /* Open the image */ ++ ret = bdrv_open_common(bs, file, options, &local_err); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if (file) { ++ blk_unref(file); ++ file = NULL; ++ } ++ ++ /* If there is a backing file, use it */ ++ if ((flags & BDRV_O_NO_BACKING) == 0) { ++ ret = bdrv_open_backing_file(bs, options, "backing", &local_err); ++ if (ret < 0) { ++ goto close_and_fail; ++ } ++ } ++ ++ /* Remove all children options and references ++ * from bs->options and bs->explicit_options */ ++ QLIST_FOREACH(child, &bs->children, next) { ++ char *child_key_dot; ++ child_key_dot = g_strdup_printf("%s.", child->name); ++ qdict_extract_subqdict(bs->explicit_options, NULL, child_key_dot); ++ qdict_extract_subqdict(bs->options, NULL, child_key_dot); ++ qdict_del(bs->explicit_options, child->name); ++ qdict_del(bs->options, child->name); ++ g_free(child_key_dot); ++ } ++ ++ /* Check if any unknown options were used */ ++ if (qdict_size(options) != 0) { ++ const QDictEntry *entry = qdict_first(options); ++ if (flags & BDRV_O_PROTOCOL) { ++ error_setg(errp, "Block protocol '%s' doesn't support the option " ++ "'%s'", drv->format_name, entry->key); ++ } else { ++ error_setg(errp, ++ "Block format '%s' does not support the option '%s'", ++ drv->format_name, entry->key); ++ } ++ ++ goto close_and_fail; ++ } ++ ++ bdrv_parent_cb_change_media(bs, true); ++ ++ qobject_unref(options); ++ options = NULL; ++ ++ /* For snapshot=on, create a temporary qcow2 overlay. bs points to the ++ * temporary snapshot afterwards. */ ++ if (snapshot_flags) { ++ BlockDriverState *snapshot_bs; ++ snapshot_bs = bdrv_append_temp_snapshot(bs, snapshot_flags, ++ snapshot_options, &local_err); ++ snapshot_options = NULL; ++ if (local_err) { ++ goto close_and_fail; ++ } ++ /* We are not going to return bs but the overlay on top of it ++ * (snapshot_bs); thus, we have to drop the strong reference to bs ++ * (which we obtained by calling bdrv_new()). bs will not be deleted, ++ * though, because the overlay still has a reference to it. */ ++ bdrv_unref(bs); ++ bs = snapshot_bs; ++ } ++ ++ return bs; ++ ++fail: ++ blk_unref(file); ++ qobject_unref(snapshot_options); ++ qobject_unref(bs->explicit_options); ++ qobject_unref(bs->options); ++ qobject_unref(options); ++ bs->options = NULL; ++ bs->explicit_options = NULL; ++ bdrv_unref(bs); ++ error_propagate(errp, local_err); ++ return NULL; ++ ++close_and_fail: ++ bdrv_unref(bs); ++ qobject_unref(snapshot_options); ++ qobject_unref(options); ++ error_propagate(errp, local_err); ++ return NULL; ++} ++ ++BlockDriverState *bdrv_open(const char *filename, const char *reference, ++ QDict *options, int flags, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ return bdrv_open_inherit(filename, reference, options, flags, NULL, ++ NULL, 0, true, errp); ++} ++ ++/* Return true if the NULL-terminated @list contains @str */ ++static bool is_str_in_list(const char *str, const char *const *list) ++{ ++ if (str && list) { ++ int i; ++ for (i = 0; list[i] != NULL; i++) { ++ if (!strcmp(str, list[i])) { ++ return true; ++ } ++ } ++ } ++ return false; ++} ++ ++/* ++ * Check that every option set in @bs->options is also set in ++ * @new_opts. ++ * ++ * Options listed in the common_options list and in ++ * @bs->drv->mutable_opts are skipped. ++ * ++ * Return 0 on success, otherwise return -EINVAL and set @errp. ++ */ ++static int bdrv_reset_options_allowed(BlockDriverState *bs, ++ const QDict *new_opts, Error **errp) ++{ ++ const QDictEntry *e; ++ /* These options are common to all block drivers and are handled ++ * in bdrv_reopen_prepare() so they can be left out of @new_opts */ ++ const char *const common_options[] = { ++ "node-name", "discard", "cache.direct", "cache.no-flush", ++ "read-only", "auto-read-only", "detect-zeroes", NULL ++ }; ++ ++ for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) { ++ if (!qdict_haskey(new_opts, e->key) && ++ !is_str_in_list(e->key, common_options) && ++ !is_str_in_list(e->key, bs->drv->mutable_opts)) { ++ error_setg(errp, "Option '%s' cannot be reset " ++ "to its default value", e->key); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Returns true if @child can be reached recursively from @bs ++ */ ++static bool GRAPH_RDLOCK ++bdrv_recurse_has_child(BlockDriverState *bs, BlockDriverState *child) ++{ ++ BdrvChild *c; ++ ++ if (bs == child) { ++ return true; ++ } ++ ++ QLIST_FOREACH(c, &bs->children, next) { ++ if (bdrv_recurse_has_child(c->bs, child)) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++/* ++ * Adds a BlockDriverState to a simple queue for an atomic, transactional ++ * reopen of multiple devices. ++ * ++ * bs_queue can either be an existing BlockReopenQueue that has had QTAILQ_INIT ++ * already performed, or alternatively may be NULL a new BlockReopenQueue will ++ * be created and initialized. This newly created BlockReopenQueue should be ++ * passed back in for subsequent calls that are intended to be of the same ++ * atomic 'set'. ++ * ++ * bs is the BlockDriverState to add to the reopen queue. ++ * ++ * options contains the changed options for the associated bs ++ * (the BlockReopenQueue takes ownership) ++ * ++ * flags contains the open flags for the associated bs ++ * ++ * returns a pointer to bs_queue, which is either the newly allocated ++ * bs_queue, or the existing bs_queue being used. ++ * ++ * bs is drained here and undrained by bdrv_reopen_queue_free(). ++ * ++ * To be called with bs->aio_context locked. ++ */ ++static BlockReopenQueue * GRAPH_RDLOCK ++bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs, ++ QDict *options, const BdrvChildClass *klass, ++ BdrvChildRole role, bool parent_is_format, ++ QDict *parent_options, int parent_flags, ++ bool keep_old_opts) ++{ ++ assert(bs != NULL); ++ ++ BlockReopenQueueEntry *bs_entry; ++ BdrvChild *child; ++ QDict *old_options, *explicit_options, *options_copy; ++ int flags; ++ QemuOpts *opts; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * Strictly speaking, draining is illegal under GRAPH_RDLOCK. We know that ++ * we've been called with bdrv_graph_rdlock_main_loop(), though, so it's ok ++ * in practice. ++ */ ++ bdrv_drained_begin(bs); ++ ++ if (bs_queue == NULL) { ++ bs_queue = g_new0(BlockReopenQueue, 1); ++ QTAILQ_INIT(bs_queue); ++ } ++ ++ if (!options) { ++ options = qdict_new(); ++ } ++ ++ /* Check if this BlockDriverState is already in the queue */ ++ QTAILQ_FOREACH(bs_entry, bs_queue, entry) { ++ if (bs == bs_entry->state.bs) { ++ break; ++ } ++ } ++ ++ /* ++ * Precedence of options: ++ * 1. Explicitly passed in options (highest) ++ * 2. Retained from explicitly set options of bs ++ * 3. Inherited from parent node ++ * 4. Retained from effective options of bs ++ */ ++ ++ /* Old explicitly set values (don't overwrite by inherited value) */ ++ if (bs_entry || keep_old_opts) { ++ old_options = qdict_clone_shallow(bs_entry ? ++ bs_entry->state.explicit_options : ++ bs->explicit_options); ++ bdrv_join_options(bs, options, old_options); ++ qobject_unref(old_options); ++ } ++ ++ explicit_options = qdict_clone_shallow(options); ++ ++ /* Inherit from parent node */ ++ if (parent_options) { ++ flags = 0; ++ klass->inherit_options(role, parent_is_format, &flags, options, ++ parent_flags, parent_options); ++ } else { ++ flags = bdrv_get_flags(bs); ++ } ++ ++ if (keep_old_opts) { ++ /* Old values are used for options that aren't set yet */ ++ old_options = qdict_clone_shallow(bs->options); ++ bdrv_join_options(bs, options, old_options); ++ qobject_unref(old_options); ++ } ++ ++ /* We have the final set of options so let's update the flags */ ++ options_copy = qdict_clone_shallow(options); ++ opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); ++ qemu_opts_absorb_qdict(opts, options_copy, NULL); ++ update_flags_from_options(&flags, opts); ++ qemu_opts_del(opts); ++ qobject_unref(options_copy); ++ ++ /* bdrv_open_inherit() sets and clears some additional flags internally */ ++ flags &= ~BDRV_O_PROTOCOL; ++ if (flags & BDRV_O_RDWR) { ++ flags |= BDRV_O_ALLOW_RDWR; ++ } ++ ++ if (!bs_entry) { ++ bs_entry = g_new0(BlockReopenQueueEntry, 1); ++ QTAILQ_INSERT_TAIL(bs_queue, bs_entry, entry); ++ } else { ++ qobject_unref(bs_entry->state.options); ++ qobject_unref(bs_entry->state.explicit_options); ++ } ++ ++ bs_entry->state.bs = bs; ++ bs_entry->state.options = options; ++ bs_entry->state.explicit_options = explicit_options; ++ bs_entry->state.flags = flags; ++ ++ /* ++ * If keep_old_opts is false then it means that unspecified ++ * options must be reset to their original value. We don't allow ++ * resetting 'backing' but we need to know if the option is ++ * missing in order to decide if we have to return an error. ++ */ ++ if (!keep_old_opts) { ++ bs_entry->state.backing_missing = ++ !qdict_haskey(options, "backing") && ++ !qdict_haskey(options, "backing.driver"); ++ } ++ ++ QLIST_FOREACH(child, &bs->children, next) { ++ QDict *new_child_options = NULL; ++ bool child_keep_old = keep_old_opts; ++ ++ /* reopen can only change the options of block devices that were ++ * implicitly created and inherited options. For other (referenced) ++ * block devices, a syntax like "backing.foo" results in an error. */ ++ if (child->bs->inherits_from != bs) { ++ continue; ++ } ++ ++ /* Check if the options contain a child reference */ ++ if (qdict_haskey(options, child->name)) { ++ const char *childref = qdict_get_try_str(options, child->name); ++ /* ++ * The current child must not be reopened if the child ++ * reference is null or points to a different node. ++ */ ++ if (g_strcmp0(childref, child->bs->node_name)) { ++ continue; ++ } ++ /* ++ * If the child reference points to the current child then ++ * reopen it with its existing set of options (note that ++ * it can still inherit new options from the parent). ++ */ ++ child_keep_old = true; ++ } else { ++ /* Extract child options ("child-name.*") */ ++ char *child_key_dot = g_strdup_printf("%s.", child->name); ++ qdict_extract_subqdict(explicit_options, NULL, child_key_dot); ++ qdict_extract_subqdict(options, &new_child_options, child_key_dot); ++ g_free(child_key_dot); ++ } ++ ++ bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options, ++ child->klass, child->role, bs->drv->is_format, ++ options, flags, child_keep_old); ++ } ++ ++ return bs_queue; ++} ++ ++/* To be called with bs->aio_context locked */ ++BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, ++ BlockDriverState *bs, ++ QDict *options, bool keep_old_opts) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, 0, false, ++ NULL, 0, keep_old_opts); ++} ++ ++void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue) ++{ ++ GLOBAL_STATE_CODE(); ++ if (bs_queue) { ++ BlockReopenQueueEntry *bs_entry, *next; ++ QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { ++ bdrv_drained_end(bs_entry->state.bs); ++ qobject_unref(bs_entry->state.explicit_options); ++ qobject_unref(bs_entry->state.options); ++ g_free(bs_entry); ++ } ++ g_free(bs_queue); ++ } ++} ++ ++/* ++ * Reopen multiple BlockDriverStates atomically & transactionally. ++ * ++ * The queue passed in (bs_queue) must have been built up previous ++ * via bdrv_reopen_queue(). ++ * ++ * Reopens all BDS specified in the queue, with the appropriate ++ * flags. All devices are prepared for reopen, and failure of any ++ * device will cause all device changes to be abandoned, and intermediate ++ * data cleaned up. ++ * ++ * If all devices prepare successfully, then the changes are committed ++ * to all devices. ++ * ++ * All affected nodes must be drained between bdrv_reopen_queue() and ++ * bdrv_reopen_multiple(). ++ * ++ * To be called from the main thread, with all other AioContexts unlocked. ++ */ ++int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) ++{ ++ int ret = -1; ++ BlockReopenQueueEntry *bs_entry, *next; ++ Transaction *tran = tran_new(); ++ g_autoptr(GSList) refresh_list = NULL; ++ ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ assert(bs_queue != NULL); ++ GLOBAL_STATE_CODE(); ++ ++ QTAILQ_FOREACH(bs_entry, bs_queue, entry) { ++ ret = bdrv_flush(bs_entry->state.bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Error flushing drive"); ++ goto abort; ++ } ++ } ++ ++ QTAILQ_FOREACH(bs_entry, bs_queue, entry) { ++ assert(bs_entry->state.bs->quiesce_counter > 0); ++ ret = bdrv_reopen_prepare(&bs_entry->state, bs_queue, tran, errp); ++ if (ret < 0) { ++ goto abort; ++ } ++ bs_entry->prepared = true; ++ } ++ ++ QTAILQ_FOREACH(bs_entry, bs_queue, entry) { ++ BDRVReopenState *state = &bs_entry->state; ++ ++ refresh_list = g_slist_prepend(refresh_list, state->bs); ++ if (state->old_backing_bs) { ++ refresh_list = g_slist_prepend(refresh_list, state->old_backing_bs); ++ } ++ if (state->old_file_bs) { ++ refresh_list = g_slist_prepend(refresh_list, state->old_file_bs); ++ } ++ } ++ ++ /* ++ * Note that file-posix driver rely on permission update done during reopen ++ * (even if no permission changed), because it wants "new" permissions for ++ * reconfiguring the fd and that's why it does it in raw_check_perm(), not ++ * in raw_reopen_prepare() which is called with "old" permissions. ++ */ ++ bdrv_graph_rdlock_main_loop(); ++ ret = bdrv_list_refresh_perms(refresh_list, bs_queue, tran, errp); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (ret < 0) { ++ goto abort; ++ } ++ ++ /* ++ * If we reach this point, we have success and just need to apply the ++ * changes. ++ * ++ * Reverse order is used to comfort qcow2 driver: on commit it need to write ++ * IN_USE flag to the image, to mark bitmaps in the image as invalid. But ++ * children are usually goes after parents in reopen-queue, so go from last ++ * to first element. ++ */ ++ QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { ++ bdrv_reopen_commit(&bs_entry->state); ++ } ++ ++ bdrv_graph_wrlock(); ++ tran_commit(tran); ++ bdrv_graph_wrunlock(); ++ ++ QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { ++ BlockDriverState *bs = bs_entry->state.bs; ++ ++ if (bs->drv->bdrv_reopen_commit_post) { ++ bs->drv->bdrv_reopen_commit_post(&bs_entry->state); ++ } ++ } ++ ++ ret = 0; ++ goto cleanup; ++ ++abort: ++ bdrv_graph_wrlock(); ++ tran_abort(tran); ++ bdrv_graph_wrunlock(); ++ ++ QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { ++ if (bs_entry->prepared) { ++ bdrv_reopen_abort(&bs_entry->state); ++ } ++ } ++ ++cleanup: ++ bdrv_reopen_queue_free(bs_queue); ++ ++ return ret; ++} ++ ++int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts, ++ Error **errp) ++{ ++ BlockReopenQueue *queue; ++ ++ GLOBAL_STATE_CODE(); ++ ++ queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts); ++ ++ return bdrv_reopen_multiple(queue, errp); ++} ++ ++int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, ++ Error **errp) ++{ ++ QDict *opts = qdict_new(); ++ ++ GLOBAL_STATE_CODE(); ++ ++ qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only); ++ ++ return bdrv_reopen(bs, opts, true, errp); ++} ++ ++/* ++ * Take a BDRVReopenState and check if the value of 'backing' in the ++ * reopen_state->options QDict is valid or not. ++ * ++ * If 'backing' is missing from the QDict then return 0. ++ * ++ * If 'backing' contains the node name of the backing file of ++ * reopen_state->bs then return 0. ++ * ++ * If 'backing' contains a different node name (or is null) then check ++ * whether the current backing file can be replaced with the new one. ++ * If that's the case then reopen_state->replace_backing_bs is set to ++ * true and reopen_state->new_backing_bs contains a pointer to the new ++ * backing BlockDriverState (or NULL). ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ * ++ * Return 0 on success, otherwise return < 0 and set @errp. ++ * ++ * @reopen_state->bs can move to a different AioContext in this function. ++ */ ++static int GRAPH_UNLOCKED ++bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, ++ bool is_backing, Transaction *tran, ++ Error **errp) ++{ ++ BlockDriverState *bs = reopen_state->bs; ++ BlockDriverState *new_child_bs; ++ BlockDriverState *old_child_bs; ++ ++ const char *child_name = is_backing ? "backing" : "file"; ++ QObject *value; ++ const char *str; ++ bool has_child; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ value = qdict_get(reopen_state->options, child_name); ++ if (value == NULL) { ++ return 0; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ ++ switch (qobject_type(value)) { ++ case QTYPE_QNULL: ++ assert(is_backing); /* The 'file' option does not allow a null value */ ++ new_child_bs = NULL; ++ break; ++ case QTYPE_QSTRING: ++ str = qstring_get_str(qobject_to(QString, value)); ++ new_child_bs = bdrv_lookup_bs(NULL, str, errp); ++ if (new_child_bs == NULL) { ++ ret = -EINVAL; ++ goto out_rdlock; ++ } ++ ++ has_child = bdrv_recurse_has_child(new_child_bs, bs); ++ if (has_child) { ++ error_setg(errp, "Making '%s' a %s child of '%s' would create a " ++ "cycle", str, child_name, bs->node_name); ++ ret = -EINVAL; ++ goto out_rdlock; ++ } ++ break; ++ default: ++ /* ++ * The options QDict has been flattened, so 'backing' and 'file' ++ * do not allow any other data type here. ++ */ ++ g_assert_not_reached(); ++ } ++ ++ old_child_bs = is_backing ? child_bs(bs->backing) : child_bs(bs->file); ++ if (old_child_bs == new_child_bs) { ++ ret = 0; ++ goto out_rdlock; ++ } ++ ++ if (old_child_bs) { ++ if (bdrv_skip_implicit_filters(old_child_bs) == new_child_bs) { ++ ret = 0; ++ goto out_rdlock; ++ } ++ ++ if (old_child_bs->implicit) { ++ error_setg(errp, "Cannot replace implicit %s child of %s", ++ child_name, bs->node_name); ++ ret = -EPERM; ++ goto out_rdlock; ++ } ++ } ++ ++ if (bs->drv->is_filter && !old_child_bs) { ++ /* ++ * Filters always have a file or a backing child, so we are trying to ++ * change wrong child ++ */ ++ error_setg(errp, "'%s' is a %s filter node that does not support a " ++ "%s child", bs->node_name, bs->drv->format_name, child_name); ++ ret = -EINVAL; ++ goto out_rdlock; ++ } ++ ++ if (is_backing) { ++ reopen_state->old_backing_bs = old_child_bs; ++ } else { ++ reopen_state->old_file_bs = old_child_bs; ++ } ++ ++ if (old_child_bs) { ++ bdrv_ref(old_child_bs); ++ bdrv_drained_begin(old_child_bs); ++ } ++ ++ bdrv_graph_rdunlock_main_loop(); ++ bdrv_graph_wrlock(); ++ ++ ret = bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing, ++ tran, errp); ++ ++ bdrv_graph_wrunlock(); ++ ++ if (old_child_bs) { ++ bdrv_drained_end(old_child_bs); ++ bdrv_unref(old_child_bs); ++ } ++ ++ return ret; ++ ++out_rdlock: ++ bdrv_graph_rdunlock_main_loop(); ++ return ret; ++} ++ ++/* ++ * Prepares a BlockDriverState for reopen. All changes are staged in the ++ * 'opaque' field of the BDRVReopenState, which is used and allocated by ++ * the block driver layer .bdrv_reopen_prepare() ++ * ++ * bs is the BlockDriverState to reopen ++ * flags are the new open flags ++ * queue is the reopen queue ++ * ++ * Returns 0 on success, non-zero on error. On error errp will be set ++ * as well. ++ * ++ * On failure, bdrv_reopen_abort() will be called to clean up any data. ++ * It is the responsibility of the caller to then call the abort() or ++ * commit() for any other BDS that have been left in a prepare() state ++ * ++ * After calling this function, the transaction @change_child_tran may only be ++ * completed while holding a writer lock for the graph. ++ */ ++static int GRAPH_UNLOCKED ++bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, ++ Transaction *change_child_tran, Error **errp) ++{ ++ int ret = -1; ++ int old_flags; ++ Error *local_err = NULL; ++ BlockDriver *drv; ++ QemuOpts *opts; ++ QDict *orig_reopen_opts; ++ char *discard = NULL; ++ bool read_only; ++ bool drv_prepared = false; ++ ++ assert(reopen_state != NULL); ++ assert(reopen_state->bs->drv != NULL); ++ GLOBAL_STATE_CODE(); ++ drv = reopen_state->bs->drv; ++ ++ /* This function and each driver's bdrv_reopen_prepare() remove ++ * entries from reopen_state->options as they are processed, so ++ * we need to make a copy of the original QDict. */ ++ orig_reopen_opts = qdict_clone_shallow(reopen_state->options); ++ ++ /* Process generic block layer options */ ++ opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_absorb_qdict(opts, reopen_state->options, errp)) { ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ /* This was already called in bdrv_reopen_queue_child() so the flags ++ * are up-to-date. This time we simply want to remove the options from ++ * QemuOpts in order to indicate that they have been processed. */ ++ old_flags = reopen_state->flags; ++ update_flags_from_options(&reopen_state->flags, opts); ++ assert(old_flags == reopen_state->flags); ++ ++ discard = qemu_opt_get_del(opts, BDRV_OPT_DISCARD); ++ if (discard != NULL) { ++ if (bdrv_parse_discard_flags(discard, &reopen_state->flags) != 0) { ++ error_setg(errp, "Invalid discard option"); ++ ret = -EINVAL; ++ goto error; ++ } ++ } ++ ++ reopen_state->detect_zeroes = ++ bdrv_parse_detect_zeroes(opts, reopen_state->flags, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ /* All other options (including node-name and driver) must be unchanged. ++ * Put them back into the QDict, so that they are checked at the end ++ * of this function. */ ++ qemu_opts_to_qdict(opts, reopen_state->options); ++ ++ /* If we are to stay read-only, do not allow permission change ++ * to r/w. Attempting to set to r/w may fail if either BDRV_O_ALLOW_RDWR is ++ * not set, or if the BDS still has copy_on_read enabled */ ++ read_only = !(reopen_state->flags & BDRV_O_RDWR); ++ ++ bdrv_graph_rdlock_main_loop(); ++ ret = bdrv_can_set_read_only(reopen_state->bs, read_only, true, &local_err); ++ bdrv_graph_rdunlock_main_loop(); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ goto error; ++ } ++ ++ if (drv->bdrv_reopen_prepare) { ++ /* ++ * If a driver-specific option is missing, it means that we ++ * should reset it to its default value. ++ * But not all options allow that, so we need to check it first. ++ */ ++ ret = bdrv_reset_options_allowed(reopen_state->bs, ++ reopen_state->options, errp); ++ if (ret) { ++ goto error; ++ } ++ ++ ret = drv->bdrv_reopen_prepare(reopen_state, queue, &local_err); ++ if (ret) { ++ if (local_err != NULL) { ++ error_propagate(errp, local_err); ++ } else { ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_refresh_filename(reopen_state->bs); ++ bdrv_graph_rdunlock_main_loop(); ++ error_setg(errp, "failed while preparing to reopen image '%s'", ++ reopen_state->bs->filename); ++ } ++ goto error; ++ } ++ } else { ++ /* It is currently mandatory to have a bdrv_reopen_prepare() ++ * handler for each supported drv. */ ++ bdrv_graph_rdlock_main_loop(); ++ error_setg(errp, "Block format '%s' used by node '%s' " ++ "does not support reopening files", drv->format_name, ++ bdrv_get_device_or_node_name(reopen_state->bs)); ++ bdrv_graph_rdunlock_main_loop(); ++ ret = -1; ++ goto error; ++ } ++ ++ drv_prepared = true; ++ ++ /* ++ * We must provide the 'backing' option if the BDS has a backing ++ * file or if the image file has a backing file name as part of ++ * its metadata. Otherwise the 'backing' option can be omitted. ++ */ ++ bdrv_graph_rdlock_main_loop(); ++ if (drv->supports_backing && reopen_state->backing_missing && ++ (reopen_state->bs->backing || reopen_state->bs->backing_file[0])) { ++ error_setg(errp, "backing is missing for '%s'", ++ reopen_state->bs->node_name); ++ bdrv_graph_rdunlock_main_loop(); ++ ret = -EINVAL; ++ goto error; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ /* ++ * Allow changing the 'backing' option. The new value can be ++ * either a reference to an existing node (using its node name) ++ * or NULL to simply detach the current backing file. ++ */ ++ ret = bdrv_reopen_parse_file_or_backing(reopen_state, true, ++ change_child_tran, errp); ++ if (ret < 0) { ++ goto error; ++ } ++ qdict_del(reopen_state->options, "backing"); ++ ++ /* Allow changing the 'file' option. In this case NULL is not allowed */ ++ ret = bdrv_reopen_parse_file_or_backing(reopen_state, false, ++ change_child_tran, errp); ++ if (ret < 0) { ++ goto error; ++ } ++ qdict_del(reopen_state->options, "file"); ++ ++ /* Options that are not handled are only okay if they are unchanged ++ * compared to the old state. It is expected that some options are only ++ * used for the initial open, but not reopen (e.g. filename) */ ++ if (qdict_size(reopen_state->options)) { ++ const QDictEntry *entry = qdict_first(reopen_state->options); ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ do { ++ QObject *new = entry->value; ++ QObject *old = qdict_get(reopen_state->bs->options, entry->key); ++ ++ /* Allow child references (child_name=node_name) as long as they ++ * point to the current child (i.e. everything stays the same). */ ++ if (qobject_type(new) == QTYPE_QSTRING) { ++ BdrvChild *child; ++ QLIST_FOREACH(child, &reopen_state->bs->children, next) { ++ if (!strcmp(child->name, entry->key)) { ++ break; ++ } ++ } ++ ++ if (child) { ++ if (!strcmp(child->bs->node_name, ++ qstring_get_str(qobject_to(QString, new)))) { ++ continue; /* Found child with this name, skip option */ ++ } ++ } ++ } ++ ++ /* ++ * TODO: When using -drive to specify blockdev options, all values ++ * will be strings; however, when using -blockdev, blockdev-add or ++ * filenames using the json:{} pseudo-protocol, they will be ++ * correctly typed. ++ * In contrast, reopening options are (currently) always strings ++ * (because you can only specify them through qemu-io; all other ++ * callers do not specify any options). ++ * Therefore, when using anything other than -drive to create a BDS, ++ * this cannot detect non-string options as unchanged, because ++ * qobject_is_equal() always returns false for objects of different ++ * type. In the future, this should be remedied by correctly typing ++ * all options. For now, this is not too big of an issue because ++ * the user can simply omit options which cannot be changed anyway, ++ * so they will stay unchanged. ++ */ ++ if (!qobject_is_equal(new, old)) { ++ error_setg(errp, "Cannot change the option '%s'", entry->key); ++ ret = -EINVAL; ++ goto error; ++ } ++ } while ((entry = qdict_next(reopen_state->options, entry))); ++ } ++ ++ ret = 0; ++ ++ /* Restore the original reopen_state->options QDict */ ++ qobject_unref(reopen_state->options); ++ reopen_state->options = qobject_ref(orig_reopen_opts); ++ ++error: ++ if (ret < 0 && drv_prepared) { ++ /* drv->bdrv_reopen_prepare() has succeeded, so we need to ++ * call drv->bdrv_reopen_abort() before signaling an error ++ * (bdrv_reopen_multiple() will not call bdrv_reopen_abort() ++ * when the respective bdrv_reopen_prepare() has failed) */ ++ if (drv->bdrv_reopen_abort) { ++ drv->bdrv_reopen_abort(reopen_state); ++ } ++ } ++ qemu_opts_del(opts); ++ qobject_unref(orig_reopen_opts); ++ g_free(discard); ++ return ret; ++} ++ ++/* ++ * Takes the staged changes for the reopen from bdrv_reopen_prepare(), and ++ * makes them final by swapping the staging BlockDriverState contents into ++ * the active BlockDriverState contents. ++ */ ++static void GRAPH_UNLOCKED bdrv_reopen_commit(BDRVReopenState *reopen_state) ++{ ++ BlockDriver *drv; ++ BlockDriverState *bs; ++ BdrvChild *child; ++ ++ assert(reopen_state != NULL); ++ bs = reopen_state->bs; ++ drv = bs->drv; ++ assert(drv != NULL); ++ GLOBAL_STATE_CODE(); ++ ++ /* If there are any driver level actions to take */ ++ if (drv->bdrv_reopen_commit) { ++ drv->bdrv_reopen_commit(reopen_state); ++ } ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ /* set BDS specific flags now */ ++ qobject_unref(bs->explicit_options); ++ qobject_unref(bs->options); ++ qobject_ref(reopen_state->explicit_options); ++ qobject_ref(reopen_state->options); ++ ++ bs->explicit_options = reopen_state->explicit_options; ++ bs->options = reopen_state->options; ++ bs->open_flags = reopen_state->flags; ++ bs->detect_zeroes = reopen_state->detect_zeroes; ++ ++ /* Remove child references from bs->options and bs->explicit_options. ++ * Child options were already removed in bdrv_reopen_queue_child() */ ++ QLIST_FOREACH(child, &bs->children, next) { ++ qdict_del(bs->explicit_options, child->name); ++ qdict_del(bs->options, child->name); ++ } ++ /* backing is probably removed, so it's not handled by previous loop */ ++ qdict_del(bs->explicit_options, "backing"); ++ qdict_del(bs->options, "backing"); ++ ++ bdrv_refresh_limits(bs, NULL, NULL); ++ bdrv_refresh_total_sectors(bs, bs->total_sectors); ++} ++ ++/* ++ * Abort the reopen, and delete and free the staged changes in ++ * reopen_state ++ */ ++static void GRAPH_UNLOCKED bdrv_reopen_abort(BDRVReopenState *reopen_state) ++{ ++ BlockDriver *drv; ++ ++ assert(reopen_state != NULL); ++ drv = reopen_state->bs->drv; ++ assert(drv != NULL); ++ GLOBAL_STATE_CODE(); ++ ++ if (drv->bdrv_reopen_abort) { ++ drv->bdrv_reopen_abort(reopen_state); ++ } ++} ++ ++ ++static void bdrv_close(BlockDriverState *bs) ++{ ++ BdrvAioNotifier *ban, *ban_next; ++ BdrvChild *child, *next; ++ ++ GLOBAL_STATE_CODE(); ++ assert(!bs->refcnt); ++ ++ bdrv_drained_begin(bs); /* complete I/O */ ++ bdrv_flush(bs); ++ bdrv_drain(bs); /* in case flush left pending I/O */ ++ ++ if (bs->drv) { ++ if (bs->drv->bdrv_close) { ++ /* Must unfreeze all children, so bdrv_unref_child() works */ ++ bs->drv->bdrv_close(bs); ++ } ++ bs->drv = NULL; ++ } ++ ++ bdrv_graph_wrlock(); ++ QLIST_FOREACH_SAFE(child, &bs->children, next, next) { ++ bdrv_unref_child(bs, child); ++ } ++ ++ assert(!bs->backing); ++ assert(!bs->file); ++ bdrv_graph_wrunlock(); ++ ++ g_free(bs->opaque); ++ bs->opaque = NULL; ++ qatomic_set(&bs->copy_on_read, 0); ++ bs->backing_file[0] = '\0'; ++ bs->backing_format[0] = '\0'; ++ bs->total_sectors = 0; ++ bs->encrypted = false; ++ bs->sg = false; ++ qobject_unref(bs->options); ++ qobject_unref(bs->explicit_options); ++ bs->options = NULL; ++ bs->explicit_options = NULL; ++ qobject_unref(bs->full_open_options); ++ bs->full_open_options = NULL; ++ g_free(bs->block_status_cache); ++ bs->block_status_cache = NULL; ++ ++ bdrv_release_named_dirty_bitmaps(bs); ++ assert(QLIST_EMPTY(&bs->dirty_bitmaps)); ++ ++ QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_next) { ++ g_free(ban); ++ } ++ QLIST_INIT(&bs->aio_notifiers); ++ bdrv_drained_end(bs); ++ ++ /* ++ * If we're still inside some bdrv_drain_all_begin()/end() sections, end ++ * them now since this BDS won't exist anymore when bdrv_drain_all_end() ++ * gets called. ++ */ ++ if (bs->quiesce_counter) { ++ bdrv_drain_all_end_quiesce(bs); ++ } ++} ++ ++void bdrv_close_all(void) ++{ ++ GLOBAL_STATE_CODE(); ++ assert(job_next(NULL) == NULL); ++ ++ /* Drop references from requests still in flight, such as canceled block ++ * jobs whose AIO context has not been polled yet */ ++ bdrv_drain_all(); ++ ++ blk_remove_all_bs(); ++ blockdev_close_all_bdrv_states(); ++ ++ assert(QTAILQ_EMPTY(&all_bdrv_states)); ++} ++ ++static bool GRAPH_RDLOCK should_update_child(BdrvChild *c, BlockDriverState *to) ++{ ++ GQueue *queue; ++ GHashTable *found; ++ bool ret; ++ ++ if (c->klass->stay_at_node) { ++ return false; ++ } ++ ++ /* If the child @c belongs to the BDS @to, replacing the current ++ * c->bs by @to would mean to create a loop. ++ * ++ * Such a case occurs when appending a BDS to a backing chain. ++ * For instance, imagine the following chain: ++ * ++ * guest device -> node A -> further backing chain... ++ * ++ * Now we create a new BDS B which we want to put on top of this ++ * chain, so we first attach A as its backing node: ++ * ++ * node B ++ * | ++ * v ++ * guest device -> node A -> further backing chain... ++ * ++ * Finally we want to replace A by B. When doing that, we want to ++ * replace all pointers to A by pointers to B -- except for the ++ * pointer from B because (1) that would create a loop, and (2) ++ * that pointer should simply stay intact: ++ * ++ * guest device -> node B ++ * | ++ * v ++ * node A -> further backing chain... ++ * ++ * In general, when replacing a node A (c->bs) by a node B (@to), ++ * if A is a child of B, that means we cannot replace A by B there ++ * because that would create a loop. Silently detaching A from B ++ * is also not really an option. So overall just leaving A in ++ * place there is the most sensible choice. ++ * ++ * We would also create a loop in any cases where @c is only ++ * indirectly referenced by @to. Prevent this by returning false ++ * if @c is found (by breadth-first search) anywhere in the whole ++ * subtree of @to. ++ */ ++ ++ ret = true; ++ found = g_hash_table_new(NULL, NULL); ++ g_hash_table_add(found, to); ++ queue = g_queue_new(); ++ g_queue_push_tail(queue, to); ++ ++ while (!g_queue_is_empty(queue)) { ++ BlockDriverState *v = g_queue_pop_head(queue); ++ BdrvChild *c2; ++ ++ QLIST_FOREACH(c2, &v->children, next) { ++ if (c2 == c) { ++ ret = false; ++ break; ++ } ++ ++ if (g_hash_table_contains(found, c2->bs)) { ++ continue; ++ } ++ ++ g_queue_push_tail(queue, c2->bs); ++ g_hash_table_add(found, c2->bs); ++ } ++ } ++ ++ g_queue_free(queue); ++ g_hash_table_destroy(found); ++ ++ return ret; ++} ++ ++static void bdrv_remove_child_commit(void *opaque) ++{ ++ GLOBAL_STATE_CODE(); ++ bdrv_child_free(opaque); ++} ++ ++static TransactionActionDrv bdrv_remove_child_drv = { ++ .commit = bdrv_remove_child_commit, ++}; ++ ++/* ++ * Function doesn't update permissions, caller is responsible for this. ++ * ++ * @child->bs (if non-NULL) must be drained. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ */ ++static void GRAPH_WRLOCK bdrv_remove_child(BdrvChild *child, Transaction *tran) ++{ ++ if (!child) { ++ return; ++ } ++ ++ if (child->bs) { ++ assert(child->quiesced_parent); ++ bdrv_replace_child_tran(child, NULL, tran); ++ } ++ ++ tran_add(tran, &bdrv_remove_child_drv, child); ++} ++ ++/* ++ * Both @from and @to (if non-NULL) must be drained. @to must be kept drained ++ * until the transaction is completed. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ */ ++static int GRAPH_WRLOCK ++bdrv_replace_node_noperm(BlockDriverState *from, ++ BlockDriverState *to, ++ bool auto_skip, Transaction *tran, ++ Error **errp) ++{ ++ BdrvChild *c, *next; ++ ++ GLOBAL_STATE_CODE(); ++ ++ assert(from->quiesce_counter); ++ assert(to->quiesce_counter); ++ ++ QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { ++ assert(c->bs == from); ++ if (!should_update_child(c, to)) { ++ if (auto_skip) { ++ continue; ++ } ++ error_setg(errp, "Should not change '%s' link to '%s'", ++ c->name, from->node_name); ++ return -EINVAL; ++ } ++ if (c->frozen) { ++ error_setg(errp, "Cannot change '%s' link to '%s'", ++ c->name, from->node_name); ++ return -EPERM; ++ } ++ bdrv_replace_child_tran(c, to, tran); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Switch all parents of @from to point to @to instead. @from and @to must be in ++ * the same AioContext and both must be drained. ++ * ++ * With auto_skip=true bdrv_replace_node_common skips updating from parents ++ * if it creates a parent-child relation loop or if parent is block-job. ++ * ++ * With auto_skip=false the error is returned if from has a parent which should ++ * not be updated. ++ * ++ * With @detach_subchain=true @to must be in a backing chain of @from. In this ++ * case backing link of the cow-parent of @to is removed. ++ */ ++static int GRAPH_WRLOCK ++bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to, ++ bool auto_skip, bool detach_subchain, Error **errp) ++{ ++ Transaction *tran = tran_new(); ++ g_autoptr(GSList) refresh_list = NULL; ++ BlockDriverState *to_cow_parent = NULL; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ assert(from->quiesce_counter); ++ assert(to->quiesce_counter); ++ assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to)); ++ ++ if (detach_subchain) { ++ assert(bdrv_chain_contains(from, to)); ++ assert(from != to); ++ for (to_cow_parent = from; ++ bdrv_filter_or_cow_bs(to_cow_parent) != to; ++ to_cow_parent = bdrv_filter_or_cow_bs(to_cow_parent)) ++ { ++ ; ++ } ++ } ++ ++ /* ++ * Do the replacement without permission update. ++ * Replacement may influence the permissions, we should calculate new ++ * permissions based on new graph. If we fail, we'll roll-back the ++ * replacement. ++ */ ++ ret = bdrv_replace_node_noperm(from, to, auto_skip, tran, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ if (detach_subchain) { ++ /* to_cow_parent is already drained because from is drained */ ++ bdrv_remove_child(bdrv_filter_or_cow_child(to_cow_parent), tran); ++ } ++ ++ refresh_list = g_slist_prepend(refresh_list, to); ++ refresh_list = g_slist_prepend(refresh_list, from); ++ ++ ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = 0; ++ ++out: ++ tran_finalize(tran, ret); ++ return ret; ++} ++ ++int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, ++ Error **errp) ++{ ++ return bdrv_replace_node_common(from, to, true, false, errp); ++} ++ ++int bdrv_drop_filter(BlockDriverState *bs, Error **errp) ++{ ++ BlockDriverState *child_bs; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_graph_rdlock_main_loop(); ++ child_bs = bdrv_filter_or_cow_bs(bs); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_drained_begin(child_bs); ++ bdrv_graph_wrlock(); ++ ret = bdrv_replace_node_common(bs, child_bs, true, true, errp); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(child_bs); ++ ++ return ret; ++} ++ ++/* ++ * Add new bs contents at the top of an image chain while the chain is ++ * live, while keeping required fields on the top layer. ++ * ++ * This will modify the BlockDriverState fields, and swap contents ++ * between bs_new and bs_top. Both bs_new and bs_top are modified. ++ * ++ * bs_new must not be attached to a BlockBackend and must not have backing ++ * child. ++ * ++ * This function does not create any image files. ++ */ ++int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, ++ Error **errp) ++{ ++ int ret; ++ BdrvChild *child; ++ Transaction *tran = tran_new(); ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_graph_rdlock_main_loop(); ++ assert(!bs_new->backing); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_drained_begin(bs_top); ++ bdrv_drained_begin(bs_new); ++ ++ bdrv_graph_wrlock(); ++ ++ child = bdrv_attach_child_noperm(bs_new, bs_top, "backing", ++ &child_of_bds, bdrv_backing_role(bs_new), ++ tran, errp); ++ if (!child) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ret = bdrv_replace_node_noperm(bs_top, bs_new, true, tran, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = bdrv_refresh_perms(bs_new, tran, errp); ++out: ++ tran_finalize(tran, ret); ++ ++ bdrv_refresh_limits(bs_top, NULL, NULL); ++ bdrv_graph_wrunlock(); ++ ++ bdrv_drained_end(bs_top); ++ bdrv_drained_end(bs_new); ++ ++ return ret; ++} ++ ++/* Not for empty child */ ++int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, ++ Error **errp) ++{ ++ int ret; ++ Transaction *tran = tran_new(); ++ g_autoptr(GSList) refresh_list = NULL; ++ BlockDriverState *old_bs = child->bs; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_ref(old_bs); ++ bdrv_drained_begin(old_bs); ++ bdrv_drained_begin(new_bs); ++ bdrv_graph_wrlock(); ++ ++ bdrv_replace_child_tran(child, new_bs, tran); ++ ++ refresh_list = g_slist_prepend(refresh_list, old_bs); ++ refresh_list = g_slist_prepend(refresh_list, new_bs); ++ ++ ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp); ++ ++ tran_finalize(tran, ret); ++ ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(old_bs); ++ bdrv_drained_end(new_bs); ++ bdrv_unref(old_bs); ++ ++ return ret; ++} ++ ++static void bdrv_delete(BlockDriverState *bs) ++{ ++ assert(bdrv_op_blocker_is_empty(bs)); ++ assert(!bs->refcnt); ++ GLOBAL_STATE_CODE(); ++ ++ /* remove from list, if necessary */ ++ if (bs->node_name[0] != '\0') { ++ QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list); ++ } ++ QTAILQ_REMOVE(&all_bdrv_states, bs, bs_list); ++ ++ bdrv_close(bs); ++ ++ qemu_mutex_destroy(&bs->reqs_lock); ++ ++ g_free(bs); ++} ++ ++ ++/* ++ * Replace @bs by newly created block node. ++ * ++ * @options is a QDict of options to pass to the block drivers, or NULL for an ++ * empty set of options. The reference to the QDict belongs to the block layer ++ * after the call (even on failure), so if the caller intends to reuse the ++ * dictionary, it needs to use qobject_ref() before calling bdrv_open. ++ * ++ * The caller must make sure that @bs stays in the same AioContext, i.e. ++ * @options must not refer to nodes in a different AioContext. ++ */ ++BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, ++ int flags, Error **errp) ++{ ++ ERRP_GUARD(); ++ int ret; ++ AioContext *ctx = bdrv_get_aio_context(bs); ++ BlockDriverState *new_node_bs = NULL; ++ const char *drvname, *node_name; ++ BlockDriver *drv; ++ ++ drvname = qdict_get_try_str(options, "driver"); ++ if (!drvname) { ++ error_setg(errp, "driver is not specified"); ++ goto fail; ++ } ++ ++ drv = bdrv_find_format(drvname); ++ if (!drv) { ++ error_setg(errp, "Unknown driver: '%s'", drvname); ++ goto fail; ++ } ++ ++ node_name = qdict_get_try_str(options, "node-name"); ++ ++ GLOBAL_STATE_CODE(); ++ ++ new_node_bs = bdrv_new_open_driver_opts(drv, node_name, options, flags, ++ errp); ++ assert(bdrv_get_aio_context(bs) == ctx); ++ ++ options = NULL; /* bdrv_new_open_driver() eats options */ ++ if (!new_node_bs) { ++ error_prepend(errp, "Could not create node: "); ++ goto fail; ++ } ++ ++ /* ++ * Make sure that @bs doesn't go away until we have successfully attached ++ * all of its parents to @new_node_bs and undrained it again. ++ */ ++ bdrv_ref(bs); ++ bdrv_drained_begin(bs); ++ bdrv_drained_begin(new_node_bs); ++ bdrv_graph_wrlock(); ++ ret = bdrv_replace_node(bs, new_node_bs, errp); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(new_node_bs); ++ bdrv_drained_end(bs); ++ bdrv_unref(bs); ++ ++ if (ret < 0) { ++ error_prepend(errp, "Could not replace node: "); ++ goto fail; ++ } ++ ++ return new_node_bs; ++ ++fail: ++ qobject_unref(options); ++ bdrv_unref(new_node_bs); ++ return NULL; ++} ++ ++/* ++ * Run consistency checks on an image ++ * ++ * Returns 0 if the check could be completed (it doesn't mean that the image is ++ * free of errors) or -errno when an internal error occurred. The results of the ++ * check are stored in res. ++ */ ++int coroutine_fn bdrv_co_check(BlockDriverState *bs, ++ BdrvCheckResult *res, BdrvCheckMode fix) ++{ ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ if (bs->drv == NULL) { ++ return -ENOMEDIUM; ++ } ++ if (bs->drv->bdrv_co_check == NULL) { ++ return -ENOTSUP; ++ } ++ ++ memset(res, 0, sizeof(*res)); ++ return bs->drv->bdrv_co_check(bs, res, fix); ++} ++ ++/* ++ * Return values: ++ * 0 - success ++ * -EINVAL - backing format specified, but no file ++ * -ENOSPC - can't update the backing file because no space is left in the ++ * image file header ++ * -ENOTSUP - format driver doesn't support changing the backing file ++ */ ++int coroutine_fn ++bdrv_co_change_backing_file(BlockDriverState *bs, const char *backing_file, ++ const char *backing_fmt, bool require) ++{ ++ BlockDriver *drv = bs->drv; ++ int ret; ++ ++ IO_CODE(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ /* Backing file format doesn't make sense without a backing file */ ++ if (backing_fmt && !backing_file) { ++ return -EINVAL; ++ } ++ ++ if (require && backing_file && !backing_fmt) { ++ return -EINVAL; ++ } ++ ++ if (drv->bdrv_co_change_backing_file != NULL) { ++ ret = drv->bdrv_co_change_backing_file(bs, backing_file, backing_fmt); ++ } else { ++ ret = -ENOTSUP; ++ } ++ ++ if (ret == 0) { ++ pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: ""); ++ pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: ""); ++ pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), ++ backing_file ?: ""); ++ } ++ return ret; ++} ++ ++/* ++ * Finds the first non-filter node above bs in the chain between ++ * active and bs. The returned node is either an immediate parent of ++ * bs, or there are only filter nodes between the two. ++ * ++ * Returns NULL if bs is not found in active's image chain, ++ * or if active == bs. ++ * ++ * Returns the bottommost base image if bs == NULL. ++ */ ++BlockDriverState *bdrv_find_overlay(BlockDriverState *active, ++ BlockDriverState *bs) ++{ ++ ++ GLOBAL_STATE_CODE(); ++ ++ bs = bdrv_skip_filters(bs); ++ active = bdrv_skip_filters(active); ++ ++ while (active) { ++ BlockDriverState *next = bdrv_backing_chain_next(active); ++ if (bs == next) { ++ return active; ++ } ++ active = next; ++ } ++ ++ return NULL; ++} ++ ++/* Given a BDS, searches for the base layer. */ ++BlockDriverState *bdrv_find_base(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ return bdrv_find_overlay(bs, NULL); ++} ++ ++/* ++ * Return true if at least one of the COW (backing) and filter links ++ * between @bs and @base is frozen. @errp is set if that's the case. ++ * @base must be reachable from @bs, or NULL. ++ */ ++static bool GRAPH_RDLOCK ++bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, ++ Error **errp) ++{ ++ BlockDriverState *i; ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ ++ for (i = bs; i != base; i = child_bs(child)) { ++ child = bdrv_filter_or_cow_child(i); ++ ++ if (child && child->frozen) { ++ error_setg(errp, "Cannot change '%s' link from '%s' to '%s'", ++ child->name, i->node_name, child->bs->node_name); ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++/* ++ * Freeze all COW (backing) and filter links between @bs and @base. ++ * If any of the links is already frozen the operation is aborted and ++ * none of the links are modified. ++ * @base must be reachable from @bs, or NULL. ++ * Returns 0 on success. On failure returns < 0 and sets @errp. ++ */ ++int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, ++ Error **errp) ++{ ++ BlockDriverState *i; ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (bdrv_is_backing_chain_frozen(bs, base, errp)) { ++ return -EPERM; ++ } ++ ++ for (i = bs; i != base; i = child_bs(child)) { ++ child = bdrv_filter_or_cow_child(i); ++ if (child && child->bs->never_freeze) { ++ error_setg(errp, "Cannot freeze '%s' link to '%s'", ++ child->name, child->bs->node_name); ++ return -EPERM; ++ } ++ } ++ ++ for (i = bs; i != base; i = child_bs(child)) { ++ child = bdrv_filter_or_cow_child(i); ++ if (child) { ++ child->frozen = true; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Unfreeze all COW (backing) and filter links between @bs and @base. ++ * The caller must ensure that all links are frozen before using this ++ * function. ++ * @base must be reachable from @bs, or NULL. ++ */ ++void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base) ++{ ++ BlockDriverState *i; ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ ++ for (i = bs; i != base; i = child_bs(child)) { ++ child = bdrv_filter_or_cow_child(i); ++ if (child) { ++ assert(child->frozen); ++ child->frozen = false; ++ } ++ } ++} ++ ++/* ++ * Drops images above 'base' up to and including 'top', and sets the image ++ * above 'top' to have base as its backing file. ++ * ++ * Requires that the overlay to 'top' is opened r/w, so that the backing file ++ * information in 'bs' can be properly updated. ++ * ++ * E.g., this will convert the following chain: ++ * bottom <- base <- intermediate <- top <- active ++ * ++ * to ++ * ++ * bottom <- base <- active ++ * ++ * It is allowed for bottom==base, in which case it converts: ++ * ++ * base <- intermediate <- top <- active ++ * ++ * to ++ * ++ * base <- active ++ * ++ * If backing_file_str is non-NULL, it will be used when modifying top's ++ * overlay image metadata. ++ * ++ * Error conditions: ++ * if active == top, that is considered an error ++ * ++ */ ++int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, ++ const char *backing_file_str, ++ bool backing_mask_protocol) ++{ ++ BlockDriverState *explicit_top = top; ++ bool update_inherits_from; ++ BdrvChild *c; ++ Error *local_err = NULL; ++ int ret = -EIO; ++ g_autoptr(GSList) updated_children = NULL; ++ GSList *p; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_ref(top); ++ bdrv_drained_begin(base); ++ bdrv_graph_wrlock(); ++ ++ if (!top->drv || !base->drv) { ++ goto exit_wrlock; ++ } ++ ++ /* Make sure that base is in the backing chain of top */ ++ if (!bdrv_chain_contains(top, base)) { ++ goto exit_wrlock; ++ } ++ ++ /* If 'base' recursively inherits from 'top' then we should set ++ * base->inherits_from to top->inherits_from after 'top' and all ++ * other intermediate nodes have been dropped. ++ * If 'top' is an implicit node (e.g. "commit_top") we should skip ++ * it because no one inherits from it. We use explicit_top for that. */ ++ explicit_top = bdrv_skip_implicit_filters(explicit_top); ++ update_inherits_from = bdrv_inherits_from_recursive(base, explicit_top); ++ ++ /* success - we can delete the intermediate states, and link top->base */ ++ if (!backing_file_str) { ++ bdrv_refresh_filename(base); ++ backing_file_str = base->filename; ++ } ++ ++ QLIST_FOREACH(c, &top->parents, next_parent) { ++ updated_children = g_slist_prepend(updated_children, c); ++ } ++ ++ /* ++ * It seems correct to pass detach_subchain=true here, but it triggers ++ * one more yet not fixed bug, when due to nested aio_poll loop we switch to ++ * another drained section, which modify the graph (for example, removing ++ * the child, which we keep in updated_children list). So, it's a TODO. ++ * ++ * Note, bug triggered if pass detach_subchain=true here and run ++ * test-bdrv-drain. test_drop_intermediate_poll() test-case will crash. ++ * That's a FIXME. ++ */ ++ bdrv_replace_node_common(top, base, false, false, &local_err); ++ bdrv_graph_wrunlock(); ++ ++ if (local_err) { ++ error_report_err(local_err); ++ goto exit; ++ } ++ ++ for (p = updated_children; p; p = p->next) { ++ c = p->data; ++ ++ if (c->klass->update_filename) { ++ ret = c->klass->update_filename(c, base, backing_file_str, ++ backing_mask_protocol, ++ &local_err); ++ if (ret < 0) { ++ /* ++ * TODO: Actually, we want to rollback all previous iterations ++ * of this loop, and (which is almost impossible) previous ++ * bdrv_replace_node()... ++ * ++ * Note, that c->klass->update_filename may lead to permission ++ * update, so it's a bad idea to call it inside permission ++ * update transaction of bdrv_replace_node. ++ */ ++ error_report_err(local_err); ++ goto exit; ++ } ++ } ++ } ++ ++ if (update_inherits_from) { ++ base->inherits_from = explicit_top->inherits_from; ++ } ++ ++ ret = 0; ++ goto exit; ++ ++exit_wrlock: ++ bdrv_graph_wrunlock(); ++exit: ++ bdrv_drained_end(base); ++ bdrv_unref(top); ++ return ret; ++} ++ ++/** ++ * Implementation of BlockDriver.bdrv_co_get_allocated_file_size() that ++ * sums the size of all data-bearing children. (This excludes backing ++ * children.) ++ */ ++static int64_t coroutine_fn GRAPH_RDLOCK ++bdrv_sum_allocated_file_size(BlockDriverState *bs) ++{ ++ BdrvChild *child; ++ int64_t child_size, sum = 0; ++ ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (child->role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA | ++ BDRV_CHILD_FILTERED)) ++ { ++ child_size = bdrv_co_get_allocated_file_size(child->bs); ++ if (child_size < 0) { ++ return child_size; ++ } ++ sum += child_size; ++ } ++ } ++ ++ return sum; ++} ++ ++/** ++ * Length of a allocated file in bytes. Sparse files are counted by actual ++ * allocated space. Return < 0 if error or unknown. ++ */ ++int64_t coroutine_fn bdrv_co_get_allocated_file_size(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ if (drv->bdrv_co_get_allocated_file_size) { ++ return drv->bdrv_co_get_allocated_file_size(bs); ++ } ++ ++ if (drv->protocol_name) { ++ /* ++ * Protocol drivers default to -ENOTSUP (most of their data is ++ * not stored in any of their children (if they even have any), ++ * so there is no generic way to figure it out). ++ */ ++ return -ENOTSUP; ++ } else if (drv->is_filter) { ++ /* Filter drivers default to the size of their filtered child */ ++ return bdrv_co_get_allocated_file_size(bdrv_filter_bs(bs)); ++ } else { ++ /* Other drivers default to summing their children's sizes */ ++ return bdrv_sum_allocated_file_size(bs); ++ } ++} ++ ++/* ++ * bdrv_measure: ++ * @drv: Format driver ++ * @opts: Creation options for new image ++ * @in_bs: Existing image containing data for new image (may be NULL) ++ * @errp: Error object ++ * Returns: A #BlockMeasureInfo (free using qapi_free_BlockMeasureInfo()) ++ * or NULL on error ++ * ++ * Calculate file size required to create a new image. ++ * ++ * If @in_bs is given then space for allocated clusters and zero clusters ++ * from that image are included in the calculation. If @opts contains a ++ * backing file that is shared by @in_bs then backing clusters may be omitted ++ * from the calculation. ++ * ++ * If @in_bs is NULL then the calculation includes no allocated clusters ++ * unless a preallocation option is given in @opts. ++ * ++ * Note that @in_bs may use a different BlockDriver from @drv. ++ * ++ * If an error occurs the @errp pointer is set. ++ */ ++BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts, ++ BlockDriverState *in_bs, Error **errp) ++{ ++ IO_CODE(); ++ if (!drv->bdrv_measure) { ++ error_setg(errp, "Block driver '%s' does not support size measurement", ++ drv->format_name); ++ return NULL; ++ } ++ ++ return drv->bdrv_measure(opts, in_bs, errp); ++} ++ ++/** ++ * Return number of sectors on success, -errno on error. ++ */ ++int64_t coroutine_fn bdrv_co_nb_sectors(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) ++ return -ENOMEDIUM; ++ ++ if (bs->bl.has_variable_length) { ++ int ret = bdrv_co_refresh_total_sectors(bs, bs->total_sectors); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ return bs->total_sectors; ++} ++ ++/* ++ * This wrapper is written by hand because this function is in the hot I/O path, ++ * via blk_get_geometry. ++ */ ++int64_t coroutine_mixed_fn bdrv_nb_sectors(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ ++ if (!drv) ++ return -ENOMEDIUM; ++ ++ if (bs->bl.has_variable_length) { ++ int ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return bs->total_sectors; ++} ++ ++/** ++ * Return length in bytes on success, -errno on error. ++ * The length is always a multiple of BDRV_SECTOR_SIZE. ++ */ ++int64_t coroutine_fn bdrv_co_getlength(BlockDriverState *bs) ++{ ++ int64_t ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ ret = bdrv_co_nb_sectors(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ if (ret > INT64_MAX / BDRV_SECTOR_SIZE) { ++ return -EFBIG; ++ } ++ return ret * BDRV_SECTOR_SIZE; ++} ++ ++bool bdrv_is_sg(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bs->sg; ++} ++ ++/** ++ * Return whether the given node supports compressed writes. ++ */ ++bool bdrv_supports_compressed_writes(BlockDriverState *bs) ++{ ++ BlockDriverState *filtered; ++ IO_CODE(); ++ ++ if (!bs->drv || !block_driver_can_compress(bs->drv)) { ++ return false; ++ } ++ ++ filtered = bdrv_filter_bs(bs); ++ if (filtered) { ++ /* ++ * Filters can only forward compressed writes, so we have to ++ * check the child. ++ */ ++ return bdrv_supports_compressed_writes(filtered); ++ } ++ ++ return true; ++} ++ ++const char *bdrv_get_format_name(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bs->drv ? bs->drv->format_name : NULL; ++} ++ ++static int qsort_strcmp(const void *a, const void *b) ++{ ++ return strcmp(*(char *const *)a, *(char *const *)b); ++} ++ ++void bdrv_iterate_format(void (*it)(void *opaque, const char *name), ++ void *opaque, bool read_only) ++{ ++ BlockDriver *drv; ++ int count = 0; ++ int i; ++ const char **formats = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH(drv, &bdrv_drivers, list) { ++ if (drv->format_name) { ++ bool found = false; ++ ++ if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, read_only)) { ++ continue; ++ } ++ ++ i = count; ++ while (formats && i && !found) { ++ found = !strcmp(formats[--i], drv->format_name); ++ } ++ ++ if (!found) { ++ formats = g_renew(const char *, formats, count + 1); ++ formats[count++] = drv->format_name; ++ } ++ } ++ } ++ ++ for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); i++) { ++ const char *format_name = block_driver_modules[i].format_name; ++ ++ if (format_name) { ++ bool found = false; ++ int j = count; ++ ++ if (use_bdrv_whitelist && ++ !bdrv_format_is_whitelisted(format_name, read_only)) { ++ continue; ++ } ++ ++ while (formats && j && !found) { ++ found = !strcmp(formats[--j], format_name); ++ } ++ ++ if (!found) { ++ formats = g_renew(const char *, formats, count + 1); ++ formats[count++] = format_name; ++ } ++ } ++ } ++ ++ qsort(formats, count, sizeof(formats[0]), qsort_strcmp); ++ ++ for (i = 0; i < count; i++) { ++ it(opaque, formats[i]); ++ } ++ ++ g_free(formats); ++} ++ ++/* This function is to find a node in the bs graph */ ++BlockDriverState *bdrv_find_node(const char *node_name) ++{ ++ BlockDriverState *bs; ++ ++ assert(node_name); ++ GLOBAL_STATE_CODE(); ++ ++ QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { ++ if (!strcmp(node_name, bs->node_name)) { ++ return bs; ++ } ++ } ++ return NULL; ++} ++ ++/* Put this QMP function here so it can access the static graph_bdrv_states. */ ++BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, ++ Error **errp) ++{ ++ BlockDeviceInfoList *list; ++ BlockDriverState *bs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ list = NULL; ++ QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { ++ BlockDeviceInfo *info = bdrv_block_device_info(NULL, bs, flat, errp); ++ if (!info) { ++ qapi_free_BlockDeviceInfoList(list); ++ return NULL; ++ } ++ QAPI_LIST_PREPEND(list, info); ++ } ++ ++ return list; ++} ++ ++typedef struct XDbgBlockGraphConstructor { ++ XDbgBlockGraph *graph; ++ GHashTable *graph_nodes; ++} XDbgBlockGraphConstructor; ++ ++static XDbgBlockGraphConstructor *xdbg_graph_new(void) ++{ ++ XDbgBlockGraphConstructor *gr = g_new(XDbgBlockGraphConstructor, 1); ++ ++ gr->graph = g_new0(XDbgBlockGraph, 1); ++ gr->graph_nodes = g_hash_table_new(NULL, NULL); ++ ++ return gr; ++} ++ ++static XDbgBlockGraph *xdbg_graph_finalize(XDbgBlockGraphConstructor *gr) ++{ ++ XDbgBlockGraph *graph = gr->graph; ++ ++ g_hash_table_destroy(gr->graph_nodes); ++ g_free(gr); ++ ++ return graph; ++} ++ ++static uintptr_t xdbg_graph_node_num(XDbgBlockGraphConstructor *gr, void *node) ++{ ++ uintptr_t ret = (uintptr_t)g_hash_table_lookup(gr->graph_nodes, node); ++ ++ if (ret != 0) { ++ return ret; ++ } ++ ++ /* ++ * Start counting from 1, not 0, because 0 interferes with not-found (NULL) ++ * answer of g_hash_table_lookup. ++ */ ++ ret = g_hash_table_size(gr->graph_nodes) + 1; ++ g_hash_table_insert(gr->graph_nodes, node, (void *)ret); ++ ++ return ret; ++} ++ ++static void xdbg_graph_add_node(XDbgBlockGraphConstructor *gr, void *node, ++ XDbgBlockGraphNodeType type, const char *name) ++{ ++ XDbgBlockGraphNode *n; ++ ++ n = g_new0(XDbgBlockGraphNode, 1); ++ ++ n->id = xdbg_graph_node_num(gr, node); ++ n->type = type; ++ n->name = g_strdup(name); ++ ++ QAPI_LIST_PREPEND(gr->graph->nodes, n); ++} ++ ++static void xdbg_graph_add_edge(XDbgBlockGraphConstructor *gr, void *parent, ++ const BdrvChild *child) ++{ ++ BlockPermission qapi_perm; ++ XDbgBlockGraphEdge *edge; ++ GLOBAL_STATE_CODE(); ++ ++ edge = g_new0(XDbgBlockGraphEdge, 1); ++ ++ edge->parent = xdbg_graph_node_num(gr, parent); ++ edge->child = xdbg_graph_node_num(gr, child->bs); ++ edge->name = g_strdup(child->name); ++ ++ for (qapi_perm = 0; qapi_perm < BLOCK_PERMISSION__MAX; qapi_perm++) { ++ uint64_t flag = bdrv_qapi_perm_to_blk_perm(qapi_perm); ++ ++ if (flag & child->perm) { ++ QAPI_LIST_PREPEND(edge->perm, qapi_perm); ++ } ++ if (flag & child->shared_perm) { ++ QAPI_LIST_PREPEND(edge->shared_perm, qapi_perm); ++ } ++ } ++ ++ QAPI_LIST_PREPEND(gr->graph->edges, edge); ++} ++ ++ ++XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp) ++{ ++ BlockBackend *blk; ++ BlockJob *job; ++ BlockDriverState *bs; ++ BdrvChild *child; ++ XDbgBlockGraphConstructor *gr = xdbg_graph_new(); ++ ++ GLOBAL_STATE_CODE(); ++ ++ for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { ++ char *allocated_name = NULL; ++ const char *name = blk_name(blk); ++ ++ if (!*name) { ++ name = allocated_name = blk_get_attached_dev_id(blk); ++ } ++ xdbg_graph_add_node(gr, blk, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_BACKEND, ++ name); ++ g_free(allocated_name); ++ if (blk_root(blk)) { ++ xdbg_graph_add_edge(gr, blk, blk_root(blk)); ++ } ++ } ++ ++ WITH_JOB_LOCK_GUARD() { ++ for (job = block_job_next_locked(NULL); job; ++ job = block_job_next_locked(job)) { ++ GSList *el; ++ ++ xdbg_graph_add_node(gr, job, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB, ++ job->job.id); ++ for (el = job->nodes; el; el = el->next) { ++ xdbg_graph_add_edge(gr, job, (BdrvChild *)el->data); ++ } ++ } ++ } ++ ++ QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { ++ xdbg_graph_add_node(gr, bs, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_DRIVER, ++ bs->node_name); ++ QLIST_FOREACH(child, &bs->children, next) { ++ xdbg_graph_add_edge(gr, bs, child); ++ } ++ } ++ ++ return xdbg_graph_finalize(gr); ++} ++ ++BlockDriverState *bdrv_lookup_bs(const char *device, ++ const char *node_name, ++ Error **errp) ++{ ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (device) { ++ blk = blk_by_name(device); ++ ++ if (blk) { ++ bs = blk_bs(blk); ++ if (!bs) { ++ error_setg(errp, "Device '%s' has no medium", device); ++ } ++ ++ return bs; ++ } ++ } ++ ++ if (node_name) { ++ bs = bdrv_find_node(node_name); ++ ++ if (bs) { ++ return bs; ++ } ++ } ++ ++ error_setg(errp, "Cannot find device=\'%s\' nor node-name=\'%s\'", ++ device ? device : "", ++ node_name ? node_name : ""); ++ return NULL; ++} ++ ++/* If 'base' is in the same chain as 'top', return true. Otherwise, ++ * return false. If either argument is NULL, return false. */ ++bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base) ++{ ++ ++ GLOBAL_STATE_CODE(); ++ ++ while (top && top != base) { ++ top = bdrv_filter_or_cow_bs(top); ++ } ++ ++ return top != NULL; ++} ++ ++BlockDriverState *bdrv_next_node(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!bs) { ++ return QTAILQ_FIRST(&graph_bdrv_states); ++ } ++ return QTAILQ_NEXT(bs, node_list); ++} ++ ++BlockDriverState *bdrv_next_all_states(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!bs) { ++ return QTAILQ_FIRST(&all_bdrv_states); ++ } ++ return QTAILQ_NEXT(bs, bs_list); ++} ++ ++const char *bdrv_get_node_name(const BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bs->node_name; ++} ++ ++const char *bdrv_get_parent_name(const BlockDriverState *bs) ++{ ++ BdrvChild *c; ++ const char *name; ++ IO_CODE(); ++ ++ /* If multiple parents have a name, just pick the first one. */ ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ if (c->klass->get_name) { ++ name = c->klass->get_name(c); ++ if (name && *name) { ++ return name; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++/* TODO check what callers really want: bs->node_name or blk_name() */ ++const char *bdrv_get_device_name(const BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bdrv_get_parent_name(bs) ?: ""; ++} ++ ++/* This can be used to identify nodes that might not have a device ++ * name associated. Since node and device names live in the same ++ * namespace, the result is unambiguous. The exception is if both are ++ * absent, then this returns an empty (non-null) string. */ ++const char *bdrv_get_device_or_node_name(const BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bdrv_get_parent_name(bs) ?: bs->node_name; ++} ++ ++int bdrv_get_flags(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bs->open_flags; ++} ++ ++int bdrv_has_zero_init_1(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ return 1; ++} ++ ++int coroutine_mixed_fn bdrv_has_zero_init(BlockDriverState *bs) ++{ ++ BlockDriverState *filtered; ++ GLOBAL_STATE_CODE(); ++ ++ if (!bs->drv) { ++ return 0; ++ } ++ ++ /* If BS is a copy on write image, it is initialized to ++ the contents of the base image, which may not be zeroes. */ ++ if (bdrv_cow_child(bs)) { ++ return 0; ++ } ++ if (bs->drv->bdrv_has_zero_init) { ++ return bs->drv->bdrv_has_zero_init(bs); ++ } ++ ++ filtered = bdrv_filter_bs(bs); ++ if (filtered) { ++ return bdrv_has_zero_init(filtered); ++ } ++ ++ /* safe default */ ++ return 0; ++} ++ ++bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ if (!(bs->open_flags & BDRV_O_UNMAP)) { ++ return false; ++ } ++ ++ return bs->supported_zero_flags & BDRV_REQ_MAY_UNMAP; ++} ++ ++void bdrv_get_backing_filename(BlockDriverState *bs, ++ char *filename, int filename_size) ++{ ++ IO_CODE(); ++ pstrcpy(filename, filename_size, bs->backing_file); ++} ++ ++int coroutine_fn bdrv_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) ++{ ++ int ret; ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ if (!drv->bdrv_co_get_info) { ++ BlockDriverState *filtered = bdrv_filter_bs(bs); ++ if (filtered) { ++ return bdrv_co_get_info(filtered, bdi); ++ } ++ return -ENOTSUP; ++ } ++ memset(bdi, 0, sizeof(*bdi)); ++ ret = drv->bdrv_co_get_info(bs, bdi); ++ if (bdi->subcluster_size == 0) { ++ /* ++ * If the driver left this unset, subclusters are not supported. ++ * Then it is safe to treat each cluster as having only one subcluster. ++ */ ++ bdi->subcluster_size = bdi->cluster_size; ++ } ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (bdi->cluster_size > BDRV_MAX_ALIGNMENT) { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, ++ Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ if (drv && drv->bdrv_get_specific_info) { ++ return drv->bdrv_get_specific_info(bs, errp); ++ } ++ return NULL; ++} ++ ++BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ if (!drv || !drv->bdrv_get_specific_stats) { ++ return NULL; ++ } ++ return drv->bdrv_get_specific_stats(bs); ++} ++ ++void coroutine_fn bdrv_co_debug_event(BlockDriverState *bs, BlkdebugEvent event) ++{ ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!bs || !bs->drv || !bs->drv->bdrv_co_debug_event) { ++ return; ++ } ++ ++ bs->drv->bdrv_co_debug_event(bs, event); ++} ++ ++static BlockDriverState * GRAPH_RDLOCK ++bdrv_find_debug_node(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) { ++ bs = bdrv_primary_bs(bs); ++ } ++ ++ if (bs && bs->drv && bs->drv->bdrv_debug_breakpoint) { ++ assert(bs->drv->bdrv_debug_remove_breakpoint); ++ return bs; ++ } ++ ++ return NULL; ++} ++ ++int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, ++ const char *tag) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ bs = bdrv_find_debug_node(bs); ++ if (bs) { ++ return bs->drv->bdrv_debug_breakpoint(bs, event, tag); ++ } ++ ++ return -ENOTSUP; ++} ++ ++int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ bs = bdrv_find_debug_node(bs); ++ if (bs) { ++ return bs->drv->bdrv_debug_remove_breakpoint(bs, tag); ++ } ++ ++ return -ENOTSUP; ++} ++ ++int bdrv_debug_resume(BlockDriverState *bs, const char *tag) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ while (bs && (!bs->drv || !bs->drv->bdrv_debug_resume)) { ++ bs = bdrv_primary_bs(bs); ++ } ++ ++ if (bs && bs->drv && bs->drv->bdrv_debug_resume) { ++ return bs->drv->bdrv_debug_resume(bs, tag); ++ } ++ ++ return -ENOTSUP; ++} ++ ++bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ while (bs && bs->drv && !bs->drv->bdrv_debug_is_suspended) { ++ bs = bdrv_primary_bs(bs); ++ } ++ ++ if (bs && bs->drv && bs->drv->bdrv_debug_is_suspended) { ++ return bs->drv->bdrv_debug_is_suspended(bs, tag); ++ } ++ ++ return false; ++} ++ ++/* backing_file can either be relative, or absolute, or a protocol. If it is ++ * relative, it must be relative to the chain. So, passing in bs->filename ++ * from a BDS as backing_file should not be done, as that may be relative to ++ * the CWD rather than the chain. */ ++BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, ++ const char *backing_file) ++{ ++ char *filename_full = NULL; ++ char *backing_file_full = NULL; ++ char *filename_tmp = NULL; ++ int is_protocol = 0; ++ bool filenames_refreshed = false; ++ BlockDriverState *curr_bs = NULL; ++ BlockDriverState *retval = NULL; ++ BlockDriverState *bs_below; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!bs || !bs->drv || !backing_file) { ++ return NULL; ++ } ++ ++ filename_full = g_malloc(PATH_MAX); ++ backing_file_full = g_malloc(PATH_MAX); ++ ++ is_protocol = path_has_protocol(backing_file); ++ ++ /* ++ * Being largely a legacy function, skip any filters here ++ * (because filters do not have normal filenames, so they cannot ++ * match anyway; and allowing json:{} filenames is a bit out of ++ * scope). ++ */ ++ for (curr_bs = bdrv_skip_filters(bs); ++ bdrv_cow_child(curr_bs) != NULL; ++ curr_bs = bs_below) ++ { ++ bs_below = bdrv_backing_chain_next(curr_bs); ++ ++ if (bdrv_backing_overridden(curr_bs)) { ++ /* ++ * If the backing file was overridden, we can only compare ++ * directly against the backing node's filename. ++ */ ++ ++ if (!filenames_refreshed) { ++ /* ++ * This will automatically refresh all of the ++ * filenames in the rest of the backing chain, so we ++ * only need to do this once. ++ */ ++ bdrv_refresh_filename(bs_below); ++ filenames_refreshed = true; ++ } ++ ++ if (strcmp(backing_file, bs_below->filename) == 0) { ++ retval = bs_below; ++ break; ++ } ++ } else if (is_protocol || path_has_protocol(curr_bs->backing_file)) { ++ /* ++ * If either of the filename paths is actually a protocol, then ++ * compare unmodified paths; otherwise make paths relative. ++ */ ++ char *backing_file_full_ret; ++ ++ if (strcmp(backing_file, curr_bs->backing_file) == 0) { ++ retval = bs_below; ++ break; ++ } ++ /* Also check against the full backing filename for the image */ ++ backing_file_full_ret = bdrv_get_full_backing_filename(curr_bs, ++ NULL); ++ if (backing_file_full_ret) { ++ bool equal = strcmp(backing_file, backing_file_full_ret) == 0; ++ g_free(backing_file_full_ret); ++ if (equal) { ++ retval = bs_below; ++ break; ++ } ++ } ++ } else { ++ /* If not an absolute filename path, make it relative to the current ++ * image's filename path */ ++ filename_tmp = bdrv_make_absolute_filename(curr_bs, backing_file, ++ NULL); ++ /* We are going to compare canonicalized absolute pathnames */ ++ if (!filename_tmp || !realpath(filename_tmp, filename_full)) { ++ g_free(filename_tmp); ++ continue; ++ } ++ g_free(filename_tmp); ++ ++ /* We need to make sure the backing filename we are comparing against ++ * is relative to the current image filename (or absolute) */ ++ filename_tmp = bdrv_get_full_backing_filename(curr_bs, NULL); ++ if (!filename_tmp || !realpath(filename_tmp, backing_file_full)) { ++ g_free(filename_tmp); ++ continue; ++ } ++ g_free(filename_tmp); ++ ++ if (strcmp(backing_file_full, filename_full) == 0) { ++ retval = bs_below; ++ break; ++ } ++ } ++ } ++ ++ g_free(filename_full); ++ g_free(backing_file_full); ++ return retval; ++} ++ ++void bdrv_init(void) ++{ ++#ifdef CONFIG_BDRV_WHITELIST_TOOLS ++ use_bdrv_whitelist = 1; ++#endif ++ module_call_init(MODULE_INIT_BLOCK); ++} ++ ++void bdrv_init_with_whitelist(void) ++{ ++ use_bdrv_whitelist = 1; ++ bdrv_init(); ++} ++ ++int bdrv_activate(BlockDriverState *bs, Error **errp) ++{ ++ BdrvChild *child, *parent; ++ Error *local_err = NULL; ++ int ret; ++ BdrvDirtyBitmap *bm; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!bs->drv) { ++ return -ENOMEDIUM; ++ } ++ ++ QLIST_FOREACH(child, &bs->children, next) { ++ bdrv_activate(child->bs, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ } ++ ++ /* ++ * Update permissions, they may differ for inactive nodes. ++ * ++ * Note that the required permissions of inactive images are always a ++ * subset of the permissions required after activating the image. This ++ * allows us to just get the permissions upfront without restricting ++ * bdrv_co_invalidate_cache(). ++ * ++ * It also means that in error cases, we don't have to try and revert to ++ * the old permissions (which is an operation that could fail, too). We can ++ * just keep the extended permissions for the next time that an activation ++ * of the image is tried. ++ */ ++ if (bs->open_flags & BDRV_O_INACTIVE) { ++ bs->open_flags &= ~BDRV_O_INACTIVE; ++ ret = bdrv_refresh_perms(bs, NULL, errp); ++ if (ret < 0) { ++ bs->open_flags |= BDRV_O_INACTIVE; ++ return ret; ++ } ++ ++ ret = bdrv_invalidate_cache(bs, errp); ++ if (ret < 0) { ++ bs->open_flags |= BDRV_O_INACTIVE; ++ return ret; ++ } ++ ++ FOR_EACH_DIRTY_BITMAP(bs, bm) { ++ bdrv_dirty_bitmap_skip_store(bm, false); ++ } ++ ++ ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); ++ if (ret < 0) { ++ bs->open_flags |= BDRV_O_INACTIVE; ++ error_setg_errno(errp, -ret, "Could not refresh total sector count"); ++ return ret; ++ } ++ } ++ ++ QLIST_FOREACH(parent, &bs->parents, next_parent) { ++ if (parent->klass->activate) { ++ parent->klass->activate(parent, &local_err); ++ if (local_err) { ++ bs->open_flags |= BDRV_O_INACTIVE; ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp) ++{ ++ Error *local_err = NULL; ++ IO_CODE(); ++ ++ assert(!(bs->open_flags & BDRV_O_INACTIVE)); ++ assert_bdrv_graph_readable(); ++ ++ if (bs->drv->bdrv_co_invalidate_cache) { ++ bs->drv->bdrv_co_invalidate_cache(bs, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++void bdrv_activate_all(Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvNextIterator it; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { ++ int ret; ++ ++ ret = bdrv_activate(bs, errp); ++ if (ret < 0) { ++ bdrv_next_cleanup(&it); ++ return; ++ } ++ } ++} ++ ++static bool GRAPH_RDLOCK ++bdrv_has_bds_parent(BlockDriverState *bs, bool only_active) ++{ ++ BdrvChild *parent; ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH(parent, &bs->parents, next_parent) { ++ if (parent->klass->parent_is_bds) { ++ BlockDriverState *parent_bs = parent->opaque; ++ if (!only_active || !(parent_bs->open_flags & BDRV_O_INACTIVE)) { ++ return true; ++ } ++ } ++ } ++ ++ return false; ++} ++ ++static int GRAPH_RDLOCK bdrv_inactivate_recurse(BlockDriverState *bs) ++{ ++ BdrvChild *child, *parent; ++ int ret; ++ uint64_t cumulative_perms, cumulative_shared_perms; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!bs->drv) { ++ return -ENOMEDIUM; ++ } ++ ++ /* Make sure that we don't inactivate a child before its parent. ++ * It will be covered by recursion from the yet active parent. */ ++ if (bdrv_has_bds_parent(bs, true)) { ++ return 0; ++ } ++ ++ assert(!(bs->open_flags & BDRV_O_INACTIVE)); ++ ++ /* Inactivate this node */ ++ if (bs->drv->bdrv_inactivate) { ++ ret = bs->drv->bdrv_inactivate(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ QLIST_FOREACH(parent, &bs->parents, next_parent) { ++ if (parent->klass->inactivate) { ++ ret = parent->klass->inactivate(parent); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ } ++ ++ bdrv_get_cumulative_perm(bs, &cumulative_perms, ++ &cumulative_shared_perms); ++ if (cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) { ++ /* Our inactive parents still need write access. Inactivation failed. */ ++ return -EPERM; ++ } ++ ++ bs->open_flags |= BDRV_O_INACTIVE; ++ ++ /* ++ * Update permissions, they may differ for inactive nodes. ++ * We only tried to loosen restrictions, so errors are not fatal, ignore ++ * them. ++ */ ++ bdrv_refresh_perms(bs, NULL, NULL); ++ ++ /* Recursively inactivate children */ ++ QLIST_FOREACH(child, &bs->children, next) { ++ ret = bdrv_inactivate_recurse(child->bs); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++int bdrv_inactivate_all(void) ++{ ++ BlockDriverState *bs = NULL; ++ BdrvNextIterator it; ++ int ret = 0; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { ++ /* Nodes with BDS parents are covered by recursion from the last ++ * parent that gets inactivated. Don't inactivate them a second ++ * time if that has already happened. */ ++ if (bdrv_has_bds_parent(bs, false)) { ++ continue; ++ } ++ ret = bdrv_inactivate_recurse(bs); ++ if (ret < 0) { ++ bdrv_next_cleanup(&it); ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++/**************************************************************/ ++/* removable device support */ ++ ++/** ++ * Return TRUE if the media is present ++ */ ++bool coroutine_fn bdrv_co_is_inserted(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ BdrvChild *child; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ return false; ++ } ++ if (drv->bdrv_co_is_inserted) { ++ return drv->bdrv_co_is_inserted(bs); ++ } ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (!bdrv_co_is_inserted(child->bs)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++/** ++ * If eject_flag is TRUE, eject the media. Otherwise, close the tray ++ */ ++void coroutine_fn bdrv_co_eject(BlockDriverState *bs, bool eject_flag) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (drv && drv->bdrv_co_eject) { ++ drv->bdrv_co_eject(bs, eject_flag); ++ } ++} ++ ++/** ++ * Lock or unlock the media (if it is locked, the user won't be able ++ * to eject it manually). ++ */ ++void coroutine_fn bdrv_co_lock_medium(BlockDriverState *bs, bool locked) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ trace_bdrv_lock_medium(bs, locked); ++ ++ if (drv && drv->bdrv_co_lock_medium) { ++ drv->bdrv_co_lock_medium(bs, locked); ++ } ++} ++ ++/* Get a reference to bs */ ++void bdrv_ref(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ bs->refcnt++; ++} ++ ++/* Release a previously grabbed reference to bs. ++ * If after releasing, reference count is zero, the BlockDriverState is ++ * deleted. */ ++void bdrv_unref(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!bs) { ++ return; ++ } ++ assert(bs->refcnt > 0); ++ if (--bs->refcnt == 0) { ++ bdrv_delete(bs); ++ } ++} ++ ++static void bdrv_schedule_unref_bh(void *opaque) ++{ ++ BlockDriverState *bs = opaque; ++ ++ bdrv_unref(bs); ++} ++ ++/* ++ * Release a BlockDriverState reference while holding the graph write lock. ++ * ++ * Calling bdrv_unref() directly is forbidden while holding the graph lock ++ * because bdrv_close() both involves polling and taking the graph lock ++ * internally. bdrv_schedule_unref() instead delays decreasing the refcount and ++ * possibly closing @bs until the graph lock is released. ++ */ ++void bdrv_schedule_unref(BlockDriverState *bs) ++{ ++ if (!bs) { ++ return; ++ } ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_schedule_unref_bh, bs); ++} ++ ++struct BdrvOpBlocker { ++ Error *reason; ++ QLIST_ENTRY(BdrvOpBlocker) list; ++}; ++ ++bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp) ++{ ++ BdrvOpBlocker *blocker; ++ GLOBAL_STATE_CODE(); ++ ++ assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); ++ if (!QLIST_EMPTY(&bs->op_blockers[op])) { ++ blocker = QLIST_FIRST(&bs->op_blockers[op]); ++ error_propagate_prepend(errp, error_copy(blocker->reason), ++ "Node '%s' is busy: ", ++ bdrv_get_device_or_node_name(bs)); ++ return true; ++ } ++ return false; ++} ++ ++void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason) ++{ ++ BdrvOpBlocker *blocker; ++ GLOBAL_STATE_CODE(); ++ assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); ++ ++ blocker = g_new0(BdrvOpBlocker, 1); ++ blocker->reason = reason; ++ QLIST_INSERT_HEAD(&bs->op_blockers[op], blocker, list); ++} ++ ++void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason) ++{ ++ BdrvOpBlocker *blocker, *next; ++ GLOBAL_STATE_CODE(); ++ assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); ++ QLIST_FOREACH_SAFE(blocker, &bs->op_blockers[op], list, next) { ++ if (blocker->reason == reason) { ++ QLIST_REMOVE(blocker, list); ++ g_free(blocker); ++ } ++ } ++} ++ ++void bdrv_op_block_all(BlockDriverState *bs, Error *reason) ++{ ++ int i; ++ GLOBAL_STATE_CODE(); ++ for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { ++ bdrv_op_block(bs, i, reason); ++ } ++} ++ ++void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason) ++{ ++ int i; ++ GLOBAL_STATE_CODE(); ++ for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { ++ bdrv_op_unblock(bs, i, reason); ++ } ++} ++ ++bool bdrv_op_blocker_is_empty(BlockDriverState *bs) ++{ ++ int i; ++ GLOBAL_STATE_CODE(); ++ for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { ++ if (!QLIST_EMPTY(&bs->op_blockers[i])) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++/* ++ * Must not be called while holding the lock of an AioContext other than the ++ * current one. ++ */ ++void bdrv_img_create(const char *filename, const char *fmt, ++ const char *base_filename, const char *base_fmt, ++ char *options, uint64_t img_size, int flags, bool quiet, ++ Error **errp) ++{ ++ QemuOptsList *create_opts = NULL; ++ QemuOpts *opts = NULL; ++ const char *backing_fmt, *backing_file; ++ int64_t size; ++ BlockDriver *drv, *proto_drv; ++ Error *local_err = NULL; ++ int ret = 0; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* Find driver and parse its options */ ++ drv = bdrv_find_format(fmt); ++ if (!drv) { ++ error_setg(errp, "Unknown file format '%s'", fmt); ++ return; ++ } ++ ++ proto_drv = bdrv_find_protocol(filename, true, errp); ++ if (!proto_drv) { ++ return; ++ } ++ ++ if (!drv->create_opts) { ++ error_setg(errp, "Format driver '%s' does not support image creation", ++ drv->format_name); ++ return; ++ } ++ ++ if (!proto_drv->create_opts) { ++ error_setg(errp, "Protocol driver '%s' does not support image creation", ++ proto_drv->format_name); ++ return; ++ } ++ ++ /* Create parameter list */ ++ create_opts = qemu_opts_append(create_opts, drv->create_opts); ++ create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); ++ ++ opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); ++ ++ /* Parse -o options */ ++ if (options) { ++ if (!qemu_opts_do_parse(opts, options, NULL, errp)) { ++ goto out; ++ } ++ } ++ ++ if (!qemu_opt_get(opts, BLOCK_OPT_SIZE)) { ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort); ++ } else if (img_size != UINT64_C(-1)) { ++ error_setg(errp, "The image size must be specified only once"); ++ goto out; ++ } ++ ++ if (base_filename) { ++ if (!qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename, ++ NULL)) { ++ error_setg(errp, "Backing file not supported for file format '%s'", ++ fmt); ++ goto out; ++ } ++ } ++ ++ if (base_fmt) { ++ if (!qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt, NULL)) { ++ error_setg(errp, "Backing file format not supported for file " ++ "format '%s'", fmt); ++ goto out; ++ } ++ } ++ ++ backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); ++ if (backing_file) { ++ if (!strcmp(filename, backing_file)) { ++ error_setg(errp, "Error: Trying to create an image with the " ++ "same filename as the backing file"); ++ goto out; ++ } ++ if (backing_file[0] == '\0') { ++ error_setg(errp, "Expected backing file name, got empty string"); ++ goto out; ++ } ++ } ++ ++ backing_fmt = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); ++ ++ /* The size for the image must always be specified, unless we have a backing ++ * file and we have not been forbidden from opening it. */ ++ size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, img_size); ++ if (backing_file && !(flags & BDRV_O_NO_BACKING)) { ++ BlockDriverState *bs; ++ char *full_backing; ++ int back_flags; ++ QDict *backing_options = NULL; ++ ++ full_backing = ++ bdrv_get_full_backing_filename_from_filename(filename, backing_file, ++ &local_err); ++ if (local_err) { ++ goto out; ++ } ++ assert(full_backing); ++ ++ /* ++ * No need to do I/O here, which allows us to open encrypted ++ * backing images without needing the secret ++ */ ++ back_flags = flags; ++ back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); ++ back_flags |= BDRV_O_NO_IO; ++ ++ backing_options = qdict_new(); ++ if (backing_fmt) { ++ qdict_put_str(backing_options, "driver", backing_fmt); ++ } ++ qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true); ++ ++ bs = bdrv_open(full_backing, NULL, backing_options, back_flags, ++ &local_err); ++ g_free(full_backing); ++ if (!bs) { ++ error_append_hint(&local_err, "Could not open backing image.\n"); ++ goto out; ++ } else { ++ if (!backing_fmt) { ++ error_setg(&local_err, ++ "Backing file specified without backing format"); ++ error_append_hint(&local_err, "Detected format of %s.\n", ++ bs->drv->format_name); ++ goto out; ++ } ++ if (size == -1) { ++ /* Opened BS, have no size */ ++ size = bdrv_getlength(bs); ++ if (size < 0) { ++ error_setg_errno(errp, -size, "Could not get size of '%s'", ++ backing_file); ++ bdrv_unref(bs); ++ goto out; ++ } ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, size, &error_abort); ++ } ++ bdrv_unref(bs); ++ } ++ /* (backing_file && !(flags & BDRV_O_NO_BACKING)) */ ++ } else if (backing_file && !backing_fmt) { ++ error_setg(&local_err, ++ "Backing file specified without backing format"); ++ goto out; ++ } ++ ++ /* Parameter 'size' is not needed for detached LUKS header */ ++ if (size == -1 && ++ !(!strcmp(fmt, "luks") && ++ qemu_opt_get_bool(opts, "detached-header", false))) { ++ error_setg(errp, "Image creation needs a size parameter"); ++ goto out; ++ } ++ ++ if (!quiet) { ++ printf("Formatting '%s', fmt=%s ", filename, fmt); ++ qemu_opts_print(opts, " "); ++ puts(""); ++ fflush(stdout); ++ } ++ ++ ret = bdrv_create(drv, filename, opts, &local_err); ++ ++ if (ret == -EFBIG) { ++ /* This is generally a better message than whatever the driver would ++ * deliver (especially because of the cluster_size_hint), since that ++ * is most probably not much different from "image too large". */ ++ const char *cluster_size_hint = ""; ++ if (qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, 0)) { ++ cluster_size_hint = " (try using a larger cluster size)"; ++ } ++ error_setg(errp, "The image size is too large for file format '%s'" ++ "%s", fmt, cluster_size_hint); ++ error_free(local_err); ++ local_err = NULL; ++ } ++ ++out: ++ qemu_opts_del(opts); ++ qemu_opts_free(create_opts); ++ error_propagate(errp, local_err); ++} ++ ++AioContext *bdrv_get_aio_context(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bs ? bs->aio_context : qemu_get_aio_context(); ++} ++ ++AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ AioContext *old_ctx = qemu_coroutine_get_aio_context(self); ++ AioContext *new_ctx; ++ IO_CODE(); ++ ++ /* ++ * Increase bs->in_flight to ensure that this operation is completed before ++ * moving the node to a different AioContext. Read new_ctx only afterwards. ++ */ ++ bdrv_inc_in_flight(bs); ++ ++ new_ctx = bdrv_get_aio_context(bs); ++ aio_co_reschedule_self(new_ctx); ++ return old_ctx; ++} ++ ++void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx) ++{ ++ IO_CODE(); ++ aio_co_reschedule_self(old_ctx); ++ bdrv_dec_in_flight(bs); ++} ++ ++static void bdrv_do_remove_aio_context_notifier(BdrvAioNotifier *ban) ++{ ++ GLOBAL_STATE_CODE(); ++ QLIST_REMOVE(ban, list); ++ g_free(ban); ++} ++ ++static void bdrv_detach_aio_context(BlockDriverState *bs) ++{ ++ BdrvAioNotifier *baf, *baf_tmp; ++ ++ assert(!bs->walking_aio_notifiers); ++ GLOBAL_STATE_CODE(); ++ bs->walking_aio_notifiers = true; ++ QLIST_FOREACH_SAFE(baf, &bs->aio_notifiers, list, baf_tmp) { ++ if (baf->deleted) { ++ bdrv_do_remove_aio_context_notifier(baf); ++ } else { ++ baf->detach_aio_context(baf->opaque); ++ } ++ } ++ /* Never mind iterating again to check for ->deleted. bdrv_close() will ++ * remove remaining aio notifiers if we aren't called again. ++ */ ++ bs->walking_aio_notifiers = false; ++ ++ if (bs->drv && bs->drv->bdrv_detach_aio_context) { ++ bs->drv->bdrv_detach_aio_context(bs); ++ } ++ ++ bs->aio_context = NULL; ++} ++ ++static void bdrv_attach_aio_context(BlockDriverState *bs, ++ AioContext *new_context) ++{ ++ BdrvAioNotifier *ban, *ban_tmp; ++ GLOBAL_STATE_CODE(); ++ ++ bs->aio_context = new_context; ++ ++ if (bs->drv && bs->drv->bdrv_attach_aio_context) { ++ bs->drv->bdrv_attach_aio_context(bs, new_context); ++ } ++ ++ assert(!bs->walking_aio_notifiers); ++ bs->walking_aio_notifiers = true; ++ QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_tmp) { ++ if (ban->deleted) { ++ bdrv_do_remove_aio_context_notifier(ban); ++ } else { ++ ban->attached_aio_context(new_context, ban->opaque); ++ } ++ } ++ bs->walking_aio_notifiers = false; ++} ++ ++typedef struct BdrvStateSetAioContext { ++ AioContext *new_ctx; ++ BlockDriverState *bs; ++} BdrvStateSetAioContext; ++ ++static bool bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx, ++ GHashTable *visited, ++ Transaction *tran, ++ Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ if (g_hash_table_contains(visited, c)) { ++ return true; ++ } ++ g_hash_table_add(visited, c); ++ ++ /* ++ * A BdrvChildClass that doesn't handle AioContext changes cannot ++ * tolerate any AioContext changes ++ */ ++ if (!c->klass->change_aio_ctx) { ++ char *user = bdrv_child_user_desc(c); ++ error_setg(errp, "Changing iothreads is not supported by %s", user); ++ g_free(user); ++ return false; ++ } ++ if (!c->klass->change_aio_ctx(c, ctx, visited, tran, errp)) { ++ assert(!errp || *errp); ++ return false; ++ } ++ return true; ++} ++ ++bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ if (g_hash_table_contains(visited, c)) { ++ return true; ++ } ++ g_hash_table_add(visited, c); ++ return bdrv_change_aio_context(c->bs, ctx, visited, tran, errp); ++} ++ ++static void bdrv_set_aio_context_clean(void *opaque) ++{ ++ BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque; ++ BlockDriverState *bs = (BlockDriverState *) state->bs; ++ ++ /* Paired with bdrv_drained_begin in bdrv_change_aio_context() */ ++ bdrv_drained_end(bs); ++ ++ g_free(state); ++} ++ ++static void bdrv_set_aio_context_commit(void *opaque) ++{ ++ BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque; ++ BlockDriverState *bs = (BlockDriverState *) state->bs; ++ AioContext *new_context = state->new_ctx; ++ ++ bdrv_detach_aio_context(bs); ++ bdrv_attach_aio_context(bs, new_context); ++} ++ ++static TransactionActionDrv set_aio_context = { ++ .commit = bdrv_set_aio_context_commit, ++ .clean = bdrv_set_aio_context_clean, ++}; ++ ++/* ++ * Changes the AioContext used for fd handlers, timers, and BHs by this ++ * BlockDriverState and all its children and parents. ++ * ++ * Must be called from the main AioContext. ++ * ++ * @visited will accumulate all visited BdrvChild objects. The caller is ++ * responsible for freeing the list afterwards. ++ */ ++static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp) ++{ ++ BdrvChild *c; ++ BdrvStateSetAioContext *state; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (bdrv_get_aio_context(bs) == ctx) { ++ return true; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ if (!bdrv_parent_change_aio_context(c, ctx, visited, tran, errp)) { ++ bdrv_graph_rdunlock_main_loop(); ++ return false; ++ } ++ } ++ ++ QLIST_FOREACH(c, &bs->children, next) { ++ if (!bdrv_child_change_aio_context(c, ctx, visited, tran, errp)) { ++ bdrv_graph_rdunlock_main_loop(); ++ return false; ++ } ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ state = g_new(BdrvStateSetAioContext, 1); ++ *state = (BdrvStateSetAioContext) { ++ .new_ctx = ctx, ++ .bs = bs, ++ }; ++ ++ /* Paired with bdrv_drained_end in bdrv_set_aio_context_clean() */ ++ bdrv_drained_begin(bs); ++ ++ tran_add(tran, &set_aio_context, state); ++ ++ return true; ++} ++ ++/* ++ * Change bs's and recursively all of its parents' and children's AioContext ++ * to the given new context, returning an error if that isn't possible. ++ * ++ * If ignore_child is not NULL, that child (and its subgraph) will not ++ * be touched. ++ */ ++int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, ++ BdrvChild *ignore_child, Error **errp) ++{ ++ Transaction *tran; ++ GHashTable *visited; ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * Recursion phase: go through all nodes of the graph. ++ * Take care of checking that all nodes support changing AioContext ++ * and drain them, building a linear list of callbacks to run if everything ++ * is successful (the transaction itself). ++ */ ++ tran = tran_new(); ++ visited = g_hash_table_new(NULL, NULL); ++ if (ignore_child) { ++ g_hash_table_add(visited, ignore_child); ++ } ++ ret = bdrv_change_aio_context(bs, ctx, visited, tran, errp); ++ g_hash_table_destroy(visited); ++ ++ /* ++ * Linear phase: go through all callbacks collected in the transaction. ++ * Run all callbacks collected in the recursion to switch every node's ++ * AioContext (transaction commit), or undo all changes done in the ++ * recursion (transaction abort). ++ */ ++ ++ if (!ret) { ++ /* Just run clean() callbacks. No AioContext changed. */ ++ tran_abort(tran); ++ return -EPERM; ++ } ++ ++ tran_commit(tran); ++ return 0; ++} ++ ++void bdrv_add_aio_context_notifier(BlockDriverState *bs, ++ void (*attached_aio_context)(AioContext *new_context, void *opaque), ++ void (*detach_aio_context)(void *opaque), void *opaque) ++{ ++ BdrvAioNotifier *ban = g_new(BdrvAioNotifier, 1); ++ *ban = (BdrvAioNotifier){ ++ .attached_aio_context = attached_aio_context, ++ .detach_aio_context = detach_aio_context, ++ .opaque = opaque ++ }; ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_INSERT_HEAD(&bs->aio_notifiers, ban, list); ++} ++ ++void bdrv_remove_aio_context_notifier(BlockDriverState *bs, ++ void (*attached_aio_context)(AioContext *, ++ void *), ++ void (*detach_aio_context)(void *), ++ void *opaque) ++{ ++ BdrvAioNotifier *ban, *ban_next; ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_next) { ++ if (ban->attached_aio_context == attached_aio_context && ++ ban->detach_aio_context == detach_aio_context && ++ ban->opaque == opaque && ++ ban->deleted == false) ++ { ++ if (bs->walking_aio_notifiers) { ++ ban->deleted = true; ++ } else { ++ bdrv_do_remove_aio_context_notifier(ban); ++ } ++ return; ++ } ++ } ++ ++ abort(); ++} ++ ++int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ bool force, ++ Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!bs->drv) { ++ error_setg(errp, "Node is ejected"); ++ return -ENOMEDIUM; ++ } ++ if (!bs->drv->bdrv_amend_options) { ++ error_setg(errp, "Block driver '%s' does not support option amendment", ++ bs->drv->format_name); ++ return -ENOTSUP; ++ } ++ return bs->drv->bdrv_amend_options(bs, opts, status_cb, ++ cb_opaque, force, errp); ++} ++ ++/* ++ * This function checks whether the given @to_replace is allowed to be ++ * replaced by a node that always shows the same data as @bs. This is ++ * used for example to verify whether the mirror job can replace ++ * @to_replace by the target mirrored from @bs. ++ * To be replaceable, @bs and @to_replace may either be guaranteed to ++ * always show the same data (because they are only connected through ++ * filters), or some driver may allow replacing one of its children ++ * because it can guarantee that this child's data is not visible at ++ * all (for example, for dissenting quorum children that have no other ++ * parents). ++ */ ++bool bdrv_recurse_can_replace(BlockDriverState *bs, ++ BlockDriverState *to_replace) ++{ ++ BlockDriverState *filtered; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!bs || !bs->drv) { ++ return false; ++ } ++ ++ if (bs == to_replace) { ++ return true; ++ } ++ ++ /* See what the driver can do */ ++ if (bs->drv->bdrv_recurse_can_replace) { ++ return bs->drv->bdrv_recurse_can_replace(bs, to_replace); ++ } ++ ++ /* For filters without an own implementation, we can recurse on our own */ ++ filtered = bdrv_filter_bs(bs); ++ if (filtered) { ++ return bdrv_recurse_can_replace(filtered, to_replace); ++ } ++ ++ /* Safe default */ ++ return false; ++} ++ ++/* ++ * Check whether the given @node_name can be replaced by a node that ++ * has the same data as @parent_bs. If so, return @node_name's BDS; ++ * NULL otherwise. ++ * ++ * @node_name must be a (recursive) *child of @parent_bs (or this ++ * function will return NULL). ++ * ++ * The result (whether the node can be replaced or not) is only valid ++ * for as long as no graph or permission changes occur. ++ */ ++BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, ++ const char *node_name, Error **errp) ++{ ++ BlockDriverState *to_replace_bs = bdrv_find_node(node_name); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!to_replace_bs) { ++ error_setg(errp, "Failed to find node with node-name='%s'", node_name); ++ return NULL; ++ } ++ ++ if (bdrv_op_is_blocked(to_replace_bs, BLOCK_OP_TYPE_REPLACE, errp)) { ++ return NULL; ++ } ++ ++ /* We don't want arbitrary node of the BDS chain to be replaced only the top ++ * most non filter in order to prevent data corruption. ++ * Another benefit is that this tests exclude backing files which are ++ * blocked by the backing blockers. ++ */ ++ if (!bdrv_recurse_can_replace(parent_bs, to_replace_bs)) { ++ error_setg(errp, "Cannot replace '%s' by a node mirrored from '%s', " ++ "because it cannot be guaranteed that doing so would not " ++ "lead to an abrupt change of visible data", ++ node_name, parent_bs->node_name); ++ return NULL; ++ } ++ ++ return to_replace_bs; ++} ++ ++/** ++ * Iterates through the list of runtime option keys that are said to ++ * be "strong" for a BDS. An option is called "strong" if it changes ++ * a BDS's data. For example, the null block driver's "size" and ++ * "read-zeroes" options are strong, but its "latency-ns" option is ++ * not. ++ * ++ * If a key returned by this function ends with a dot, all options ++ * starting with that prefix are strong. ++ */ ++static const char *const *strong_options(BlockDriverState *bs, ++ const char *const *curopt) ++{ ++ static const char *const global_options[] = { ++ "driver", "filename", NULL ++ }; ++ ++ if (!curopt) { ++ return &global_options[0]; ++ } ++ ++ curopt++; ++ if (curopt == &global_options[ARRAY_SIZE(global_options) - 1] && bs->drv) { ++ curopt = bs->drv->strong_runtime_opts; ++ } ++ ++ return (curopt && *curopt) ? curopt : NULL; ++} ++ ++/** ++ * Copies all strong runtime options from bs->options to the given ++ * QDict. The set of strong option keys is determined by invoking ++ * strong_options(). ++ * ++ * Returns true iff any strong option was present in bs->options (and ++ * thus copied to the target QDict) with the exception of "filename" ++ * and "driver". The caller is expected to use this value to decide ++ * whether the existence of strong options prevents the generation of ++ * a plain filename. ++ */ ++static bool append_strong_runtime_options(QDict *d, BlockDriverState *bs) ++{ ++ bool found_any = false; ++ const char *const *option_name = NULL; ++ ++ if (!bs->drv) { ++ return false; ++ } ++ ++ while ((option_name = strong_options(bs, option_name))) { ++ bool option_given = false; ++ ++ assert(strlen(*option_name) > 0); ++ if ((*option_name)[strlen(*option_name) - 1] != '.') { ++ QObject *entry = qdict_get(bs->options, *option_name); ++ if (!entry) { ++ continue; ++ } ++ ++ qdict_put_obj(d, *option_name, qobject_ref(entry)); ++ option_given = true; ++ } else { ++ const QDictEntry *entry; ++ for (entry = qdict_first(bs->options); entry; ++ entry = qdict_next(bs->options, entry)) ++ { ++ if (strstart(qdict_entry_key(entry), *option_name, NULL)) { ++ qdict_put_obj(d, qdict_entry_key(entry), ++ qobject_ref(qdict_entry_value(entry))); ++ option_given = true; ++ } ++ } ++ } ++ ++ /* While "driver" and "filename" need to be included in a JSON filename, ++ * their existence does not prohibit generation of a plain filename. */ ++ if (!found_any && option_given && ++ strcmp(*option_name, "driver") && strcmp(*option_name, "filename")) ++ { ++ found_any = true; ++ } ++ } ++ ++ if (!qdict_haskey(d, "driver")) { ++ /* Drivers created with bdrv_new_open_driver() may not have a ++ * @driver option. Add it here. */ ++ qdict_put_str(d, "driver", bs->drv->format_name); ++ } ++ ++ return found_any; ++} ++ ++/* Note: This function may return false positives; it may return true ++ * even if opening the backing file specified by bs's image header ++ * would result in exactly bs->backing. */ ++static bool GRAPH_RDLOCK bdrv_backing_overridden(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ if (bs->backing) { ++ return strcmp(bs->auto_backing_file, ++ bs->backing->bs->filename); ++ } else { ++ /* No backing BDS, so if the image header reports any backing ++ * file, it must have been suppressed */ ++ return bs->auto_backing_file[0] != '\0'; ++ } ++} ++ ++/* Updates the following BDS fields: ++ * - exact_filename: A filename which may be used for opening a block device ++ * which (mostly) equals the given BDS (even without any ++ * other options; so reading and writing must return the same ++ * results, but caching etc. may be different) ++ * - full_open_options: Options which, when given when opening a block device ++ * (without a filename), result in a BDS (mostly) ++ * equalling the given one ++ * - filename: If exact_filename is set, it is copied here. Otherwise, ++ * full_open_options is converted to a JSON object, prefixed with ++ * "json:" (for use through the JSON pseudo protocol) and put here. ++ */ ++void bdrv_refresh_filename(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ BdrvChild *child; ++ BlockDriverState *primary_child_bs; ++ QDict *opts; ++ bool backing_overridden; ++ bool generate_json_filename; /* Whether our default implementation should ++ fill exact_filename (false) or not (true) */ ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv) { ++ return; ++ } ++ ++ /* This BDS's file name may depend on any of its children's file names, so ++ * refresh those first */ ++ QLIST_FOREACH(child, &bs->children, next) { ++ bdrv_refresh_filename(child->bs); ++ } ++ ++ if (bs->implicit) { ++ /* For implicit nodes, just copy everything from the single child */ ++ child = QLIST_FIRST(&bs->children); ++ assert(QLIST_NEXT(child, next) == NULL); ++ ++ pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), ++ child->bs->exact_filename); ++ pstrcpy(bs->filename, sizeof(bs->filename), child->bs->filename); ++ ++ qobject_unref(bs->full_open_options); ++ bs->full_open_options = qobject_ref(child->bs->full_open_options); ++ ++ return; ++ } ++ ++ backing_overridden = bdrv_backing_overridden(bs); ++ ++ if (bs->open_flags & BDRV_O_NO_IO) { ++ /* Without I/O, the backing file does not change anything. ++ * Therefore, in such a case (primarily qemu-img), we can ++ * pretend the backing file has not been overridden even if ++ * it technically has been. */ ++ backing_overridden = false; ++ } ++ ++ /* Gather the options QDict */ ++ opts = qdict_new(); ++ generate_json_filename = append_strong_runtime_options(opts, bs); ++ generate_json_filename |= backing_overridden; ++ ++ if (drv->bdrv_gather_child_options) { ++ /* Some block drivers may not want to present all of their children's ++ * options, or name them differently from BdrvChild.name */ ++ drv->bdrv_gather_child_options(bs, opts, backing_overridden); ++ } else { ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (child == bs->backing && !backing_overridden) { ++ /* We can skip the backing BDS if it has not been overridden */ ++ continue; ++ } ++ ++ qdict_put(opts, child->name, ++ qobject_ref(child->bs->full_open_options)); ++ } ++ ++ if (backing_overridden && !bs->backing) { ++ /* Force no backing file */ ++ qdict_put_null(opts, "backing"); ++ } ++ } ++ ++ qobject_unref(bs->full_open_options); ++ bs->full_open_options = opts; ++ ++ primary_child_bs = bdrv_primary_bs(bs); ++ ++ if (drv->bdrv_refresh_filename) { ++ /* Obsolete information is of no use here, so drop the old file name ++ * information before refreshing it */ ++ bs->exact_filename[0] = '\0'; ++ ++ drv->bdrv_refresh_filename(bs); ++ } else if (primary_child_bs) { ++ /* ++ * Try to reconstruct valid information from the underlying ++ * file -- this only works for format nodes (filter nodes ++ * cannot be probed and as such must be selected by the user ++ * either through an options dict, or through a special ++ * filename which the filter driver must construct in its ++ * .bdrv_refresh_filename() implementation). ++ */ ++ ++ bs->exact_filename[0] = '\0'; ++ ++ /* ++ * We can use the underlying file's filename if: ++ * - it has a filename, ++ * - the current BDS is not a filter, ++ * - the file is a protocol BDS, and ++ * - opening that file (as this BDS's format) will automatically create ++ * the BDS tree we have right now, that is: ++ * - the user did not significantly change this BDS's behavior with ++ * some explicit (strong) options ++ * - no non-file child of this BDS has been overridden by the user ++ * Both of these conditions are represented by generate_json_filename. ++ */ ++ if (primary_child_bs->exact_filename[0] && ++ primary_child_bs->drv->protocol_name && ++ !drv->is_filter && !generate_json_filename) ++ { ++ strcpy(bs->exact_filename, primary_child_bs->exact_filename); ++ } ++ } ++ ++ if (bs->exact_filename[0]) { ++ pstrcpy(bs->filename, sizeof(bs->filename), bs->exact_filename); ++ } else { ++ GString *json = qobject_to_json(QOBJECT(bs->full_open_options)); ++ if (snprintf(bs->filename, sizeof(bs->filename), "json:%s", ++ json->str) >= sizeof(bs->filename)) { ++ /* Give user a hint if we truncated things. */ ++ strcpy(bs->filename + sizeof(bs->filename) - 4, "..."); ++ } ++ g_string_free(json, true); ++ } ++} ++ ++char *bdrv_dirname(BlockDriverState *bs, Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *child_bs; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv) { ++ error_setg(errp, "Node '%s' is ejected", bs->node_name); ++ return NULL; ++ } ++ ++ if (drv->bdrv_dirname) { ++ return drv->bdrv_dirname(bs, errp); ++ } ++ ++ child_bs = bdrv_primary_bs(bs); ++ if (child_bs) { ++ return bdrv_dirname(child_bs, errp); ++ } ++ ++ bdrv_refresh_filename(bs); ++ if (bs->exact_filename[0] != '\0') { ++ return path_combine(bs->exact_filename, ""); ++ } ++ ++ error_setg(errp, "Cannot generate a base directory for %s nodes", ++ drv->format_name); ++ return NULL; ++} ++ ++/* ++ * Hot add/remove a BDS's child. So the user can take a child offline when ++ * it is broken and take a new child online ++ */ ++void bdrv_add_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, ++ Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!parent_bs->drv || !parent_bs->drv->bdrv_add_child) { ++ error_setg(errp, "The node %s does not support adding a child", ++ bdrv_get_device_or_node_name(parent_bs)); ++ return; ++ } ++ ++ /* ++ * Non-zoned block drivers do not follow zoned storage constraints ++ * (i.e. sequential writes to zones). Refuse mixing zoned and non-zoned ++ * drivers in a graph. ++ */ ++ if (!parent_bs->drv->supports_zoned_children && ++ child_bs->bl.zoned == BLK_Z_HM) { ++ /* ++ * The host-aware model allows zoned storage constraints and random ++ * write. Allow mixing host-aware and non-zoned drivers. Using ++ * host-aware device as a regular device. ++ */ ++ error_setg(errp, "Cannot add a %s child to a %s parent", ++ child_bs->bl.zoned == BLK_Z_HM ? "zoned" : "non-zoned", ++ parent_bs->drv->supports_zoned_children ? ++ "support zoned children" : "not support zoned children"); ++ return; ++ } ++ ++ if (!QLIST_EMPTY(&child_bs->parents)) { ++ error_setg(errp, "The node %s already has a parent", ++ child_bs->node_name); ++ return; ++ } ++ ++ parent_bs->drv->bdrv_add_child(parent_bs, child_bs, errp); ++} ++ ++void bdrv_del_child(BlockDriverState *parent_bs, BdrvChild *child, Error **errp) ++{ ++ BdrvChild *tmp; ++ ++ GLOBAL_STATE_CODE(); ++ if (!parent_bs->drv || !parent_bs->drv->bdrv_del_child) { ++ error_setg(errp, "The node %s does not support removing a child", ++ bdrv_get_device_or_node_name(parent_bs)); ++ return; ++ } ++ ++ QLIST_FOREACH(tmp, &parent_bs->children, next) { ++ if (tmp == child) { ++ break; ++ } ++ } ++ ++ if (!tmp) { ++ error_setg(errp, "The node %s does not have a child named %s", ++ bdrv_get_device_or_node_name(parent_bs), ++ bdrv_get_device_or_node_name(child->bs)); ++ return; ++ } ++ ++ parent_bs->drv->bdrv_del_child(parent_bs, child, errp); ++} ++ ++int bdrv_make_empty(BdrvChild *c, Error **errp) ++{ ++ BlockDriver *drv = c->bs->drv; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ assert(c->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)); ++ ++ if (!drv->bdrv_make_empty) { ++ error_setg(errp, "%s does not support emptying nodes", ++ drv->format_name); ++ return -ENOTSUP; ++ } ++ ++ ret = drv->bdrv_make_empty(c->bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to empty %s", ++ c->bs->filename); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Return the child that @bs acts as an overlay for, and from which data may be ++ * copied in COW or COR operations. Usually this is the backing file. ++ */ ++BdrvChild *bdrv_cow_child(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ ++ if (!bs || !bs->drv) { ++ return NULL; ++ } ++ ++ if (bs->drv->is_filter) { ++ return NULL; ++ } ++ ++ if (!bs->backing) { ++ return NULL; ++ } ++ ++ assert(bs->backing->role & BDRV_CHILD_COW); ++ return bs->backing; ++} ++ ++/* ++ * If @bs acts as a filter for exactly one of its children, return ++ * that child. ++ */ ++BdrvChild *bdrv_filter_child(BlockDriverState *bs) ++{ ++ BdrvChild *c; ++ IO_CODE(); ++ ++ if (!bs || !bs->drv) { ++ return NULL; ++ } ++ ++ if (!bs->drv->is_filter) { ++ return NULL; ++ } ++ ++ /* Only one of @backing or @file may be used */ ++ assert(!(bs->backing && bs->file)); ++ ++ c = bs->backing ?: bs->file; ++ if (!c) { ++ return NULL; ++ } ++ ++ assert(c->role & BDRV_CHILD_FILTERED); ++ return c; ++} ++ ++/* ++ * Return either the result of bdrv_cow_child() or bdrv_filter_child(), ++ * whichever is non-NULL. ++ * ++ * Return NULL if both are NULL. ++ */ ++BdrvChild *bdrv_filter_or_cow_child(BlockDriverState *bs) ++{ ++ BdrvChild *cow_child = bdrv_cow_child(bs); ++ BdrvChild *filter_child = bdrv_filter_child(bs); ++ IO_CODE(); ++ ++ /* Filter nodes cannot have COW backing files */ ++ assert(!(cow_child && filter_child)); ++ ++ return cow_child ?: filter_child; ++} ++ ++/* ++ * Return the primary child of this node: For filters, that is the ++ * filtered child. For other nodes, that is usually the child storing ++ * metadata. ++ * (A generally more helpful description is that this is (usually) the ++ * child that has the same filename as @bs.) ++ * ++ * Drivers do not necessarily have a primary child; for example quorum ++ * does not. ++ */ ++BdrvChild *bdrv_primary_child(BlockDriverState *bs) ++{ ++ BdrvChild *c, *found = NULL; ++ IO_CODE(); ++ ++ QLIST_FOREACH(c, &bs->children, next) { ++ if (c->role & BDRV_CHILD_PRIMARY) { ++ assert(!found); ++ found = c; ++ } ++ } ++ ++ return found; ++} ++ ++static BlockDriverState * GRAPH_RDLOCK ++bdrv_do_skip_filters(BlockDriverState *bs, bool stop_on_explicit_filter) ++{ ++ BdrvChild *c; ++ ++ if (!bs) { ++ return NULL; ++ } ++ ++ while (!(stop_on_explicit_filter && !bs->implicit)) { ++ c = bdrv_filter_child(bs); ++ if (!c) { ++ /* ++ * A filter that is embedded in a working block graph must ++ * have a child. Assert this here so this function does ++ * not return a filter node that is not expected by the ++ * caller. ++ */ ++ assert(!bs->drv || !bs->drv->is_filter); ++ break; ++ } ++ bs = c->bs; ++ } ++ /* ++ * Note that this treats nodes with bs->drv == NULL as not being ++ * filters (bs->drv == NULL should be replaced by something else ++ * anyway). ++ * The advantage of this behavior is that this function will thus ++ * always return a non-NULL value (given a non-NULL @bs). ++ */ ++ ++ return bs; ++} ++ ++/* ++ * Return the first BDS that has not been added implicitly or that ++ * does not have a filtered child down the chain starting from @bs ++ * (including @bs itself). ++ */ ++BlockDriverState *bdrv_skip_implicit_filters(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_do_skip_filters(bs, true); ++} ++ ++/* ++ * Return the first BDS that does not have a filtered child down the ++ * chain starting from @bs (including @bs itself). ++ */ ++BlockDriverState *bdrv_skip_filters(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bdrv_do_skip_filters(bs, false); ++} ++ ++/* ++ * For a backing chain, return the first non-filter backing image of ++ * the first non-filter image. ++ */ ++BlockDriverState *bdrv_backing_chain_next(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bdrv_skip_filters(bdrv_cow_bs(bdrv_skip_filters(bs))); ++} ++ ++/** ++ * Check whether [offset, offset + bytes) overlaps with the cached ++ * block-status data region. ++ * ++ * If so, and @pnum is not NULL, set *pnum to `bsc.data_end - offset`, ++ * which is what bdrv_bsc_is_data()'s interface needs. ++ * Otherwise, *pnum is not touched. ++ */ ++static bool bdrv_bsc_range_overlaps_locked(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, ++ int64_t *pnum) ++{ ++ BdrvBlockStatusCache *bsc = qatomic_rcu_read(&bs->block_status_cache); ++ bool overlaps; ++ ++ overlaps = ++ qatomic_read(&bsc->valid) && ++ ranges_overlap(offset, bytes, bsc->data_start, ++ bsc->data_end - bsc->data_start); ++ ++ if (overlaps && pnum) { ++ *pnum = bsc->data_end - offset; ++ } ++ ++ return overlaps; ++} ++ ++/** ++ * See block_int.h for this function's documentation. ++ */ ++bool bdrv_bsc_is_data(BlockDriverState *bs, int64_t offset, int64_t *pnum) ++{ ++ IO_CODE(); ++ RCU_READ_LOCK_GUARD(); ++ return bdrv_bsc_range_overlaps_locked(bs, offset, 1, pnum); ++} ++ ++/** ++ * See block_int.h for this function's documentation. ++ */ ++void bdrv_bsc_invalidate_range(BlockDriverState *bs, ++ int64_t offset, int64_t bytes) ++{ ++ IO_CODE(); ++ RCU_READ_LOCK_GUARD(); ++ ++ if (bdrv_bsc_range_overlaps_locked(bs, offset, bytes, NULL)) { ++ qatomic_set(&bs->block_status_cache->valid, false); ++ } ++} ++ ++/** ++ * See block_int.h for this function's documentation. ++ */ ++void bdrv_bsc_fill(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ BdrvBlockStatusCache *new_bsc = g_new(BdrvBlockStatusCache, 1); ++ BdrvBlockStatusCache *old_bsc; ++ IO_CODE(); ++ ++ *new_bsc = (BdrvBlockStatusCache) { ++ .valid = true, ++ .data_start = offset, ++ .data_end = offset + bytes, ++ }; ++ ++ QEMU_LOCK_GUARD(&bs->bsc_modify_lock); ++ ++ old_bsc = qatomic_rcu_read(&bs->block_status_cache); ++ qatomic_rcu_set(&bs->block_status_cache, new_bsc); ++ if (old_bsc) { ++ g_free_rcu(old_bsc, rcu); ++ } ++} +diff --git a/qcow2/lib/block/accounting.c b/qcow2/lib/block/accounting.c +new file mode 100644 +index 00000000..28297453 +--- /dev/null ++++ b/qcow2/lib/block/accounting.c +@@ -0,0 +1,298 @@ ++/* ++ * QEMU System Emulator block accounting ++ * ++ * Copyright (c) 2011 Christoph Hellwig ++ * Copyright (c) 2015 Igalia, S.L. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/accounting.h" ++#include "block/block_int.h" ++#include "qemu/timer.h" ++#include "sysemu/qtest.h" ++ ++static QEMUClockType clock_type = QEMU_CLOCK_REALTIME; ++static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000; ++ ++void block_acct_init(BlockAcctStats *stats) ++{ ++ qemu_mutex_init(&stats->lock); ++ if (qtest_enabled()) { ++ clock_type = QEMU_CLOCK_VIRTUAL; ++ } ++ stats->account_invalid = true; ++ stats->account_failed = true; ++} ++ ++static bool bool_from_onoffauto(OnOffAuto val, bool def) ++{ ++ switch (val) { ++ case ON_OFF_AUTO_AUTO: ++ return def; ++ case ON_OFF_AUTO_ON: ++ return true; ++ case ON_OFF_AUTO_OFF: ++ return false; ++ default: ++ abort(); ++ } ++} ++ ++void block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid, ++ enum OnOffAuto account_failed) ++{ ++ stats->account_invalid = bool_from_onoffauto(account_invalid, ++ stats->account_invalid); ++ stats->account_failed = bool_from_onoffauto(account_failed, ++ stats->account_failed); ++} ++ ++void block_acct_cleanup(BlockAcctStats *stats) ++{ ++ BlockAcctTimedStats *s, *next; ++ QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) { ++ g_free(s); ++ } ++ qemu_mutex_destroy(&stats->lock); ++} ++ ++void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length) ++{ ++ BlockAcctTimedStats *s; ++ unsigned i; ++ ++ s = g_new0(BlockAcctTimedStats, 1); ++ s->interval_length = interval_length; ++ s->stats = stats; ++ qemu_mutex_lock(&stats->lock); ++ QSLIST_INSERT_HEAD(&stats->intervals, s, entries); ++ ++ for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { ++ timed_average_init(&s->latency[i], clock_type, ++ (uint64_t) interval_length * NANOSECONDS_PER_SECOND); ++ } ++ qemu_mutex_unlock(&stats->lock); ++} ++ ++BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats, ++ BlockAcctTimedStats *s) ++{ ++ if (s == NULL) { ++ return QSLIST_FIRST(&stats->intervals); ++ } else { ++ return QSLIST_NEXT(s, entries); ++ } ++} ++ ++void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, ++ int64_t bytes, enum BlockAcctType type) ++{ ++ assert(type < BLOCK_MAX_IOTYPE); ++ ++ cookie->bytes = bytes; ++ cookie->start_time_ns = qemu_clock_get_ns(clock_type); ++ cookie->type = type; ++} ++ ++/* block_latency_histogram_compare_func: ++ * Compare @key with interval [@it[0], @it[1]). ++ * Return: -1 if @key < @it[0] ++ * 0 if @key in [@it[0], @it[1]) ++ * +1 if @key >= @it[1] ++ */ ++static int block_latency_histogram_compare_func(const void *key, const void *it) ++{ ++ uint64_t k = *(uint64_t *)key; ++ uint64_t a = ((uint64_t *)it)[0]; ++ uint64_t b = ((uint64_t *)it)[1]; ++ ++ return k < a ? -1 : (k < b ? 0 : 1); ++} ++ ++static void block_latency_histogram_account(BlockLatencyHistogram *hist, ++ int64_t latency_ns) ++{ ++ uint64_t *pos; ++ ++ if (hist->bins == NULL) { ++ /* histogram disabled */ ++ return; ++ } ++ ++ ++ if (latency_ns < hist->boundaries[0]) { ++ hist->bins[0]++; ++ return; ++ } ++ ++ if (latency_ns >= hist->boundaries[hist->nbins - 2]) { ++ hist->bins[hist->nbins - 1]++; ++ return; ++ } ++ ++ pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2, ++ sizeof(hist->boundaries[0]), ++ block_latency_histogram_compare_func); ++ assert(pos != NULL); ++ ++ hist->bins[pos - hist->boundaries + 1]++; ++} ++ ++int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, ++ uint64List *boundaries) ++{ ++ BlockLatencyHistogram *hist = &stats->latency_histogram[type]; ++ uint64List *entry; ++ uint64_t *ptr; ++ uint64_t prev = 0; ++ int new_nbins = 1; ++ ++ for (entry = boundaries; entry; entry = entry->next) { ++ if (entry->value <= prev) { ++ return -EINVAL; ++ } ++ new_nbins++; ++ prev = entry->value; ++ } ++ ++ hist->nbins = new_nbins; ++ g_free(hist->boundaries); ++ hist->boundaries = g_new(uint64_t, hist->nbins - 1); ++ for (entry = boundaries, ptr = hist->boundaries; entry; ++ entry = entry->next, ptr++) ++ { ++ *ptr = entry->value; ++ } ++ ++ g_free(hist->bins); ++ hist->bins = g_new0(uint64_t, hist->nbins); ++ ++ return 0; ++} ++ ++void block_latency_histograms_clear(BlockAcctStats *stats) ++{ ++ int i; ++ ++ for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { ++ BlockLatencyHistogram *hist = &stats->latency_histogram[i]; ++ g_free(hist->bins); ++ g_free(hist->boundaries); ++ memset(hist, 0, sizeof(*hist)); ++ } ++} ++ ++static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, ++ bool failed) ++{ ++ BlockAcctTimedStats *s; ++ int64_t time_ns = qemu_clock_get_ns(clock_type); ++ int64_t latency_ns = time_ns - cookie->start_time_ns; ++ ++ if (qtest_enabled()) { ++ latency_ns = qtest_latency_ns; ++ } ++ ++ assert(cookie->type < BLOCK_MAX_IOTYPE); ++ ++ if (cookie->type == BLOCK_ACCT_NONE) { ++ return; ++ } ++ ++ WITH_QEMU_LOCK_GUARD(&stats->lock) { ++ if (failed) { ++ stats->failed_ops[cookie->type]++; ++ } else { ++ stats->nr_bytes[cookie->type] += cookie->bytes; ++ stats->nr_ops[cookie->type]++; ++ } ++ ++ block_latency_histogram_account(&stats->latency_histogram[cookie->type], ++ latency_ns); ++ ++ if (!failed || stats->account_failed) { ++ stats->total_time_ns[cookie->type] += latency_ns; ++ stats->last_access_time_ns = time_ns; ++ ++ QSLIST_FOREACH(s, &stats->intervals, entries) { ++ timed_average_account(&s->latency[cookie->type], latency_ns); ++ } ++ } ++ } ++ ++ cookie->type = BLOCK_ACCT_NONE; ++} ++ ++void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie) ++{ ++ block_account_one_io(stats, cookie, false); ++} ++ ++void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie) ++{ ++ block_account_one_io(stats, cookie, true); ++} ++ ++void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type) ++{ ++ assert(type < BLOCK_MAX_IOTYPE); ++ ++ /* block_account_one_io() updates total_time_ns[], but this one does ++ * not. The reason is that invalid requests are accounted during their ++ * submission, therefore there's no actual I/O involved. ++ */ ++ qemu_mutex_lock(&stats->lock); ++ stats->invalid_ops[type]++; ++ ++ if (stats->account_invalid) { ++ stats->last_access_time_ns = qemu_clock_get_ns(clock_type); ++ } ++ qemu_mutex_unlock(&stats->lock); ++} ++ ++void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, ++ int num_requests) ++{ ++ assert(type < BLOCK_MAX_IOTYPE); ++ ++ qemu_mutex_lock(&stats->lock); ++ stats->merged[type] += num_requests; ++ qemu_mutex_unlock(&stats->lock); ++} ++ ++int64_t block_acct_idle_time_ns(BlockAcctStats *stats) ++{ ++ return qemu_clock_get_ns(clock_type) - stats->last_access_time_ns; ++} ++ ++double block_acct_queue_depth(BlockAcctTimedStats *stats, ++ enum BlockAcctType type) ++{ ++ uint64_t sum, elapsed; ++ ++ assert(type < BLOCK_MAX_IOTYPE); ++ ++ qemu_mutex_lock(&stats->stats->lock); ++ sum = timed_average_sum(&stats->latency[type], &elapsed); ++ qemu_mutex_unlock(&stats->stats->lock); ++ ++ return (double) sum / elapsed; ++} +diff --git a/qcow2/lib/block/aio_task.c b/qcow2/lib/block/aio_task.c +new file mode 100644 +index 00000000..9bd17ea2 +--- /dev/null ++++ b/qcow2/lib/block/aio_task.c +@@ -0,0 +1,126 @@ ++/* ++ * Aio tasks loops ++ * ++ * Copyright (c) 2019 Virtuozzo International GmbH. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/aio.h" ++#include "block/aio_task.h" ++ ++struct AioTaskPool { ++ Coroutine *main_co; ++ int status; ++ int max_busy_tasks; ++ int busy_tasks; ++ bool waiting; ++}; ++ ++static void coroutine_fn aio_task_co(void *opaque) ++{ ++ AioTask *task = opaque; ++ AioTaskPool *pool = task->pool; ++ ++ assert(pool->busy_tasks < pool->max_busy_tasks); ++ pool->busy_tasks++; ++ ++ task->ret = task->func(task); ++ ++ pool->busy_tasks--; ++ ++ if (task->ret < 0 && pool->status == 0) { ++ pool->status = task->ret; ++ } ++ ++ g_free(task); ++ ++ if (pool->waiting) { ++ pool->waiting = false; ++ aio_co_wake(pool->main_co); ++ } ++} ++ ++void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool) ++{ ++ assert(pool->busy_tasks > 0); ++ assert(qemu_coroutine_self() == pool->main_co); ++ ++ pool->waiting = true; ++ qemu_coroutine_yield(); ++ ++ assert(!pool->waiting); ++ assert(pool->busy_tasks < pool->max_busy_tasks); ++} ++ ++void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool) ++{ ++ if (pool->busy_tasks < pool->max_busy_tasks) { ++ return; ++ } ++ ++ aio_task_pool_wait_one(pool); ++} ++ ++void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool) ++{ ++ while (pool->busy_tasks > 0) { ++ aio_task_pool_wait_one(pool); ++ } ++} ++ ++void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task) ++{ ++ aio_task_pool_wait_slot(pool); ++ ++ task->pool = pool; ++ qemu_coroutine_enter(qemu_coroutine_create(aio_task_co, task)); ++} ++ ++AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks) ++{ ++ AioTaskPool *pool = g_new0(AioTaskPool, 1); ++ ++ assert(max_busy_tasks > 0); ++ ++ pool->main_co = qemu_coroutine_self(); ++ pool->max_busy_tasks = max_busy_tasks; ++ ++ return pool; ++} ++ ++void aio_task_pool_free(AioTaskPool *pool) ++{ ++ g_free(pool); ++} ++ ++int aio_task_pool_status(AioTaskPool *pool) ++{ ++ if (!pool) { ++ return 0; /* Sugar for lazy allocation of aio pool */ ++ } ++ ++ return pool->status; ++} ++ ++bool aio_task_pool_empty(AioTaskPool *pool) ++{ ++ return pool->busy_tasks == 0; ++} +diff --git a/qcow2/lib/block/block-backend.c b/qcow2/lib/block/block-backend.c +new file mode 100644 +index 00000000..db6f9b92 +--- /dev/null ++++ b/qcow2/lib/block/block-backend.c +@@ -0,0 +1,2899 @@ ++/* ++ * QEMU Block backends ++ * ++ * Copyright (C) 2014-2016 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "sysemu/block-backend.h" ++#include "block/block_int.h" ++#include "block/blockjob.h" ++#include "block/coroutines.h" ++#include "block/throttle-groups.h" ++#include "hw/qdev-core.h" ++#include "sysemu/blockdev.h" ++#include "sysemu/runstate.h" ++#include "sysemu/replay.h" ++#include "qapi/error.h" ++#include "qapi/qapi-events-block.h" ++#include "qemu/id.h" ++#include "qemu/main-loop.h" ++#include "qemu/option.h" ++#include "trace.h" ++#include "migration/misc.h" ++ ++/* Number of coroutines to reserve per attached device model */ ++#define COROUTINE_POOL_RESERVATION 64 ++ ++#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ ++ ++typedef struct BlockBackendAioNotifier { ++ void (*attached_aio_context)(AioContext *new_context, void *opaque); ++ void (*detach_aio_context)(void *opaque); ++ void *opaque; ++ QLIST_ENTRY(BlockBackendAioNotifier) list; ++} BlockBackendAioNotifier; ++ ++struct BlockBackend { ++ char *name; ++ int refcnt; ++ BdrvChild *root; ++ AioContext *ctx; /* access with atomic operations only */ ++ DriveInfo *legacy_dinfo; /* null unless created by drive_new() */ ++ QTAILQ_ENTRY(BlockBackend) link; /* for block_backends */ ++ QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ ++ BlockBackendPublic public; ++ ++ DeviceState *dev; /* attached device model, if any */ ++ const BlockDevOps *dev_ops; ++ void *dev_opaque; ++ ++ /* If the BDS tree is removed, some of its options are stored here (which ++ * can be used to restore those options in the new BDS on insert) */ ++ BlockBackendRootState root_state; ++ ++ bool enable_write_cache; ++ ++ /* I/O stats (display with "info blockstats"). */ ++ BlockAcctStats stats; ++ ++ BlockdevOnError on_read_error, on_write_error; ++ bool iostatus_enabled; ++ BlockDeviceIoStatus iostatus; ++ ++ uint64_t perm; ++ uint64_t shared_perm; ++ bool disable_perm; ++ ++ bool allow_aio_context_change; ++ bool allow_write_beyond_eof; ++ ++ /* Protected by BQL */ ++ NotifierList remove_bs_notifiers, insert_bs_notifiers; ++ QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers; ++ ++ int quiesce_counter; /* atomic: written under BQL, read by other threads */ ++ QemuMutex queued_requests_lock; /* protects queued_requests */ ++ CoQueue queued_requests; ++ bool disable_request_queuing; /* atomic */ ++ ++ VMChangeStateEntry *vmsh; ++ bool force_allow_inactivate; ++ ++ /* Number of in-flight aio requests. BlockDriverState also counts ++ * in-flight requests but aio requests can exist even when blk->root is ++ * NULL, so we cannot rely on its counter for that case. ++ * Accessed with atomic ops. ++ */ ++ unsigned int in_flight; ++}; ++ ++typedef struct BlockBackendAIOCB { ++ BlockAIOCB common; ++ BlockBackend *blk; ++ int ret; ++} BlockBackendAIOCB; ++ ++static const AIOCBInfo block_backend_aiocb_info = { ++ .aiocb_size = sizeof(BlockBackendAIOCB), ++}; ++ ++static void drive_info_del(DriveInfo *dinfo); ++static BlockBackend *bdrv_first_blk(BlockDriverState *bs); ++ ++/* All BlockBackends. Protected by BQL. */ ++static QTAILQ_HEAD(, BlockBackend) block_backends = ++ QTAILQ_HEAD_INITIALIZER(block_backends); ++ ++/* ++ * All BlockBackends referenced by the monitor and which are iterated through by ++ * blk_next(). Protected by BQL. ++ */ ++static QTAILQ_HEAD(, BlockBackend) monitor_block_backends = ++ QTAILQ_HEAD_INITIALIZER(monitor_block_backends); ++ ++static int coroutine_mixed_fn GRAPH_RDLOCK ++blk_set_perm_locked(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, ++ Error **errp); ++ ++static void blk_root_inherit_options(BdrvChildRole role, bool parent_is_format, ++ int *child_flags, QDict *child_options, ++ int parent_flags, QDict *parent_options) ++{ ++ /* We're not supposed to call this function for root nodes */ ++ abort(); ++} ++static void blk_root_drained_begin(BdrvChild *child); ++static bool blk_root_drained_poll(BdrvChild *child); ++static void blk_root_drained_end(BdrvChild *child); ++ ++static void blk_root_change_media(BdrvChild *child, bool load); ++static void blk_root_resize(BdrvChild *child); ++ ++static bool blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp); ++ ++static char *blk_root_get_parent_desc(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ g_autofree char *dev_id = NULL; ++ ++ if (blk->name) { ++ return g_strdup_printf("block device '%s'", blk->name); ++ } ++ ++ dev_id = blk_get_attached_dev_id(blk); ++ if (*dev_id) { ++ return g_strdup_printf("block device '%s'", dev_id); ++ } else { ++ /* TODO Callback into the BB owner for something more detailed */ ++ return g_strdup("an unnamed block device"); ++ } ++} ++ ++static const char *blk_root_get_name(BdrvChild *child) ++{ ++ return blk_name(child->opaque); ++} ++ ++static void blk_vm_state_changed(void *opaque, bool running, RunState state) ++{ ++ Error *local_err = NULL; ++ BlockBackend *blk = opaque; ++ ++ if (state == RUN_STATE_INMIGRATE) { ++ return; ++ } ++ ++ qemu_del_vm_change_state_handler(blk->vmsh); ++ blk->vmsh = NULL; ++ blk_set_perm(blk, blk->perm, blk->shared_perm, &local_err); ++ if (local_err) { ++ error_report_err(local_err); ++ } ++} ++ ++/* ++ * Notifies the user of the BlockBackend that migration has completed. qdev ++ * devices can tighten their permissions in response (specifically revoke ++ * shared write permissions that we needed for storage migration). ++ * ++ * If an error is returned, the VM cannot be allowed to be resumed. ++ */ ++static void GRAPH_RDLOCK blk_root_activate(BdrvChild *child, Error **errp) ++{ ++ BlockBackend *blk = child->opaque; ++ Error *local_err = NULL; ++ uint64_t saved_shared_perm; ++ ++ if (!blk->disable_perm) { ++ return; ++ } ++ ++ blk->disable_perm = false; ++ ++ /* ++ * blk->shared_perm contains the permissions we want to share once ++ * migration is really completely done. For now, we need to share ++ * all; but we also need to retain blk->shared_perm, which is ++ * overwritten by a successful blk_set_perm() call. Save it and ++ * restore it below. ++ */ ++ saved_shared_perm = blk->shared_perm; ++ ++ blk_set_perm_locked(blk, blk->perm, BLK_PERM_ALL, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ blk->disable_perm = true; ++ return; ++ } ++ blk->shared_perm = saved_shared_perm; ++ ++ if (runstate_check(RUN_STATE_INMIGRATE)) { ++ /* Activation can happen when migration process is still active, for ++ * example when nbd_server_add is called during non-shared storage ++ * migration. Defer the shared_perm update to migration completion. */ ++ if (!blk->vmsh) { ++ blk->vmsh = qemu_add_vm_change_state_handler(blk_vm_state_changed, ++ blk); ++ } ++ return; ++ } ++ ++ blk_set_perm_locked(blk, blk->perm, blk->shared_perm, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ blk->disable_perm = true; ++ return; ++ } ++} ++ ++void blk_set_force_allow_inactivate(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ blk->force_allow_inactivate = true; ++} ++ ++static bool blk_can_inactivate(BlockBackend *blk) ++{ ++ /* If it is a guest device, inactivate is ok. */ ++ if (blk->dev || blk_name(blk)[0]) { ++ return true; ++ } ++ ++ /* Inactivating means no more writes to the image can be done, ++ * even if those writes would be changes invisible to the ++ * guest. For block job BBs that satisfy this, we can just allow ++ * it. This is the case for mirror job source, which is required ++ * by libvirt non-shared block migration. */ ++ if (!(blk->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED))) { ++ return true; ++ } ++ ++ return blk->force_allow_inactivate; ++} ++ ++static int GRAPH_RDLOCK blk_root_inactivate(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ ++ if (blk->disable_perm) { ++ return 0; ++ } ++ ++ if (!blk_can_inactivate(blk)) { ++ return -EPERM; ++ } ++ ++ blk->disable_perm = true; ++ if (blk->root) { ++ bdrv_child_try_set_perm(blk->root, 0, BLK_PERM_ALL, &error_abort); ++ } ++ ++ return 0; ++} ++ ++static void blk_root_attach(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ BlockBackendAioNotifier *notifier; ++ ++ trace_blk_root_attach(child, blk, child->bs); ++ ++ QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { ++ bdrv_add_aio_context_notifier(child->bs, ++ notifier->attached_aio_context, ++ notifier->detach_aio_context, ++ notifier->opaque); ++ } ++} ++ ++static void blk_root_detach(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ BlockBackendAioNotifier *notifier; ++ ++ trace_blk_root_detach(child, blk, child->bs); ++ ++ QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { ++ bdrv_remove_aio_context_notifier(child->bs, ++ notifier->attached_aio_context, ++ notifier->detach_aio_context, ++ notifier->opaque); ++ } ++} ++ ++static AioContext *blk_root_get_parent_aio_context(BdrvChild *c) ++{ ++ BlockBackend *blk = c->opaque; ++ IO_CODE(); ++ ++ return blk_get_aio_context(blk); ++} ++ ++static const BdrvChildClass child_root = { ++ .inherit_options = blk_root_inherit_options, ++ ++ .change_media = blk_root_change_media, ++ .resize = blk_root_resize, ++ .get_name = blk_root_get_name, ++ .get_parent_desc = blk_root_get_parent_desc, ++ ++ .drained_begin = blk_root_drained_begin, ++ .drained_poll = blk_root_drained_poll, ++ .drained_end = blk_root_drained_end, ++ ++ .activate = blk_root_activate, ++ .inactivate = blk_root_inactivate, ++ ++ .attach = blk_root_attach, ++ .detach = blk_root_detach, ++ ++ .change_aio_ctx = blk_root_change_aio_ctx, ++ ++ .get_parent_aio_context = blk_root_get_parent_aio_context, ++}; ++ ++/* ++ * Create a new BlockBackend with a reference count of one. ++ * ++ * @perm is a bitmasks of BLK_PERM_* constants which describes the permissions ++ * to request for a block driver node that is attached to this BlockBackend. ++ * @shared_perm is a bitmask which describes which permissions may be granted ++ * to other users of the attached node. ++ * Both sets of permissions can be changed later using blk_set_perm(). ++ * ++ * Return the new BlockBackend on success, null on failure. ++ */ ++BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm) ++{ ++ BlockBackend *blk; ++ ++ GLOBAL_STATE_CODE(); ++ ++ blk = g_new0(BlockBackend, 1); ++ blk->refcnt = 1; ++ blk->ctx = ctx; ++ blk->perm = perm; ++ blk->shared_perm = shared_perm; ++ blk_set_enable_write_cache(blk, true); ++ ++ blk->on_read_error = BLOCKDEV_ON_ERROR_REPORT; ++ blk->on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; ++ ++ block_acct_init(&blk->stats); ++ ++ qemu_mutex_init(&blk->queued_requests_lock); ++ qemu_co_queue_init(&blk->queued_requests); ++ notifier_list_init(&blk->remove_bs_notifiers); ++ notifier_list_init(&blk->insert_bs_notifiers); ++ QLIST_INIT(&blk->aio_notifiers); ++ ++ QTAILQ_INSERT_TAIL(&block_backends, blk, link); ++ return blk; ++} ++ ++/* ++ * Create a new BlockBackend connected to an existing BlockDriverState. ++ * ++ * @perm is a bitmasks of BLK_PERM_* constants which describes the ++ * permissions to request for @bs that is attached to this ++ * BlockBackend. @shared_perm is a bitmask which describes which ++ * permissions may be granted to other users of the attached node. ++ * Both sets of permissions can be changed later using blk_set_perm(). ++ * ++ * Return the new BlockBackend on success, null on failure. ++ */ ++BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm, ++ uint64_t shared_perm, Error **errp) ++{ ++ BlockBackend *blk = blk_new(bdrv_get_aio_context(bs), perm, shared_perm); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (blk_insert_bs(blk, bs, errp) < 0) { ++ blk_unref(blk); ++ return NULL; ++ } ++ return blk; ++} ++ ++/* ++ * Creates a new BlockBackend, opens a new BlockDriverState, and connects both. ++ * By default, the new BlockBackend is in the main AioContext, but if the ++ * parameters connect it with any existing node in a different AioContext, it ++ * may end up there instead. ++ * ++ * Just as with bdrv_open(), after having called this function the reference to ++ * @options belongs to the block layer (even on failure). ++ * ++ * TODO: Remove @filename and @flags; it should be possible to specify a whole ++ * BDS tree just by specifying the @options QDict (or @reference, ++ * alternatively). At the time of adding this function, this is not possible, ++ * though, so callers of this function have to be able to specify @filename and ++ * @flags. ++ */ ++BlockBackend *blk_new_open(const char *filename, const char *reference, ++ QDict *options, int flags, Error **errp) ++{ ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ uint64_t perm = 0; ++ uint64_t shared = BLK_PERM_ALL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * blk_new_open() is mainly used in .bdrv_create implementations and the ++ * tools where sharing isn't a major concern because the BDS stays private ++ * and the file is generally not supposed to be used by a second process, ++ * so we just request permission according to the flags. ++ * ++ * The exceptions are xen_disk and blockdev_init(); in these cases, the ++ * caller of blk_new_open() doesn't make use of the permissions, but they ++ * shouldn't hurt either. We can still share everything here because the ++ * guest devices will add their own blockers if they can't share. ++ */ ++ if ((flags & BDRV_O_NO_IO) == 0) { ++ perm |= BLK_PERM_CONSISTENT_READ; ++ if (flags & BDRV_O_RDWR) { ++ perm |= BLK_PERM_WRITE; ++ } ++ } ++ if (flags & BDRV_O_RESIZE) { ++ perm |= BLK_PERM_RESIZE; ++ } ++ if (flags & BDRV_O_NO_SHARE) { ++ shared = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED; ++ } ++ ++ bs = bdrv_open(filename, reference, options, flags, errp); ++ if (!bs) { ++ return NULL; ++ } ++ ++ /* bdrv_open() could have moved bs to a different AioContext */ ++ blk = blk_new(bdrv_get_aio_context(bs), perm, shared); ++ blk->perm = perm; ++ blk->shared_perm = shared; ++ ++ blk_insert_bs(blk, bs, errp); ++ bdrv_unref(bs); ++ ++ if (!blk->root) { ++ blk_unref(blk); ++ return NULL; ++ } ++ ++ return blk; ++} ++ ++static void blk_delete(BlockBackend *blk) ++{ ++ assert(!blk->refcnt); ++ assert(!blk->name); ++ assert(!blk->dev); ++ if (blk->public.throttle_group_member.throttle_state) { ++ blk_io_limits_disable(blk); ++ } ++ if (blk->root) { ++ blk_remove_bs(blk); ++ } ++ if (blk->vmsh) { ++ qemu_del_vm_change_state_handler(blk->vmsh); ++ blk->vmsh = NULL; ++ } ++ assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); ++ assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); ++ assert(QLIST_EMPTY(&blk->aio_notifiers)); ++ assert(qemu_co_queue_empty(&blk->queued_requests)); ++ qemu_mutex_destroy(&blk->queued_requests_lock); ++ QTAILQ_REMOVE(&block_backends, blk, link); ++ drive_info_del(blk->legacy_dinfo); ++ block_acct_cleanup(&blk->stats); ++ g_free(blk); ++} ++ ++static void drive_info_del(DriveInfo *dinfo) ++{ ++ if (!dinfo) { ++ return; ++ } ++ qemu_opts_del(dinfo->opts); ++ g_free(dinfo); ++} ++ ++int blk_get_refcnt(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk ? blk->refcnt : 0; ++} ++ ++/* ++ * Increment @blk's reference count. ++ * @blk must not be null. ++ */ ++void blk_ref(BlockBackend *blk) ++{ ++ assert(blk->refcnt > 0); ++ GLOBAL_STATE_CODE(); ++ blk->refcnt++; ++} ++ ++/* ++ * Decrement @blk's reference count. ++ * If this drops it to zero, destroy @blk. ++ * For convenience, do nothing if @blk is null. ++ */ ++void blk_unref(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ if (blk) { ++ assert(blk->refcnt > 0); ++ if (blk->refcnt > 1) { ++ blk->refcnt--; ++ } else { ++ blk_drain(blk); ++ /* blk_drain() cannot resurrect blk, nobody held a reference */ ++ assert(blk->refcnt == 1); ++ blk->refcnt = 0; ++ blk_delete(blk); ++ } ++ } ++} ++ ++/* ++ * Behaves similarly to blk_next() but iterates over all BlockBackends, even the ++ * ones which are hidden (i.e. are not referenced by the monitor). ++ */ ++BlockBackend *blk_all_next(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk ? QTAILQ_NEXT(blk, link) ++ : QTAILQ_FIRST(&block_backends); ++} ++ ++void blk_remove_all_bs(void) ++{ ++ BlockBackend *blk = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ while ((blk = blk_all_next(blk)) != NULL) { ++ if (blk->root) { ++ blk_remove_bs(blk); ++ } ++ } ++} ++ ++/* ++ * Return the monitor-owned BlockBackend after @blk. ++ * If @blk is null, return the first one. ++ * Else, return @blk's next sibling, which may be null. ++ * ++ * To iterate over all BlockBackends, do ++ * for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { ++ * ... ++ * } ++ */ ++BlockBackend *blk_next(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk ? QTAILQ_NEXT(blk, monitor_link) ++ : QTAILQ_FIRST(&monitor_block_backends); ++} ++ ++/* Iterates over all top-level BlockDriverStates, i.e. BDSs that are owned by ++ * the monitor or attached to a BlockBackend */ ++BlockDriverState *bdrv_next(BdrvNextIterator *it) ++{ ++ BlockDriverState *bs, *old_bs; ++ ++ /* Must be called from the main loop */ ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ ++ old_bs = it->bs; ++ ++ /* First, return all root nodes of BlockBackends. In order to avoid ++ * returning a BDS twice when multiple BBs refer to it, we only return it ++ * if the BB is the first one in the parent list of the BDS. */ ++ if (it->phase == BDRV_NEXT_BACKEND_ROOTS) { ++ BlockBackend *old_blk = it->blk; ++ ++ do { ++ it->blk = blk_all_next(it->blk); ++ bs = it->blk ? blk_bs(it->blk) : NULL; ++ } while (it->blk && (bs == NULL || bdrv_first_blk(bs) != it->blk)); ++ ++ if (it->blk) { ++ blk_ref(it->blk); ++ } ++ blk_unref(old_blk); ++ ++ if (bs) { ++ bdrv_ref(bs); ++ bdrv_unref(old_bs); ++ it->bs = bs; ++ return bs; ++ } ++ it->phase = BDRV_NEXT_MONITOR_OWNED; ++ } ++ ++ /* Then return the monitor-owned BDSes without a BB attached. Ignore all ++ * BDSes that are attached to a BlockBackend here; they have been handled ++ * by the above block already */ ++ do { ++ it->bs = bdrv_next_monitor_owned(it->bs); ++ bs = it->bs; ++ } while (bs && bdrv_has_blk(bs)); ++ ++ if (bs) { ++ bdrv_ref(bs); ++ } ++ bdrv_unref(old_bs); ++ ++ return bs; ++} ++ ++static void bdrv_next_reset(BdrvNextIterator *it) ++{ ++ *it = (BdrvNextIterator) { ++ .phase = BDRV_NEXT_BACKEND_ROOTS, ++ }; ++} ++ ++BlockDriverState *bdrv_first(BdrvNextIterator *it) ++{ ++ GLOBAL_STATE_CODE(); ++ bdrv_next_reset(it); ++ return bdrv_next(it); ++} ++ ++/* Must be called when aborting a bdrv_next() iteration before ++ * bdrv_next() returns NULL */ ++void bdrv_next_cleanup(BdrvNextIterator *it) ++{ ++ /* Must be called from the main loop */ ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ ++ bdrv_unref(it->bs); ++ ++ if (it->phase == BDRV_NEXT_BACKEND_ROOTS && it->blk) { ++ blk_unref(it->blk); ++ } ++ ++ bdrv_next_reset(it); ++} ++ ++/* ++ * Add a BlockBackend into the list of backends referenced by the monitor, with ++ * the given @name acting as the handle for the monitor. ++ * Strictly for use by blockdev.c. ++ * ++ * @name must not be null or empty. ++ * ++ * Returns true on success and false on failure. In the latter case, an Error ++ * object is returned through @errp. ++ */ ++bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp) ++{ ++ assert(!blk->name); ++ assert(name && name[0]); ++ GLOBAL_STATE_CODE(); ++ ++ if (!id_wellformed(name)) { ++ error_setg(errp, "Invalid device name"); ++ return false; ++ } ++ if (blk_by_name(name)) { ++ error_setg(errp, "Device with id '%s' already exists", name); ++ return false; ++ } ++ if (bdrv_find_node(name)) { ++ error_setg(errp, ++ "Device name '%s' conflicts with an existing node name", ++ name); ++ return false; ++ } ++ ++ blk->name = g_strdup(name); ++ QTAILQ_INSERT_TAIL(&monitor_block_backends, blk, monitor_link); ++ return true; ++} ++ ++/* ++ * Remove a BlockBackend from the list of backends referenced by the monitor. ++ * Strictly for use by blockdev.c. ++ */ ++void monitor_remove_blk(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ if (!blk->name) { ++ return; ++ } ++ ++ QTAILQ_REMOVE(&monitor_block_backends, blk, monitor_link); ++ g_free(blk->name); ++ blk->name = NULL; ++} ++ ++/* ++ * Return @blk's name, a non-null string. ++ * Returns an empty string iff @blk is not referenced by the monitor. ++ */ ++const char *blk_name(const BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk->name ?: ""; ++} ++ ++/* ++ * Return the BlockBackend with name @name if it exists, else null. ++ * @name must not be null. ++ */ ++BlockBackend *blk_by_name(const char *name) ++{ ++ BlockBackend *blk = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ assert(name); ++ while ((blk = blk_next(blk)) != NULL) { ++ if (!strcmp(name, blk->name)) { ++ return blk; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * Return the BlockDriverState attached to @blk if any, else null. ++ */ ++BlockDriverState *blk_bs(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk->root ? blk->root->bs : NULL; ++} ++ ++static BlockBackend * GRAPH_RDLOCK bdrv_first_blk(BlockDriverState *bs) ++{ ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH(child, &bs->parents, next_parent) { ++ if (child->klass == &child_root) { ++ return child->opaque; ++ } ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Returns true if @bs has an associated BlockBackend. ++ */ ++bool bdrv_has_blk(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_first_blk(bs) != NULL; ++} ++ ++/* ++ * Returns true if @bs has only BlockBackends as parents. ++ */ ++bool bdrv_is_root_node(BlockDriverState *bs) ++{ ++ BdrvChild *c; ++ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ if (c->klass != &child_root) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/* ++ * Return @blk's DriveInfo if any, else null. ++ */ ++DriveInfo *blk_legacy_dinfo(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk->legacy_dinfo; ++} ++ ++/* ++ * Set @blk's DriveInfo to @dinfo, and return it. ++ * @blk must not have a DriveInfo set already. ++ * No other BlockBackend may have the same DriveInfo set. ++ */ ++DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo) ++{ ++ assert(!blk->legacy_dinfo); ++ GLOBAL_STATE_CODE(); ++ return blk->legacy_dinfo = dinfo; ++} ++ ++/* ++ * Return the BlockBackend with DriveInfo @dinfo. ++ * It must exist. ++ */ ++BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) ++{ ++ BlockBackend *blk = NULL; ++ GLOBAL_STATE_CODE(); ++ ++ while ((blk = blk_next(blk)) != NULL) { ++ if (blk->legacy_dinfo == dinfo) { ++ return blk; ++ } ++ } ++ abort(); ++} ++ ++/* ++ * Returns a pointer to the publicly accessible fields of @blk. ++ */ ++BlockBackendPublic *blk_get_public(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return &blk->public; ++} ++ ++/* ++ * Returns a BlockBackend given the associated @public fields. ++ */ ++BlockBackend *blk_by_public(BlockBackendPublic *public) ++{ ++ GLOBAL_STATE_CODE(); ++ return container_of(public, BlockBackend, public); ++} ++ ++/* ++ * Disassociates the currently associated BlockDriverState from @blk. ++ */ ++void blk_remove_bs(BlockBackend *blk) ++{ ++ ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ BdrvChild *root; ++ ++ GLOBAL_STATE_CODE(); ++ ++ notifier_list_notify(&blk->remove_bs_notifiers, blk); ++ if (tgm->throttle_state) { ++ BlockDriverState *bs = blk_bs(blk); ++ ++ /* ++ * Take a ref in case blk_bs() changes across bdrv_drained_begin(), for ++ * example, if a temporary filter node is removed by a blockjob. ++ */ ++ bdrv_ref(bs); ++ bdrv_drained_begin(bs); ++ throttle_group_detach_aio_context(tgm); ++ throttle_group_attach_aio_context(tgm, qemu_get_aio_context()); ++ bdrv_drained_end(bs); ++ bdrv_unref(bs); ++ } ++ ++ blk_update_root_state(blk); ++ ++ /* bdrv_root_unref_child() will cause blk->root to become stale and may ++ * switch to a completion coroutine later on. Let's drain all I/O here ++ * to avoid that and a potential QEMU crash. ++ */ ++ blk_drain(blk); ++ root = blk->root; ++ blk->root = NULL; ++ ++ bdrv_graph_wrlock(); ++ bdrv_root_unref_child(root); ++ bdrv_graph_wrunlock(); ++} ++ ++/* ++ * Associates a new BlockDriverState with @blk. ++ */ ++int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) ++{ ++ ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ ++ GLOBAL_STATE_CODE(); ++ bdrv_ref(bs); ++ bdrv_graph_wrlock(); ++ blk->root = bdrv_root_attach_child(bs, "root", &child_root, ++ BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, ++ blk->perm, blk->shared_perm, ++ blk, errp); ++ bdrv_graph_wrunlock(); ++ if (blk->root == NULL) { ++ return -EPERM; ++ } ++ ++ notifier_list_notify(&blk->insert_bs_notifiers, blk); ++ if (tgm->throttle_state) { ++ throttle_group_detach_aio_context(tgm); ++ throttle_group_attach_aio_context(tgm, bdrv_get_aio_context(bs)); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Change BlockDriverState associated with @blk. ++ */ ++int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_replace_child_bs(blk->root, new_bs, errp); ++} ++ ++/* ++ * Sets the permission bitmasks that the user of the BlockBackend needs. ++ */ ++static int coroutine_mixed_fn GRAPH_RDLOCK ++blk_set_perm_locked(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, ++ Error **errp) ++{ ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ if (blk->root && !blk->disable_perm) { ++ ret = bdrv_child_try_set_perm(blk->root, perm, shared_perm, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ blk->perm = perm; ++ blk->shared_perm = shared_perm; ++ ++ return 0; ++} ++ ++int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, ++ Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ return blk_set_perm_locked(blk, perm, shared_perm, errp); ++} ++ ++void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) ++{ ++ GLOBAL_STATE_CODE(); ++ *perm = blk->perm; ++ *shared_perm = blk->shared_perm; ++} ++ ++/* ++ * Attach device model @dev to @blk. ++ * Return 0 on success, -EBUSY when a device model is attached already. ++ */ ++int blk_attach_dev(BlockBackend *blk, DeviceState *dev) ++{ ++ GLOBAL_STATE_CODE(); ++ if (blk->dev) { ++ return -EBUSY; ++ } ++ ++ /* While migration is still incoming, we don't need to apply the ++ * permissions of guest device BlockBackends. We might still have a block ++ * job or NBD server writing to the image for storage migration. */ ++ if (runstate_check(RUN_STATE_INMIGRATE)) { ++ blk->disable_perm = true; ++ } ++ ++ blk_ref(blk); ++ blk->dev = dev; ++ blk_iostatus_reset(blk); ++ ++ return 0; ++} ++ ++/* ++ * Detach device model @dev from @blk. ++ * @dev must be currently attached to @blk. ++ */ ++void blk_detach_dev(BlockBackend *blk, DeviceState *dev) ++{ ++ assert(blk->dev == dev); ++ GLOBAL_STATE_CODE(); ++ blk->dev = NULL; ++ blk->dev_ops = NULL; ++ blk->dev_opaque = NULL; ++ blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort); ++ blk_unref(blk); ++} ++ ++/* ++ * Return the device model attached to @blk if any, else null. ++ */ ++DeviceState *blk_get_attached_dev(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk->dev; ++} ++ ++/* Return the qdev ID, or if no ID is assigned the QOM path, of the block ++ * device attached to the BlockBackend. */ ++char *blk_get_attached_dev_id(BlockBackend *blk) ++{ ++ DeviceState *dev = blk->dev; ++ IO_CODE(); ++ ++ if (!dev) { ++ return g_strdup(""); ++ } else if (dev->id) { ++ return g_strdup(dev->id); ++ } ++ ++ return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); ++} ++ ++/* ++ * Return the BlockBackend which has the device model @dev attached if it ++ * exists, else null. ++ * ++ * @dev must not be null. ++ */ ++BlockBackend *blk_by_dev(void *dev) ++{ ++ BlockBackend *blk = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ assert(dev != NULL); ++ while ((blk = blk_all_next(blk)) != NULL) { ++ if (blk->dev == dev) { ++ return blk; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * Set @blk's device model callbacks to @ops. ++ * @opaque is the opaque argument to pass to the callbacks. ++ * This is for use by device models. ++ */ ++void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, ++ void *opaque) ++{ ++ GLOBAL_STATE_CODE(); ++ blk->dev_ops = ops; ++ blk->dev_opaque = opaque; ++ ++ /* Are we currently quiesced? Should we enforce this right now? */ ++ if (qatomic_read(&blk->quiesce_counter) && ops && ops->drained_begin) { ++ ops->drained_begin(opaque); ++ } ++} ++ ++/* ++ * Notify @blk's attached device model of media change. ++ * ++ * If @load is true, notify of media load. This action can fail, meaning that ++ * the medium cannot be loaded. @errp is set then. ++ * ++ * If @load is false, notify of media eject. This can never fail. ++ * ++ * Also send DEVICE_TRAY_MOVED events as appropriate. ++ */ ++void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ if (blk->dev_ops && blk->dev_ops->change_media_cb) { ++ bool tray_was_open, tray_is_open; ++ Error *local_err = NULL; ++ ++ tray_was_open = blk_dev_is_tray_open(blk); ++ blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err); ++ if (local_err) { ++ assert(load == true); ++ error_propagate(errp, local_err); ++ return; ++ } ++ tray_is_open = blk_dev_is_tray_open(blk); ++ ++ if (tray_was_open != tray_is_open) { ++ char *id = blk_get_attached_dev_id(blk); ++ qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open); ++ g_free(id); ++ } ++ } ++} ++ ++static void blk_root_change_media(BdrvChild *child, bool load) ++{ ++ blk_dev_change_media_cb(child->opaque, load, NULL); ++} ++ ++/* ++ * Does @blk's attached device model have removable media? ++ * %true if no device model is attached. ++ */ ++bool blk_dev_has_removable_media(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return !blk->dev || (blk->dev_ops && blk->dev_ops->change_media_cb); ++} ++ ++/* ++ * Does @blk's attached device model have a tray? ++ */ ++bool blk_dev_has_tray(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk->dev_ops && blk->dev_ops->is_tray_open; ++} ++ ++/* ++ * Notify @blk's attached device model of a media eject request. ++ * If @force is true, the medium is about to be yanked out forcefully. ++ */ ++void blk_dev_eject_request(BlockBackend *blk, bool force) ++{ ++ GLOBAL_STATE_CODE(); ++ if (blk->dev_ops && blk->dev_ops->eject_request_cb) { ++ blk->dev_ops->eject_request_cb(blk->dev_opaque, force); ++ } ++} ++ ++/* ++ * Does @blk's attached device model have a tray, and is it open? ++ */ ++bool blk_dev_is_tray_open(BlockBackend *blk) ++{ ++ IO_CODE(); ++ if (blk_dev_has_tray(blk)) { ++ return blk->dev_ops->is_tray_open(blk->dev_opaque); ++ } ++ return false; ++} ++ ++/* ++ * Does @blk's attached device model have the medium locked? ++ * %false if the device model has no such lock. ++ */ ++bool blk_dev_is_medium_locked(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ if (blk->dev_ops && blk->dev_ops->is_medium_locked) { ++ return blk->dev_ops->is_medium_locked(blk->dev_opaque); ++ } ++ return false; ++} ++ ++/* ++ * Notify @blk's attached device model of a backend size change. ++ */ ++static void blk_root_resize(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ ++ if (blk->dev_ops && blk->dev_ops->resize_cb) { ++ blk->dev_ops->resize_cb(blk->dev_opaque); ++ } ++} ++ ++void blk_iostatus_enable(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ blk->iostatus_enabled = true; ++ blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; ++} ++ ++/* The I/O status is only enabled if the drive explicitly ++ * enables it _and_ the VM is configured to stop on errors */ ++bool blk_iostatus_is_enabled(const BlockBackend *blk) ++{ ++ IO_CODE(); ++ return (blk->iostatus_enabled && ++ (blk->on_write_error == BLOCKDEV_ON_ERROR_ENOSPC || ++ blk->on_write_error == BLOCKDEV_ON_ERROR_STOP || ++ blk->on_read_error == BLOCKDEV_ON_ERROR_STOP)); ++} ++ ++BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk->iostatus; ++} ++ ++void blk_iostatus_disable(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ blk->iostatus_enabled = false; ++} ++ ++void blk_iostatus_reset(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ if (blk_iostatus_is_enabled(blk)) { ++ blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; ++ } ++} ++ ++void blk_iostatus_set_err(BlockBackend *blk, int error) ++{ ++ IO_CODE(); ++ assert(blk_iostatus_is_enabled(blk)); ++ if (blk->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { ++ blk->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : ++ BLOCK_DEVICE_IO_STATUS_FAILED; ++ } ++} ++ ++void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow) ++{ ++ IO_CODE(); ++ blk->allow_write_beyond_eof = allow; ++} ++ ++void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow) ++{ ++ IO_CODE(); ++ blk->allow_aio_context_change = allow; ++} ++ ++void blk_set_disable_request_queuing(BlockBackend *blk, bool disable) ++{ ++ IO_CODE(); ++ qatomic_set(&blk->disable_request_queuing, disable); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++blk_check_byte_request(BlockBackend *blk, int64_t offset, int64_t bytes) ++{ ++ int64_t len; ++ ++ if (bytes < 0) { ++ return -EIO; ++ } ++ ++ if (!blk_co_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ if (offset < 0) { ++ return -EIO; ++ } ++ ++ if (!blk->allow_write_beyond_eof) { ++ len = bdrv_co_getlength(blk_bs(blk)); ++ if (len < 0) { ++ return len; ++ } ++ ++ if (offset > len || len - offset < bytes) { ++ return -EIO; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Are we currently in a drained section? */ ++bool blk_in_drain(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); /* change to IO_OR_GS_CODE(), if necessary */ ++ return qatomic_read(&blk->quiesce_counter); ++} ++ ++/* To be called between exactly one pair of blk_inc/dec_in_flight() */ ++static void coroutine_fn blk_wait_while_drained(BlockBackend *blk) ++{ ++ assert(blk->in_flight > 0); ++ ++ if (qatomic_read(&blk->quiesce_counter) && ++ !qatomic_read(&blk->disable_request_queuing)) { ++ /* ++ * Take lock before decrementing in flight counter so main loop thread ++ * waits for us to enqueue ourselves before it can leave the drained ++ * section. ++ */ ++ qemu_mutex_lock(&blk->queued_requests_lock); ++ blk_dec_in_flight(blk); ++ qemu_co_queue_wait(&blk->queued_requests, &blk->queued_requests_lock); ++ blk_inc_in_flight(blk); ++ qemu_mutex_unlock(&blk->queued_requests_lock); ++ } ++} ++ ++/* To be called between exactly one pair of blk_inc/dec_in_flight() */ ++static int coroutine_fn ++blk_co_do_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ BlockDriverState *bs; ++ IO_CODE(); ++ ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ ++ /* Call blk_bs() only after waiting, the graph may have changed */ ++ bs = blk_bs(blk); ++ trace_blk_co_preadv(blk, bs, offset, bytes, flags); ++ ++ ret = blk_check_byte_request(blk, offset, bytes); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ++ /* throttling disk I/O */ ++ if (blk->public.throttle_group_member.throttle_state) { ++ throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, ++ bytes, THROTTLE_READ); ++ } ++ ++ ret = bdrv_co_preadv_part(blk->root, offset, bytes, qiov, qiov_offset, ++ flags); ++ bdrv_dec_in_flight(bs); ++ return ret; ++} ++ ++int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, int64_t bytes, ++ void *buf, BdrvRequestFlags flags) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); ++ IO_OR_GS_CODE(); ++ ++ assert(bytes <= SIZE_MAX); ++ ++ return blk_co_preadv(blk, offset, bytes, &qiov, flags); ++} ++ ++int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ IO_OR_GS_CODE(); ++ ++ blk_inc_in_flight(blk); ++ ret = blk_co_do_preadv_part(blk, offset, bytes, qiov, 0, flags); ++ blk_dec_in_flight(blk); ++ ++ return ret; ++} ++ ++int coroutine_fn blk_co_preadv_part(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ size_t qiov_offset, BdrvRequestFlags flags) ++{ ++ int ret; ++ IO_OR_GS_CODE(); ++ ++ blk_inc_in_flight(blk); ++ ret = blk_co_do_preadv_part(blk, offset, bytes, qiov, qiov_offset, flags); ++ blk_dec_in_flight(blk); ++ ++ return ret; ++} ++ ++/* To be called between exactly one pair of blk_inc/dec_in_flight() */ ++static int coroutine_fn ++blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ BlockDriverState *bs; ++ IO_CODE(); ++ ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ ++ /* Call blk_bs() only after waiting, the graph may have changed */ ++ bs = blk_bs(blk); ++ trace_blk_co_pwritev(blk, bs, offset, bytes, flags); ++ ++ ret = blk_check_byte_request(blk, offset, bytes); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ /* throttling disk I/O */ ++ if (blk->public.throttle_group_member.throttle_state) { ++ throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, ++ bytes, THROTTLE_WRITE); ++ } ++ ++ if (!blk->enable_write_cache) { ++ flags |= BDRV_REQ_FUA; ++ } ++ ++ ret = bdrv_co_pwritev_part(blk->root, offset, bytes, qiov, qiov_offset, ++ flags); ++ bdrv_dec_in_flight(bs); ++ return ret; ++} ++ ++int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, ++ int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ IO_OR_GS_CODE(); ++ ++ blk_inc_in_flight(blk); ++ ret = blk_co_do_pwritev_part(blk, offset, bytes, qiov, qiov_offset, flags); ++ blk_dec_in_flight(blk); ++ ++ return ret; ++} ++ ++int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, int64_t bytes, ++ const void *buf, BdrvRequestFlags flags) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); ++ IO_OR_GS_CODE(); ++ ++ assert(bytes <= SIZE_MAX); ++ ++ return blk_co_pwritev(blk, offset, bytes, &qiov, flags); ++} ++ ++int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ IO_OR_GS_CODE(); ++ return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, flags); ++} ++ ++int coroutine_fn blk_co_block_status_above(BlockBackend *blk, ++ BlockDriverState *base, ++ int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, ++ BlockDriverState **file) ++{ ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ return bdrv_co_block_status_above(blk_bs(blk), base, offset, bytes, pnum, ++ map, file); ++} ++ ++int coroutine_fn blk_co_is_allocated_above(BlockBackend *blk, ++ BlockDriverState *base, ++ bool include_base, int64_t offset, ++ int64_t bytes, int64_t *pnum) ++{ ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ return bdrv_co_is_allocated_above(blk_bs(blk), base, include_base, offset, ++ bytes, pnum); ++} ++ ++typedef struct BlkRwCo { ++ BlockBackend *blk; ++ int64_t offset; ++ void *iobuf; ++ int ret; ++ BdrvRequestFlags flags; ++} BlkRwCo; ++ ++int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_make_zero(blk->root, flags); ++} ++ ++void blk_inc_in_flight(BlockBackend *blk) ++{ ++ IO_CODE(); ++ qatomic_inc(&blk->in_flight); ++} ++ ++void blk_dec_in_flight(BlockBackend *blk) ++{ ++ IO_CODE(); ++ qatomic_dec(&blk->in_flight); ++ aio_wait_kick(); ++} ++ ++static void error_callback_bh(void *opaque) ++{ ++ struct BlockBackendAIOCB *acb = opaque; ++ ++ blk_dec_in_flight(acb->blk); ++ acb->common.cb(acb->common.opaque, acb->ret); ++ qemu_aio_unref(acb); ++} ++ ++BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, ++ BlockCompletionFunc *cb, ++ void *opaque, int ret) ++{ ++ struct BlockBackendAIOCB *acb; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); ++ acb = blk_aio_get(&block_backend_aiocb_info, blk, cb, opaque); ++ acb->blk = blk; ++ acb->ret = ret; ++ ++ replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), ++ error_callback_bh, acb); ++ return &acb->common; ++} ++ ++typedef struct BlkAioEmAIOCB { ++ BlockAIOCB common; ++ BlkRwCo rwco; ++ int64_t bytes; ++ bool has_returned; ++} BlkAioEmAIOCB; ++ ++static const AIOCBInfo blk_aio_em_aiocb_info = { ++ .aiocb_size = sizeof(BlkAioEmAIOCB), ++}; ++ ++static void blk_aio_complete(BlkAioEmAIOCB *acb) ++{ ++ if (acb->has_returned) { ++ acb->common.cb(acb->common.opaque, acb->rwco.ret); ++ blk_dec_in_flight(acb->rwco.blk); ++ qemu_aio_unref(acb); ++ } ++} ++ ++static void blk_aio_complete_bh(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ assert(acb->has_returned); ++ blk_aio_complete(acb); ++} ++ ++static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, ++ int64_t bytes, ++ void *iobuf, CoroutineEntry co_entry, ++ BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ BlkAioEmAIOCB *acb; ++ Coroutine *co; ++ ++ blk_inc_in_flight(blk); ++ acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); ++ acb->rwco = (BlkRwCo) { ++ .blk = blk, ++ .offset = offset, ++ .iobuf = iobuf, ++ .flags = flags, ++ .ret = NOT_DONE, ++ }; ++ acb->bytes = bytes; ++ acb->has_returned = false; ++ ++ co = qemu_coroutine_create(co_entry, acb); ++ aio_co_enter(qemu_get_current_aio_context(), co); ++ ++ acb->has_returned = true; ++ if (acb->rwco.ret != NOT_DONE) { ++ replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), ++ blk_aio_complete_bh, acb); ++ } ++ ++ return &acb->common; ++} ++ ++static void coroutine_fn blk_aio_read_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ QEMUIOVector *qiov = rwco->iobuf; ++ ++ assert(qiov->size == acb->bytes); ++ rwco->ret = blk_co_do_preadv_part(rwco->blk, rwco->offset, acb->bytes, qiov, ++ 0, rwco->flags); ++ blk_aio_complete(acb); ++} ++ ++static void coroutine_fn blk_aio_write_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ QEMUIOVector *qiov = rwco->iobuf; ++ ++ assert(!qiov || qiov->size == acb->bytes); ++ rwco->ret = blk_co_do_pwritev_part(rwco->blk, rwco->offset, acb->bytes, ++ qiov, 0, rwco->flags); ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_write_entry, ++ flags | BDRV_REQ_ZERO_WRITE, cb, opaque); ++} ++ ++int64_t coroutine_fn blk_co_getlength(BlockBackend *blk) ++{ ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ ++ if (!blk_co_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_co_getlength(blk_bs(blk)); ++} ++ ++int64_t coroutine_fn blk_co_nb_sectors(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ ++ if (!bs) { ++ return -ENOMEDIUM; ++ } else { ++ return bdrv_co_nb_sectors(bs); ++ } ++} ++ ++/* ++ * This wrapper is written by hand because this function is in the hot I/O path, ++ * via blk_get_geometry. ++ */ ++int64_t coroutine_mixed_fn blk_nb_sectors(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ ++ IO_CODE(); ++ ++ if (!bs) { ++ return -ENOMEDIUM; ++ } else { ++ return bdrv_nb_sectors(bs); ++ } ++} ++ ++/* return 0 as number of sectors if no device present or error */ ++void coroutine_fn blk_co_get_geometry(BlockBackend *blk, ++ uint64_t *nb_sectors_ptr) ++{ ++ int64_t ret = blk_co_nb_sectors(blk); ++ *nb_sectors_ptr = ret < 0 ? 0 : ret; ++} ++ ++/* ++ * This wrapper is written by hand because this function is in the hot I/O path. ++ */ ++void coroutine_mixed_fn blk_get_geometry(BlockBackend *blk, ++ uint64_t *nb_sectors_ptr) ++{ ++ int64_t ret = blk_nb_sectors(blk); ++ *nb_sectors_ptr = ret < 0 ? 0 : ret; ++} ++ ++BlockAIOCB *blk_aio_preadv(BlockBackend *blk, int64_t offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ assert((uint64_t)qiov->size <= INT64_MAX); ++ return blk_aio_prwv(blk, offset, qiov->size, qiov, ++ blk_aio_read_entry, flags, cb, opaque); ++} ++ ++BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ assert((uint64_t)qiov->size <= INT64_MAX); ++ return blk_aio_prwv(blk, offset, qiov->size, qiov, ++ blk_aio_write_entry, flags, cb, opaque); ++} ++ ++void blk_aio_cancel(BlockAIOCB *acb) ++{ ++ GLOBAL_STATE_CODE(); ++ bdrv_aio_cancel(acb); ++} ++ ++void blk_aio_cancel_async(BlockAIOCB *acb) ++{ ++ IO_CODE(); ++ bdrv_aio_cancel_async(acb); ++} ++ ++/* To be called between exactly one pair of blk_inc/dec_in_flight() */ ++static int coroutine_fn ++blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf) ++{ ++ IO_CODE(); ++ ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ ++ if (!blk_co_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_co_ioctl(blk_bs(blk), req, buf); ++} ++ ++int coroutine_fn blk_co_ioctl(BlockBackend *blk, unsigned long int req, ++ void *buf) ++{ ++ int ret; ++ IO_OR_GS_CODE(); ++ ++ blk_inc_in_flight(blk); ++ ret = blk_co_do_ioctl(blk, req, buf); ++ blk_dec_in_flight(blk); ++ ++ return ret; ++} ++ ++static void coroutine_fn blk_aio_ioctl_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ ++ rwco->ret = blk_co_do_ioctl(rwco->blk, rwco->offset, rwco->iobuf); ++ ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ return blk_aio_prwv(blk, req, 0, buf, blk_aio_ioctl_entry, 0, cb, opaque); ++} ++ ++/* To be called between exactly one pair of blk_inc/dec_in_flight() */ ++static int coroutine_fn ++blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) ++{ ++ int ret; ++ IO_CODE(); ++ ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ ++ ret = blk_check_byte_request(blk, offset, bytes); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return bdrv_co_pdiscard(blk->root, offset, bytes); ++} ++ ++static void coroutine_fn blk_aio_pdiscard_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ ++ rwco->ret = blk_co_do_pdiscard(rwco->blk, rwco->offset, acb->bytes); ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, ++ int64_t offset, int64_t bytes, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_pdiscard_entry, 0, ++ cb, opaque); ++} ++ ++int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, ++ int64_t bytes) ++{ ++ int ret; ++ IO_OR_GS_CODE(); ++ ++ blk_inc_in_flight(blk); ++ ret = blk_co_do_pdiscard(blk, offset, bytes); ++ blk_dec_in_flight(blk); ++ ++ return ret; ++} ++ ++/* To be called between exactly one pair of blk_inc/dec_in_flight() */ ++static int coroutine_fn blk_co_do_flush(BlockBackend *blk) ++{ ++ IO_CODE(); ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ ++ if (!blk_co_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_co_flush(blk_bs(blk)); ++} ++ ++static void coroutine_fn blk_aio_flush_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ ++ rwco->ret = blk_co_do_flush(rwco->blk); ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_flush(BlockBackend *blk, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ return blk_aio_prwv(blk, 0, 0, NULL, blk_aio_flush_entry, 0, cb, opaque); ++} ++ ++int coroutine_fn blk_co_flush(BlockBackend *blk) ++{ ++ int ret; ++ IO_OR_GS_CODE(); ++ ++ blk_inc_in_flight(blk); ++ ret = blk_co_do_flush(blk); ++ blk_dec_in_flight(blk); ++ ++ return ret; ++} ++ ++static void coroutine_fn blk_aio_zone_report_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ ++ rwco->ret = blk_co_zone_report(rwco->blk, rwco->offset, ++ (unsigned int*)(uintptr_t)acb->bytes, ++ rwco->iobuf); ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_zone_report(BlockBackend *blk, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ BlkAioEmAIOCB *acb; ++ Coroutine *co; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); ++ acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); ++ acb->rwco = (BlkRwCo) { ++ .blk = blk, ++ .offset = offset, ++ .iobuf = zones, ++ .ret = NOT_DONE, ++ }; ++ acb->bytes = (int64_t)(uintptr_t)nr_zones, ++ acb->has_returned = false; ++ ++ co = qemu_coroutine_create(blk_aio_zone_report_entry, acb); ++ aio_co_enter(qemu_get_current_aio_context(), co); ++ ++ acb->has_returned = true; ++ if (acb->rwco.ret != NOT_DONE) { ++ replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), ++ blk_aio_complete_bh, acb); ++ } ++ ++ return &acb->common; ++} ++ ++static void coroutine_fn blk_aio_zone_mgmt_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ ++ rwco->ret = blk_co_zone_mgmt(rwco->blk, ++ (BlockZoneOp)(uintptr_t)rwco->iobuf, ++ rwco->offset, acb->bytes); ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_zone_mgmt(BlockBackend *blk, BlockZoneOp op, ++ int64_t offset, int64_t len, ++ BlockCompletionFunc *cb, void *opaque) { ++ BlkAioEmAIOCB *acb; ++ Coroutine *co; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); ++ acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); ++ acb->rwco = (BlkRwCo) { ++ .blk = blk, ++ .offset = offset, ++ .iobuf = (void *)(uintptr_t)op, ++ .ret = NOT_DONE, ++ }; ++ acb->bytes = len; ++ acb->has_returned = false; ++ ++ co = qemu_coroutine_create(blk_aio_zone_mgmt_entry, acb); ++ aio_co_enter(qemu_get_current_aio_context(), co); ++ ++ acb->has_returned = true; ++ if (acb->rwco.ret != NOT_DONE) { ++ replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), ++ blk_aio_complete_bh, acb); ++ } ++ ++ return &acb->common; ++} ++ ++static void coroutine_fn blk_aio_zone_append_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ ++ rwco->ret = blk_co_zone_append(rwco->blk, (int64_t *)(uintptr_t)acb->bytes, ++ rwco->iobuf, rwco->flags); ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_zone_append(BlockBackend *blk, int64_t *offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque) { ++ BlkAioEmAIOCB *acb; ++ Coroutine *co; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); ++ acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); ++ acb->rwco = (BlkRwCo) { ++ .blk = blk, ++ .ret = NOT_DONE, ++ .flags = flags, ++ .iobuf = qiov, ++ }; ++ acb->bytes = (int64_t)(uintptr_t)offset; ++ acb->has_returned = false; ++ ++ co = qemu_coroutine_create(blk_aio_zone_append_entry, acb); ++ aio_co_enter(qemu_get_current_aio_context(), co); ++ acb->has_returned = true; ++ if (acb->rwco.ret != NOT_DONE) { ++ replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), ++ blk_aio_complete_bh, acb); ++ } ++ ++ return &acb->common; ++} ++ ++/* ++ * Send a zone_report command. ++ * offset is a byte offset from the start of the device. No alignment ++ * required for offset. ++ * nr_zones represents IN maximum and OUT actual. ++ */ ++int coroutine_fn blk_co_zone_report(BlockBackend *blk, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones) ++{ ++ int ret; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); /* increase before waiting */ ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ if (!blk_is_available(blk)) { ++ blk_dec_in_flight(blk); ++ return -ENOMEDIUM; ++ } ++ ret = bdrv_co_zone_report(blk_bs(blk), offset, nr_zones, zones); ++ blk_dec_in_flight(blk); ++ return ret; ++} ++ ++/* ++ * Send a zone_management command. ++ * op is the zone operation; ++ * offset is the byte offset from the start of the zoned device; ++ * len is the maximum number of bytes the command should operate on. It ++ * should be aligned with the device zone size. ++ */ ++int coroutine_fn blk_co_zone_mgmt(BlockBackend *blk, BlockZoneOp op, ++ int64_t offset, int64_t len) ++{ ++ int ret; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ ++ ret = blk_check_byte_request(blk, offset, len); ++ if (ret < 0) { ++ blk_dec_in_flight(blk); ++ return ret; ++ } ++ ++ ret = bdrv_co_zone_mgmt(blk_bs(blk), op, offset, len); ++ blk_dec_in_flight(blk); ++ return ret; ++} ++ ++/* ++ * Send a zone_append command. ++ */ ++int coroutine_fn blk_co_zone_append(BlockBackend *blk, int64_t *offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ int ret; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ if (!blk_is_available(blk)) { ++ blk_dec_in_flight(blk); ++ return -ENOMEDIUM; ++ } ++ ++ ret = bdrv_co_zone_append(blk_bs(blk), offset, qiov, flags); ++ blk_dec_in_flight(blk); ++ return ret; ++} ++ ++void blk_drain(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ bdrv_ref(bs); ++ bdrv_drained_begin(bs); ++ } ++ ++ /* We may have -ENOMEDIUM completions in flight */ ++ AIO_WAIT_WHILE(blk_get_aio_context(blk), ++ qatomic_read(&blk->in_flight) > 0); ++ ++ if (bs) { ++ bdrv_drained_end(bs); ++ bdrv_unref(bs); ++ } ++} ++ ++void blk_drain_all(void) ++{ ++ BlockBackend *blk = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_drain_all_begin(); ++ ++ while ((blk = blk_all_next(blk)) != NULL) { ++ /* We may have -ENOMEDIUM completions in flight */ ++ AIO_WAIT_WHILE_UNLOCKED(NULL, qatomic_read(&blk->in_flight) > 0); ++ } ++ ++ bdrv_drain_all_end(); ++} ++ ++void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, ++ BlockdevOnError on_write_error) ++{ ++ GLOBAL_STATE_CODE(); ++ blk->on_read_error = on_read_error; ++ blk->on_write_error = on_write_error; ++} ++ ++BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read) ++{ ++ IO_CODE(); ++ return is_read ? blk->on_read_error : blk->on_write_error; ++} ++ ++BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, ++ int error) ++{ ++ BlockdevOnError on_err = blk_get_on_error(blk, is_read); ++ IO_CODE(); ++ ++ switch (on_err) { ++ case BLOCKDEV_ON_ERROR_ENOSPC: ++ return (error == ENOSPC) ? ++ BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT; ++ case BLOCKDEV_ON_ERROR_STOP: ++ return BLOCK_ERROR_ACTION_STOP; ++ case BLOCKDEV_ON_ERROR_REPORT: ++ return BLOCK_ERROR_ACTION_REPORT; ++ case BLOCKDEV_ON_ERROR_IGNORE: ++ return BLOCK_ERROR_ACTION_IGNORE; ++ case BLOCKDEV_ON_ERROR_AUTO: ++ default: ++ abort(); ++ } ++} ++ ++static void send_qmp_error_event(BlockBackend *blk, ++ BlockErrorAction action, ++ bool is_read, int error) ++{ ++ IoOperationType optype; ++ BlockDriverState *bs = blk_bs(blk); ++ ++ optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; ++ qapi_event_send_block_io_error(blk_name(blk), ++ bs ? bdrv_get_node_name(bs) : NULL, optype, ++ action, blk_iostatus_is_enabled(blk), ++ error == ENOSPC, strerror(error)); ++} ++ ++/* This is done by device models because, while the block layer knows ++ * about the error, it does not know whether an operation comes from ++ * the device or the block layer (from a job, for example). ++ */ ++void blk_error_action(BlockBackend *blk, BlockErrorAction action, ++ bool is_read, int error) ++{ ++ assert(error >= 0); ++ IO_CODE(); ++ ++ if (action == BLOCK_ERROR_ACTION_STOP) { ++ /* First set the iostatus, so that "info block" returns an iostatus ++ * that matches the events raised so far (an additional error iostatus ++ * is fine, but not a lost one). ++ */ ++ blk_iostatus_set_err(blk, error); ++ ++ /* Then raise the request to stop the VM and the event. ++ * qemu_system_vmstop_request_prepare has two effects. First, ++ * it ensures that the STOP event always comes after the ++ * BLOCK_IO_ERROR event. Second, it ensures that even if management ++ * can observe the STOP event and do a "cont" before the STOP ++ * event is issued, the VM will not stop. In this case, vm_start() ++ * also ensures that the STOP/RESUME pair of events is emitted. ++ */ ++ qemu_system_vmstop_request_prepare(); ++ send_qmp_error_event(blk, action, is_read, error); ++ qemu_system_vmstop_request(RUN_STATE_IO_ERROR); ++ } else { ++ send_qmp_error_event(blk, action, is_read, error); ++ } ++} ++ ++/* ++ * Returns true if the BlockBackend can support taking write permissions ++ * (because its root node is not read-only). ++ */ ++bool blk_supports_write_perm(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ return !bdrv_is_read_only(bs); ++ } else { ++ return blk->root_state.open_flags & BDRV_O_RDWR; ++ } ++} ++ ++/* ++ * Returns true if the BlockBackend can be written to in its current ++ * configuration (i.e. if write permission have been requested) ++ */ ++bool blk_is_writable(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk->perm & BLK_PERM_WRITE; ++} ++ ++bool blk_is_sg(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (!bs) { ++ return false; ++ } ++ ++ return bdrv_is_sg(bs); ++} ++ ++bool blk_enable_write_cache(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk->enable_write_cache; ++} ++ ++void blk_set_enable_write_cache(BlockBackend *blk, bool wce) ++{ ++ IO_CODE(); ++ blk->enable_write_cache = wce; ++} ++ ++void blk_activate(BlockBackend *blk, Error **errp) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (!bs) { ++ error_setg(errp, "Device '%s' has no medium", blk->name); ++ return; ++ } ++ ++ /* ++ * Migration code can call this function in coroutine context, so leave ++ * coroutine context if necessary. ++ */ ++ if (qemu_in_coroutine()) { ++ bdrv_co_activate(bs, errp); ++ } else { ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ bdrv_activate(bs, errp); ++ } ++} ++ ++bool coroutine_fn blk_co_is_inserted(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ return bs && bdrv_co_is_inserted(bs); ++} ++ ++bool coroutine_fn blk_co_is_available(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk_co_is_inserted(blk) && !blk_dev_is_tray_open(blk); ++} ++ ++void coroutine_fn blk_co_lock_medium(BlockBackend *blk, bool locked) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ ++ if (bs) { ++ bdrv_co_lock_medium(bs, locked); ++ } ++} ++ ++void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ char *id; ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ ++ if (bs) { ++ bdrv_co_eject(bs, eject_flag); ++ } ++ ++ /* Whether or not we ejected on the backend, ++ * the frontend experienced a tray event. */ ++ id = blk_get_attached_dev_id(blk); ++ qapi_event_send_device_tray_moved(blk_name(blk), id, ++ eject_flag); ++ g_free(id); ++} ++ ++int blk_get_flags(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ return bdrv_get_flags(bs); ++ } else { ++ return blk->root_state.open_flags; ++ } ++} ++ ++/* Returns the minimum request alignment, in bytes; guaranteed nonzero */ ++uint32_t blk_get_request_alignment(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ IO_CODE(); ++ return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE; ++} ++ ++/* Returns the maximum hardware transfer length, in bytes; guaranteed nonzero */ ++uint64_t blk_get_max_hw_transfer(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ uint64_t max = INT_MAX; ++ IO_CODE(); ++ ++ if (bs) { ++ max = MIN_NON_ZERO(max, bs->bl.max_hw_transfer); ++ max = MIN_NON_ZERO(max, bs->bl.max_transfer); ++ } ++ return ROUND_DOWN(max, blk_get_request_alignment(blk)); ++} ++ ++/* Returns the maximum transfer length, in bytes; guaranteed nonzero */ ++uint32_t blk_get_max_transfer(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ uint32_t max = INT_MAX; ++ IO_CODE(); ++ ++ if (bs) { ++ max = MIN_NON_ZERO(max, bs->bl.max_transfer); ++ } ++ return ROUND_DOWN(max, blk_get_request_alignment(blk)); ++} ++ ++int blk_get_max_hw_iov(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return MIN_NON_ZERO(blk->root->bs->bl.max_hw_iov, ++ blk->root->bs->bl.max_iov); ++} ++ ++int blk_get_max_iov(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk->root->bs->bl.max_iov; ++} ++ ++void *blk_try_blockalign(BlockBackend *blk, size_t size) ++{ ++ IO_CODE(); ++ return qemu_try_blockalign(blk ? blk_bs(blk) : NULL, size); ++} ++ ++void *blk_blockalign(BlockBackend *blk, size_t size) ++{ ++ IO_CODE(); ++ return qemu_blockalign(blk ? blk_bs(blk) : NULL, size); ++} ++ ++bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!bs) { ++ return false; ++ } ++ ++ return bdrv_op_is_blocked(bs, op, errp); ++} ++ ++void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ bdrv_op_unblock(bs, op, reason); ++ } ++} ++ ++void blk_op_block_all(BlockBackend *blk, Error *reason) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ bdrv_op_block_all(bs, reason); ++ } ++} ++ ++void blk_op_unblock_all(BlockBackend *blk, Error *reason) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ bdrv_op_unblock_all(bs, reason); ++ } ++} ++ ++/** ++ * Return BB's current AioContext. Note that this context may change ++ * concurrently at any time, with one exception: If the BB has a root node ++ * attached, its context will only change through bdrv_try_change_aio_context(), ++ * which creates a drained section. Therefore, incrementing such a BB's ++ * in-flight counter will prevent its context from changing. ++ */ ++AioContext *blk_get_aio_context(BlockBackend *blk) ++{ ++ IO_CODE(); ++ ++ if (!blk) { ++ return qemu_get_aio_context(); ++ } ++ ++ return qatomic_read(&blk->ctx); ++} ++ ++int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, ++ Error **errp) ++{ ++ bool old_allow_change; ++ BlockDriverState *bs = blk_bs(blk); ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!bs) { ++ qatomic_set(&blk->ctx, new_context); ++ return 0; ++ } ++ ++ bdrv_ref(bs); ++ ++ old_allow_change = blk->allow_aio_context_change; ++ blk->allow_aio_context_change = true; ++ ++ ret = bdrv_try_change_aio_context(bs, new_context, NULL, errp); ++ ++ blk->allow_aio_context_change = old_allow_change; ++ ++ bdrv_unref(bs); ++ return ret; ++} ++ ++typedef struct BdrvStateBlkRootContext { ++ AioContext *new_ctx; ++ BlockBackend *blk; ++} BdrvStateBlkRootContext; ++ ++static void blk_root_set_aio_ctx_commit(void *opaque) ++{ ++ BdrvStateBlkRootContext *s = opaque; ++ BlockBackend *blk = s->blk; ++ AioContext *new_context = s->new_ctx; ++ ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ ++ qatomic_set(&blk->ctx, new_context); ++ if (tgm->throttle_state) { ++ throttle_group_detach_aio_context(tgm); ++ throttle_group_attach_aio_context(tgm, new_context); ++ } ++} ++ ++static TransactionActionDrv set_blk_root_context = { ++ .commit = blk_root_set_aio_ctx_commit, ++ .clean = g_free, ++}; ++ ++static bool blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp) ++{ ++ BlockBackend *blk = child->opaque; ++ BdrvStateBlkRootContext *s; ++ ++ if (!blk->allow_aio_context_change) { ++ /* ++ * Manually created BlockBackends (those with a name) that are not ++ * attached to anything can change their AioContext without updating ++ * their user; return an error for others. ++ */ ++ if (!blk->name || blk->dev) { ++ /* TODO Add BB name/QOM path */ ++ error_setg(errp, "Cannot change iothread of active block backend"); ++ return false; ++ } ++ } ++ ++ s = g_new(BdrvStateBlkRootContext, 1); ++ *s = (BdrvStateBlkRootContext) { ++ .new_ctx = ctx, ++ .blk = blk, ++ }; ++ ++ tran_add(tran, &set_blk_root_context, s); ++ return true; ++} ++ ++void blk_add_aio_context_notifier(BlockBackend *blk, ++ void (*attached_aio_context)(AioContext *new_context, void *opaque), ++ void (*detach_aio_context)(void *opaque), void *opaque) ++{ ++ BlockBackendAioNotifier *notifier; ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ notifier = g_new(BlockBackendAioNotifier, 1); ++ notifier->attached_aio_context = attached_aio_context; ++ notifier->detach_aio_context = detach_aio_context; ++ notifier->opaque = opaque; ++ QLIST_INSERT_HEAD(&blk->aio_notifiers, notifier, list); ++ ++ if (bs) { ++ bdrv_add_aio_context_notifier(bs, attached_aio_context, ++ detach_aio_context, opaque); ++ } ++} ++ ++void blk_remove_aio_context_notifier(BlockBackend *blk, ++ void (*attached_aio_context)(AioContext *, ++ void *), ++ void (*detach_aio_context)(void *), ++ void *opaque) ++{ ++ BlockBackendAioNotifier *notifier; ++ BlockDriverState *bs = blk_bs(blk); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ bdrv_remove_aio_context_notifier(bs, attached_aio_context, ++ detach_aio_context, opaque); ++ } ++ ++ QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { ++ if (notifier->attached_aio_context == attached_aio_context && ++ notifier->detach_aio_context == detach_aio_context && ++ notifier->opaque == opaque) { ++ QLIST_REMOVE(notifier, list); ++ g_free(notifier); ++ return; ++ } ++ } ++ ++ abort(); ++} ++ ++void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) ++{ ++ GLOBAL_STATE_CODE(); ++ notifier_list_add(&blk->remove_bs_notifiers, notify); ++} ++ ++void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify) ++{ ++ GLOBAL_STATE_CODE(); ++ notifier_list_add(&blk->insert_bs_notifiers, notify); ++} ++ ++BlockAcctStats *blk_get_stats(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return &blk->stats; ++} ++ ++void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ return qemu_aio_get(aiocb_info, blk_bs(blk), cb, opaque); ++} ++ ++int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags) ++{ ++ IO_OR_GS_CODE(); ++ return blk_co_pwritev(blk, offset, bytes, NULL, ++ flags | BDRV_REQ_ZERO_WRITE); ++} ++ ++int coroutine_fn blk_co_pwrite_compressed(BlockBackend *blk, int64_t offset, ++ int64_t bytes, const void *buf) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); ++ IO_OR_GS_CODE(); ++ return blk_co_pwritev_part(blk, offset, bytes, &qiov, 0, ++ BDRV_REQ_WRITE_COMPRESSED); ++} ++ ++int coroutine_fn blk_co_truncate(BlockBackend *blk, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, ++ Error **errp) ++{ ++ IO_OR_GS_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ if (!blk_co_is_available(blk)) { ++ error_setg(errp, "No medium inserted"); ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_co_truncate(blk->root, offset, exact, prealloc, flags, errp); ++} ++ ++int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, ++ int64_t pos, int size) ++{ ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ if (!blk_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ ret = bdrv_save_vmstate(blk_bs(blk), buf, pos, size); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (ret == size && !blk->enable_write_cache) { ++ ret = bdrv_flush(blk_bs(blk)); ++ } ++ ++ return ret < 0 ? ret : size; ++} ++ ++int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!blk_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_load_vmstate(blk_bs(blk), buf, pos, size); ++} ++ ++int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!blk_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_probe_blocksizes(blk_bs(blk), bsz); ++} ++ ++int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!blk_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_probe_geometry(blk_bs(blk), geo); ++} ++ ++/* ++ * Updates the BlockBackendRootState object with data from the currently ++ * attached BlockDriverState. ++ */ ++void blk_update_root_state(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ assert(blk->root); ++ ++ blk->root_state.open_flags = blk->root->bs->open_flags; ++ blk->root_state.detect_zeroes = blk->root->bs->detect_zeroes; ++} ++ ++/* ++ * Returns the detect-zeroes setting to be used for bdrv_open() of a ++ * BlockDriverState which is supposed to inherit the root state. ++ */ ++bool blk_get_detect_zeroes_from_root_state(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk->root_state.detect_zeroes; ++} ++ ++/* ++ * Returns the flags to be used for bdrv_open() of a BlockDriverState which is ++ * supposed to inherit the root state. ++ */ ++int blk_get_open_flags_from_root_state(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk->root_state.open_flags; ++} ++ ++BlockBackendRootState *blk_get_root_state(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return &blk->root_state; ++} ++ ++int blk_commit_all(void) ++{ ++ BlockBackend *blk = NULL; ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ while ((blk = blk_all_next(blk)) != NULL) { ++ BlockDriverState *unfiltered_bs = bdrv_skip_filters(blk_bs(blk)); ++ ++ if (blk_is_inserted(blk) && bdrv_cow_child(unfiltered_bs)) { ++ int ret; ++ ++ ret = bdrv_commit(unfiltered_bs); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ } ++ return 0; ++} ++ ++ ++/* throttling disk I/O limits */ ++void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg) ++{ ++ GLOBAL_STATE_CODE(); ++ throttle_group_config(&blk->public.throttle_group_member, cfg); ++} ++ ++void blk_io_limits_disable(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ assert(tgm->throttle_state); ++ GLOBAL_STATE_CODE(); ++ if (bs) { ++ bdrv_ref(bs); ++ bdrv_drained_begin(bs); ++ } ++ throttle_group_unregister_tgm(tgm); ++ if (bs) { ++ bdrv_drained_end(bs); ++ bdrv_unref(bs); ++ } ++} ++ ++/* should be called before blk_set_io_limits if a limit is set */ ++void blk_io_limits_enable(BlockBackend *blk, const char *group) ++{ ++ assert(!blk->public.throttle_group_member.throttle_state); ++ GLOBAL_STATE_CODE(); ++ throttle_group_register_tgm(&blk->public.throttle_group_member, ++ group, blk_get_aio_context(blk)); ++} ++ ++void blk_io_limits_update_group(BlockBackend *blk, const char *group) ++{ ++ GLOBAL_STATE_CODE(); ++ /* this BB is not part of any group */ ++ if (!blk->public.throttle_group_member.throttle_state) { ++ return; ++ } ++ ++ /* this BB is a part of the same group than the one we want */ ++ if (!g_strcmp0(throttle_group_get_name(&blk->public.throttle_group_member), ++ group)) { ++ return; ++ } ++ ++ /* need to change the group this bs belong to */ ++ blk_io_limits_disable(blk); ++ blk_io_limits_enable(blk, group); ++} ++ ++static void blk_root_drained_begin(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ ++ if (qatomic_fetch_inc(&blk->quiesce_counter) == 0) { ++ if (blk->dev_ops && blk->dev_ops->drained_begin) { ++ blk->dev_ops->drained_begin(blk->dev_opaque); ++ } ++ } ++ ++ /* Note that blk->root may not be accessible here yet if we are just ++ * attaching to a BlockDriverState that is drained. Use child instead. */ ++ ++ if (qatomic_fetch_inc(&tgm->io_limits_disabled) == 0) { ++ throttle_group_restart_tgm(tgm); ++ } ++} ++ ++static bool blk_root_drained_poll(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ bool busy = false; ++ assert(qatomic_read(&blk->quiesce_counter)); ++ ++ if (blk->dev_ops && blk->dev_ops->drained_poll) { ++ busy = blk->dev_ops->drained_poll(blk->dev_opaque); ++ } ++ return busy || !!blk->in_flight; ++} ++ ++static void blk_root_drained_end(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ assert(qatomic_read(&blk->quiesce_counter)); ++ ++ assert(blk->public.throttle_group_member.io_limits_disabled); ++ qatomic_dec(&blk->public.throttle_group_member.io_limits_disabled); ++ ++ if (qatomic_fetch_dec(&blk->quiesce_counter) == 1) { ++ if (blk->dev_ops && blk->dev_ops->drained_end) { ++ blk->dev_ops->drained_end(blk->dev_opaque); ++ } ++ qemu_mutex_lock(&blk->queued_requests_lock); ++ while (qemu_co_enter_next(&blk->queued_requests, ++ &blk->queued_requests_lock)) { ++ /* Resume all queued requests */ ++ } ++ qemu_mutex_unlock(&blk->queued_requests_lock); ++ } ++} ++ ++bool blk_register_buf(BlockBackend *blk, void *host, size_t size, Error **errp) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ return bdrv_register_buf(bs, host, size, errp); ++ } ++ return true; ++} ++ ++void blk_unregister_buf(BlockBackend *blk, void *host, size_t size) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ bdrv_unregister_buf(bs, host, size); ++ } ++} ++ ++int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, ++ BlockBackend *blk_out, int64_t off_out, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ int r; ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ ++ r = blk_check_byte_request(blk_in, off_in, bytes); ++ if (r) { ++ return r; ++ } ++ r = blk_check_byte_request(blk_out, off_out, bytes); ++ if (r) { ++ return r; ++ } ++ ++ return bdrv_co_copy_range(blk_in->root, off_in, ++ blk_out->root, off_out, ++ bytes, read_flags, write_flags); ++} ++ ++const BdrvChild *blk_root(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk->root; ++} ++ ++int blk_make_empty(BlockBackend *blk, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!blk_is_available(blk)) { ++ error_setg(errp, "No medium inserted"); ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_make_empty(blk->root, errp); ++} +diff --git a/qcow2/lib/block/block-gen.c b/qcow2/lib/block/block-gen.c +new file mode 100644 +index 00000000..ff2832fa +--- /dev/null ++++ b/qcow2/lib/block/block-gen.c +@@ -0,0 +1,2636 @@ ++/* ++ * File is generated by scripts/block-coroutine-wrapper.py ++ * ++ * Copyright (c) 2020 Virtuozzo International GmbH. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/coroutines.h" ++#include "block/block-gen.h" ++#include "block/block_int.h" ++#include "block/dirty-bitmap.h" ++ ++ ++ ++/* ++ * Wrappers for bdrv_co_pwrite_zeroes ++ */ ++ ++typedef struct BdrvPwriteZeroes { ++ BdrvPollCo poll_state; ++ int ret; ++ BdrvChild *child; ++ int64_t offset; ++ int64_t bytes; ++ BdrvRequestFlags flags; ++} BdrvPwriteZeroes; ++ ++static void coroutine_fn bdrv_co_pwrite_zeroes_entry(void *opaque) ++{ ++ BdrvPwriteZeroes *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_pwrite_zeroes(s->child, s->offset, s->bytes, s->flags); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int64_t bytes, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_pwrite_zeroes(child, offset, bytes, flags); ++ } else { ++ BdrvPwriteZeroes s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .child = child, ++ .offset = offset, ++ .bytes = bytes, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_pwrite_zeroes_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_pread ++ */ ++ ++typedef struct BdrvPread { ++ BdrvPollCo poll_state; ++ int ret; ++ BdrvChild *child; ++ int64_t offset; ++ int64_t bytes; ++ void *buf; ++ BdrvRequestFlags flags; ++} BdrvPread; ++ ++static void coroutine_fn bdrv_co_pread_entry(void *opaque) ++{ ++ BdrvPread *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_pread(s->child, s->offset, s->bytes, s->buf, s->flags); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_pread(BdrvChild *child, int64_t offset, int64_t bytes, void *buf, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_pread(child, offset, bytes, buf, flags); ++ } else { ++ BdrvPread s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .child = child, ++ .offset = offset, ++ .bytes = bytes, ++ .buf = buf, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_pread_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_pwrite ++ */ ++ ++typedef struct BdrvPwrite { ++ BdrvPollCo poll_state; ++ int ret; ++ BdrvChild *child; ++ int64_t offset; ++ int64_t bytes; ++ const void *buf; ++ BdrvRequestFlags flags; ++} BdrvPwrite; ++ ++static void coroutine_fn bdrv_co_pwrite_entry(void *opaque) ++{ ++ BdrvPwrite *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_pwrite(s->child, s->offset, s->bytes, s->buf, s->flags); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_pwrite(BdrvChild *child, int64_t offset, int64_t bytes, const void *buf, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_pwrite(child, offset, bytes, buf, flags); ++ } else { ++ BdrvPwrite s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .child = child, ++ .offset = offset, ++ .bytes = bytes, ++ .buf = buf, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_pwrite_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_pwrite_sync ++ */ ++ ++typedef struct BdrvPwriteSync { ++ BdrvPollCo poll_state; ++ int ret; ++ BdrvChild *child; ++ int64_t offset; ++ int64_t bytes; ++ const void *buf; ++ BdrvRequestFlags flags; ++} BdrvPwriteSync; ++ ++static void coroutine_fn bdrv_co_pwrite_sync_entry(void *opaque) ++{ ++ BdrvPwriteSync *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_pwrite_sync(s->child, s->offset, s->bytes, s->buf, s->flags); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, int64_t bytes, const void *buf, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_pwrite_sync(child, offset, bytes, buf, flags); ++ } else { ++ BdrvPwriteSync s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .child = child, ++ .offset = offset, ++ .bytes = bytes, ++ .buf = buf, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_pwrite_sync_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_getlength ++ */ ++ ++typedef struct BdrvGetlength { ++ BdrvPollCo poll_state; ++ int64_t ret; ++ BlockDriverState *bs; ++} BdrvGetlength; ++ ++static void coroutine_fn bdrv_co_getlength_entry(void *opaque) ++{ ++ BdrvGetlength *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_getlength(s->bs); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int64_t bdrv_getlength(BlockDriverState *bs) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_getlength(bs); ++ } else { ++ BdrvGetlength s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_getlength_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_get_allocated_file_size ++ */ ++ ++typedef struct BdrvGetAllocatedFileSize { ++ BdrvPollCo poll_state; ++ int64_t ret; ++ BlockDriverState *bs; ++} BdrvGetAllocatedFileSize; ++ ++static void coroutine_fn bdrv_co_get_allocated_file_size_entry(void *opaque) ++{ ++ BdrvGetAllocatedFileSize *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_get_allocated_file_size(s->bs); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) ++{ ++ BdrvGetAllocatedFileSize s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_get_allocated_file_size_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_block_status ++ */ ++ ++typedef struct BdrvBlockStatus { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ int64_t offset; ++ int64_t bytes; ++ int64_t *pnum; ++ int64_t *map; ++ BlockDriverState **file; ++} BdrvBlockStatus; ++ ++static void coroutine_fn bdrv_co_block_status_entry(void *opaque) ++{ ++ BdrvBlockStatus *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_block_status(s->bs, s->offset, s->bytes, s->pnum, s->map, s->file); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_block_status(bs, offset, bytes, pnum, map, file); ++ } else { ++ BdrvBlockStatus s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .offset = offset, ++ .bytes = bytes, ++ .pnum = pnum, ++ .map = map, ++ .file = file, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_block_status_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_block_status_above ++ */ ++ ++typedef struct BdrvBlockStatusAbove { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ BlockDriverState *base; ++ int64_t offset; ++ int64_t bytes; ++ int64_t *pnum; ++ int64_t *map; ++ BlockDriverState **file; ++} BdrvBlockStatusAbove; ++ ++static void coroutine_fn bdrv_co_block_status_above_entry(void *opaque) ++{ ++ BdrvBlockStatusAbove *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_block_status_above(s->bs, s->base, s->offset, s->bytes, s->pnum, s->map, s->file); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_block_status_above(bs, base, offset, bytes, pnum, map, file); ++ } else { ++ BdrvBlockStatusAbove s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .base = base, ++ .offset = offset, ++ .bytes = bytes, ++ .pnum = pnum, ++ .map = map, ++ .file = file, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_block_status_above_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_is_allocated ++ */ ++ ++typedef struct BdrvIsAllocated { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ int64_t offset; ++ int64_t bytes; ++ int64_t *pnum; ++} BdrvIsAllocated; ++ ++static void coroutine_fn bdrv_co_is_allocated_entry(void *opaque) ++{ ++ BdrvIsAllocated *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_is_allocated(s->bs, s->offset, s->bytes, s->pnum); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_is_allocated(bs, offset, bytes, pnum); ++ } else { ++ BdrvIsAllocated s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .offset = offset, ++ .bytes = bytes, ++ .pnum = pnum, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_is_allocated_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_is_allocated_above ++ */ ++ ++typedef struct BdrvIsAllocatedAbove { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ BlockDriverState *base; ++ bool include_base; ++ int64_t offset; ++ int64_t bytes; ++ int64_t *pnum; ++} BdrvIsAllocatedAbove; ++ ++static void coroutine_fn bdrv_co_is_allocated_above_entry(void *opaque) ++{ ++ BdrvIsAllocatedAbove *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_is_allocated_above(s->bs, s->base, s->include_base, s->offset, s->bytes, s->pnum); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_is_allocated_above(BlockDriverState *bs, BlockDriverState *base, bool include_base, int64_t offset, int64_t bytes, int64_t *pnum) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_is_allocated_above(bs, base, include_base, offset, bytes, pnum); ++ } else { ++ BdrvIsAllocatedAbove s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .base = base, ++ .include_base = include_base, ++ .offset = offset, ++ .bytes = bytes, ++ .pnum = pnum, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_is_allocated_above_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_is_inserted ++ */ ++ ++typedef struct BdrvIsInserted { ++ BdrvPollCo poll_state; ++ bool ret; ++ BlockDriverState *bs; ++} BdrvIsInserted; ++ ++static void coroutine_fn bdrv_co_is_inserted_entry(void *opaque) ++{ ++ BdrvIsInserted *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_is_inserted(s->bs); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++bool bdrv_is_inserted(BlockDriverState *bs) ++{ ++ BdrvIsInserted s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_is_inserted_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_get_info ++ */ ++ ++typedef struct BdrvGetInfo { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ BlockDriverInfo *bdi; ++} BdrvGetInfo; ++ ++static void coroutine_fn bdrv_co_get_info_entry(void *opaque) ++{ ++ BdrvGetInfo *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_get_info(s->bs, s->bdi); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_get_info(bs, bdi); ++ } else { ++ BdrvGetInfo s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .bdi = bdi, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_get_info_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_change_backing_file ++ */ ++ ++typedef struct BdrvChangeBackingFile { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ const char *backing_file; ++ const char *backing_fmt; ++ bool warn; ++} BdrvChangeBackingFile; ++ ++static void coroutine_fn bdrv_co_change_backing_file_entry(void *opaque) ++{ ++ BdrvChangeBackingFile *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_change_backing_file(s->bs, s->backing_file, s->backing_fmt, s->warn); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, const char *backing_fmt, bool warn) ++{ ++ BdrvChangeBackingFile s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .backing_file = backing_file, ++ .backing_fmt = backing_fmt, ++ .warn = warn, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_change_backing_file_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_debug_event ++ */ ++ ++typedef struct BdrvDebugEvent { ++ BdrvPollCo poll_state; ++ ++ BlockDriverState *bs; ++ BlkdebugEvent event; ++} BdrvDebugEvent; ++ ++static void coroutine_fn bdrv_co_debug_event_entry(void *opaque) ++{ ++ BdrvDebugEvent *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ bdrv_co_debug_event(s->bs, s->event); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ bdrv_co_debug_event(bs, event); ++ } else { ++ BdrvDebugEvent s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .event = event, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_debug_event_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_can_store_new_dirty_bitmap ++ */ ++ ++typedef struct BdrvCanStoreNewDirtyBitmap { ++ BdrvPollCo poll_state; ++ bool ret; ++ BlockDriverState *bs; ++ const char *name; ++ uint32_t granularity; ++ Error **errp; ++} BdrvCanStoreNewDirtyBitmap; ++ ++static void coroutine_fn bdrv_co_can_store_new_dirty_bitmap_entry(void *opaque) ++{ ++ BdrvCanStoreNewDirtyBitmap *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_can_store_new_dirty_bitmap(s->bs, s->name, s->granularity, s->errp); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, uint32_t granularity, Error **errp) ++{ ++ BdrvCanStoreNewDirtyBitmap s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .name = name, ++ .granularity = granularity, ++ .errp = errp, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_can_store_new_dirty_bitmap_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_truncate ++ */ ++ ++typedef struct BdrvTruncate { ++ BdrvPollCo poll_state; ++ int ret; ++ BdrvChild *child; ++ int64_t offset; ++ bool exact; ++ PreallocMode prealloc; ++ BdrvRequestFlags flags; ++ Error **errp; ++} BdrvTruncate; ++ ++static void coroutine_fn bdrv_co_truncate_entry(void *opaque) ++{ ++ BdrvTruncate *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_truncate(s->child, s->offset, s->exact, s->prealloc, s->flags, s->errp); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_truncate(BdrvChild *child, int64_t offset, bool exact, PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_truncate(child, offset, exact, prealloc, flags, errp); ++ } else { ++ BdrvTruncate s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .child = child, ++ .offset = offset, ++ .exact = exact, ++ .prealloc = prealloc, ++ .flags = flags, ++ .errp = errp, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_truncate_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_check ++ */ ++ ++typedef struct BdrvCheck { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ BdrvCheckResult *res; ++ BdrvCheckMode fix; ++} BdrvCheck; ++ ++static void coroutine_fn bdrv_co_check_entry(void *opaque) ++{ ++ BdrvCheck *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_check(s->bs, s->res, s->fix); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_check(bs, res, fix); ++ } else { ++ BdrvCheck s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .res = res, ++ .fix = fix, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_check_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_invalidate_cache ++ */ ++ ++typedef struct BdrvInvalidateCache { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ Error **errp; ++} BdrvInvalidateCache; ++ ++static void coroutine_fn bdrv_co_invalidate_cache_entry(void *opaque) ++{ ++ BdrvInvalidateCache *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_invalidate_cache(s->bs, s->errp); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_invalidate_cache(bs, errp); ++ } else { ++ BdrvInvalidateCache s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .errp = errp, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_invalidate_cache_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_flush ++ */ ++ ++typedef struct BdrvFlush { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++} BdrvFlush; ++ ++static void coroutine_fn bdrv_co_flush_entry(void *opaque) ++{ ++ BdrvFlush *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_flush(s->bs); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_flush(BlockDriverState *bs) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_flush(bs); ++ } else { ++ BdrvFlush s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_flush_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_pdiscard ++ */ ++ ++typedef struct BdrvPdiscard { ++ BdrvPollCo poll_state; ++ int ret; ++ BdrvChild *child; ++ int64_t offset; ++ int64_t bytes; ++} BdrvPdiscard; ++ ++static void coroutine_fn bdrv_co_pdiscard_entry(void *opaque) ++{ ++ BdrvPdiscard *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_pdiscard(s->child, s->offset, s->bytes); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_pdiscard(child, offset, bytes); ++ } else { ++ BdrvPdiscard s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .child = child, ++ .offset = offset, ++ .bytes = bytes, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_pdiscard_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_readv_vmstate ++ */ ++ ++typedef struct BdrvReadvVmstate { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ QEMUIOVector *qiov; ++ int64_t pos; ++} BdrvReadvVmstate; ++ ++static void coroutine_fn bdrv_co_readv_vmstate_entry(void *opaque) ++{ ++ BdrvReadvVmstate *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_readv_vmstate(s->bs, s->qiov, s->pos); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_readv_vmstate(bs, qiov, pos); ++ } else { ++ BdrvReadvVmstate s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .qiov = qiov, ++ .pos = pos, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_readv_vmstate_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_writev_vmstate ++ */ ++ ++typedef struct BdrvWritevVmstate { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ QEMUIOVector *qiov; ++ int64_t pos; ++} BdrvWritevVmstate; ++ ++static void coroutine_fn bdrv_co_writev_vmstate_entry(void *opaque) ++{ ++ BdrvWritevVmstate *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_writev_vmstate(s->bs, s->qiov, s->pos); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_writev_vmstate(bs, qiov, pos); ++ } else { ++ BdrvWritevVmstate s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .qiov = qiov, ++ .pos = pos, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_writev_vmstate_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++ ++/* ++ * Wrappers for bdrv_co_remove_persistent_dirty_bitmap ++ */ ++ ++typedef struct BdrvRemovePersistentDirtyBitmap { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ const char *name; ++ Error **errp; ++} BdrvRemovePersistentDirtyBitmap; ++ ++static void coroutine_fn bdrv_co_remove_persistent_dirty_bitmap_entry(void *opaque) ++{ ++ BdrvRemovePersistentDirtyBitmap *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_remove_persistent_dirty_bitmap(s->bs, s->name, s->errp); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, Error **errp) ++{ ++ BdrvRemovePersistentDirtyBitmap s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .name = name, ++ .errp = errp, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_remove_persistent_dirty_bitmap_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++} ++ ++ ++ ++/* ++ * Wrappers for bdrv_co_refresh_total_sectors ++ */ ++ ++typedef struct BdrvRefreshTotalSectors { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ int64_t hint; ++} BdrvRefreshTotalSectors; ++ ++static void coroutine_fn bdrv_co_refresh_total_sectors_entry(void *opaque) ++{ ++ BdrvRefreshTotalSectors *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_refresh_total_sectors(s->bs, s->hint); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_refresh_total_sectors(bs, hint); ++ } else { ++ BdrvRefreshTotalSectors s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .hint = hint, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_refresh_total_sectors_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++ ++/* ++ * Wrappers for bdrv_co_create ++ */ ++ ++typedef struct BdrvCreate { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriver *drv; ++ const char *filename; ++ QemuOpts *opts; ++ Error **errp; ++} BdrvCreate; ++ ++static void coroutine_fn bdrv_co_create_entry(void *opaque) ++{ ++ BdrvCreate *s = opaque; ++ ++ ++ s->ret = bdrv_co_create(s->drv, s->filename, s->opts, s->errp); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_create(BlockDriver *drv, const char *filename, QemuOpts *opts, Error **errp) ++{ ++ BdrvCreate s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .drv = drv, ++ .filename = filename, ++ .opts = opts, ++ .errp = errp, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_create_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_open_child ++ */ ++ ++typedef struct BdrvCoOpenChild { ++ Coroutine *co; ++ BdrvChild * ret; ++ const char *filename; ++ QDict *options; ++ const char *bdref_key; ++ BlockDriverState *parent; ++ const BdrvChildClass *child_class; ++ BdrvChildRole child_role; ++ bool allow_none; ++ Error **errp; ++} BdrvCoOpenChild; ++ ++static void bdrv_open_child_bh(void *opaque) ++{ ++ BdrvCoOpenChild *s = opaque; ++ ++ ++ s->ret = bdrv_open_child(s->filename, s->options, s->bdref_key, s->parent, s->child_class, s->child_role, s->allow_none, s->errp); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++BdrvChild * coroutine_fn bdrv_co_open_child(const char *filename, QDict *options, const char *bdref_key, BlockDriverState *parent, const BdrvChildClass *child_class, BdrvChildRole child_role, bool allow_none, Error **errp) ++{ ++ BdrvCoOpenChild s = { ++ .co = qemu_coroutine_self(), ++ .filename = filename, ++ .options = options, ++ .bdref_key = bdref_key, ++ .parent = parent, ++ .child_class = child_class, ++ .child_role = child_role, ++ .allow_none = allow_none, ++ .errp = errp, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_open_child_bh, &s); ++ qemu_coroutine_yield(); ++ ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_open_blockdev_ref ++ */ ++ ++typedef struct BdrvCoOpenBlockdevRef { ++ Coroutine *co; ++ BlockDriverState * ret; ++ BlockdevRef *ref; ++ Error **errp; ++} BdrvCoOpenBlockdevRef; ++ ++static void bdrv_open_blockdev_ref_bh(void *opaque) ++{ ++ BdrvCoOpenBlockdevRef *s = opaque; ++ ++ ++ s->ret = bdrv_open_blockdev_ref(s->ref, s->errp); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++BlockDriverState * coroutine_fn bdrv_co_open_blockdev_ref(BlockdevRef *ref, Error **errp) ++{ ++ BdrvCoOpenBlockdevRef s = { ++ .co = qemu_coroutine_self(), ++ .ref = ref, ++ .errp = errp, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_open_blockdev_ref_bh, &s); ++ qemu_coroutine_yield(); ++ ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_open ++ */ ++ ++typedef struct BdrvCoOpen { ++ Coroutine *co; ++ BlockDriverState * ret; ++ const char *filename; ++ const char *reference; ++ QDict *options; ++ int flags; ++ Error **errp; ++} BdrvCoOpen; ++ ++static void bdrv_open_bh(void *opaque) ++{ ++ BdrvCoOpen *s = opaque; ++ ++ ++ s->ret = bdrv_open(s->filename, s->reference, s->options, s->flags, s->errp); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++BlockDriverState * coroutine_fn bdrv_co_open(const char *filename, const char *reference, QDict *options, int flags, Error **errp) ++{ ++ BdrvCoOpen s = { ++ .co = qemu_coroutine_self(), ++ .filename = filename, ++ .reference = reference, ++ .options = options, ++ .flags = flags, ++ .errp = errp, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_open_bh, &s); ++ qemu_coroutine_yield(); ++ ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_activate ++ */ ++ ++typedef struct BdrvCoActivate { ++ Coroutine *co; ++ int ret; ++ BlockDriverState *bs; ++ Error **errp; ++} BdrvCoActivate; ++ ++static void bdrv_activate_bh(void *opaque) ++{ ++ BdrvCoActivate *s = opaque; ++ ++ bdrv_graph_rdlock_main_loop(); ++ s->ret = bdrv_activate(s->bs, s->errp); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ aio_co_wake(s->co); ++} ++ ++int coroutine_fn bdrv_co_activate(BlockDriverState *bs, Error **errp) ++{ ++ BdrvCoActivate s = { ++ .co = qemu_coroutine_self(), ++ .bs = bs, ++ .errp = errp, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_activate_bh, &s); ++ qemu_coroutine_yield(); ++ ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_unref ++ */ ++ ++typedef struct BdrvCoUnref { ++ Coroutine *co; ++ ++ BlockDriverState *bs; ++} BdrvCoUnref; ++ ++static void bdrv_unref_bh(void *opaque) ++{ ++ BdrvCoUnref *s = opaque; ++ ++ ++ bdrv_unref(s->bs); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++void coroutine_fn bdrv_co_unref(BlockDriverState *bs) ++{ ++ BdrvCoUnref s = { ++ .co = qemu_coroutine_self(), ++ .bs = bs, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_unref_bh, &s); ++ qemu_coroutine_yield(); ++ ++ ++} ++ ++ ++/* ++ * Wrappers for bdrv_unref_child ++ */ ++ ++typedef struct BdrvCoUnrefChild { ++ Coroutine *co; ++ ++ BlockDriverState *parent; ++ BdrvChild *child; ++} BdrvCoUnrefChild; ++ ++static void bdrv_unref_child_bh(void *opaque) ++{ ++ BdrvCoUnrefChild *s = opaque; ++ ++ bdrv_graph_wrlock(); ++ bdrv_unref_child(s->parent, s->child); ++ bdrv_graph_wrunlock(); ++ ++ aio_co_wake(s->co); ++} ++ ++void coroutine_fn bdrv_co_unref_child(BlockDriverState *parent, BdrvChild *child) ++{ ++ BdrvCoUnrefChild s = { ++ .co = qemu_coroutine_self(), ++ .parent = parent, ++ .child = child, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_unref_child_bh, &s); ++ qemu_coroutine_yield(); ++ ++ ++} ++ ++ ++ ++/* ++ * Wrappers for blk_new_with_bs ++ */ ++ ++typedef struct BlkCoNewWithBs { ++ Coroutine *co; ++ BlockBackend * ret; ++ BlockDriverState *bs; ++ uint64_t perm; ++ uint64_t shared_perm; ++ Error **errp; ++} BlkCoNewWithBs; ++ ++static void blk_new_with_bs_bh(void *opaque) ++{ ++ BlkCoNewWithBs *s = opaque; ++ ++ ++ s->ret = blk_new_with_bs(s->bs, s->perm, s->shared_perm, s->errp); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++BlockBackend * coroutine_fn blk_co_new_with_bs(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, Error **errp) ++{ ++ BlkCoNewWithBs s = { ++ .co = qemu_coroutine_self(), ++ .bs = bs, ++ .perm = perm, ++ .shared_perm = shared_perm, ++ .errp = errp, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), blk_new_with_bs_bh, &s); ++ qemu_coroutine_yield(); ++ ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for blk_new_open ++ */ ++ ++typedef struct BlkCoNewOpen { ++ Coroutine *co; ++ BlockBackend * ret; ++ const char *filename; ++ const char *reference; ++ QDict *options; ++ int flags; ++ Error **errp; ++} BlkCoNewOpen; ++ ++static void blk_new_open_bh(void *opaque) ++{ ++ BlkCoNewOpen *s = opaque; ++ ++ ++ s->ret = blk_new_open(s->filename, s->reference, s->options, s->flags, s->errp); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++BlockBackend * coroutine_fn blk_co_new_open(const char *filename, const char *reference, QDict *options, int flags, Error **errp) ++{ ++ BlkCoNewOpen s = { ++ .co = qemu_coroutine_self(), ++ .filename = filename, ++ .reference = reference, ++ .options = options, ++ .flags = flags, ++ .errp = errp, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), blk_new_open_bh, &s); ++ qemu_coroutine_yield(); ++ ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for blk_unref ++ */ ++ ++typedef struct BlkCoUnref { ++ Coroutine *co; ++ ++ BlockBackend *blk; ++} BlkCoUnref; ++ ++static void blk_unref_bh(void *opaque) ++{ ++ BlkCoUnref *s = opaque; ++ ++ ++ blk_unref(s->blk); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++void coroutine_fn blk_co_unref(BlockBackend *blk) ++{ ++ BlkCoUnref s = { ++ .co = qemu_coroutine_self(), ++ .blk = blk, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), blk_unref_bh, &s); ++ qemu_coroutine_yield(); ++ ++ ++} ++ ++ ++ ++/* ++ * Wrappers for blk_co_is_inserted ++ */ ++ ++typedef struct BlkIsInserted { ++ BdrvPollCo poll_state; ++ bool ret; ++ BlockBackend *blk; ++} BlkIsInserted; ++ ++static void coroutine_fn blk_co_is_inserted_entry(void *opaque) ++{ ++ BlkIsInserted *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = blk_co_is_inserted(s->blk); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++bool blk_is_inserted(BlockBackend *blk) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return blk_co_is_inserted(blk); ++ } else { ++ BlkIsInserted s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_is_inserted_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_is_available ++ */ ++ ++typedef struct BlkIsAvailable { ++ BdrvPollCo poll_state; ++ bool ret; ++ BlockBackend *blk; ++} BlkIsAvailable; ++ ++static void coroutine_fn blk_co_is_available_entry(void *opaque) ++{ ++ BlkIsAvailable *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = blk_co_is_available(s->blk); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++bool blk_is_available(BlockBackend *blk) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return blk_co_is_available(blk); ++ } else { ++ BlkIsAvailable s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_is_available_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_lock_medium ++ */ ++ ++typedef struct BlkLockMedium { ++ BdrvPollCo poll_state; ++ ++ BlockBackend *blk; ++ bool locked; ++} BlkLockMedium; ++ ++static void coroutine_fn blk_co_lock_medium_entry(void *opaque) ++{ ++ BlkLockMedium *s = opaque; ++ ++ ++ blk_co_lock_medium(s->blk, s->locked); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++void blk_lock_medium(BlockBackend *blk, bool locked) ++{ ++ BlkLockMedium s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .locked = locked, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_lock_medium_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ ++} ++ ++ ++/* ++ * Wrappers for blk_co_eject ++ */ ++ ++typedef struct BlkEject { ++ BdrvPollCo poll_state; ++ ++ BlockBackend *blk; ++ bool eject_flag; ++} BlkEject; ++ ++static void coroutine_fn blk_co_eject_entry(void *opaque) ++{ ++ BlkEject *s = opaque; ++ ++ ++ blk_co_eject(s->blk, s->eject_flag); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++void blk_eject(BlockBackend *blk, bool eject_flag) ++{ ++ BlkEject s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .eject_flag = eject_flag, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_eject_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ ++} ++ ++ ++/* ++ * Wrappers for blk_co_getlength ++ */ ++ ++typedef struct BlkGetlength { ++ BdrvPollCo poll_state; ++ int64_t ret; ++ BlockBackend *blk; ++} BlkGetlength; ++ ++static void coroutine_fn blk_co_getlength_entry(void *opaque) ++{ ++ BlkGetlength *s = opaque; ++ ++ ++ s->ret = blk_co_getlength(s->blk); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int64_t blk_getlength(BlockBackend *blk) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_getlength(blk); ++ } else { ++ BlkGetlength s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_getlength_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pread ++ */ ++ ++typedef struct BlkPread { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ void *buf; ++ BdrvRequestFlags flags; ++} BlkPread; ++ ++static void coroutine_fn blk_co_pread_entry(void *opaque) ++{ ++ BlkPread *s = opaque; ++ ++ ++ s->ret = blk_co_pread(s->blk, s->offset, s->bytes, s->buf, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pread(BlockBackend *blk, int64_t offset, int64_t bytes, void *buf, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pread(blk, offset, bytes, buf, flags); ++ } else { ++ BlkPread s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .buf = buf, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pread_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_preadv ++ */ ++ ++typedef struct BlkPreadv { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ QEMUIOVector *qiov; ++ BdrvRequestFlags flags; ++} BlkPreadv; ++ ++static void coroutine_fn blk_co_preadv_entry(void *opaque) ++{ ++ BlkPreadv *s = opaque; ++ ++ ++ s->ret = blk_co_preadv(s->blk, s->offset, s->bytes, s->qiov, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_preadv(blk, offset, bytes, qiov, flags); ++ } else { ++ BlkPreadv s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .qiov = qiov, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_preadv_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_preadv_part ++ */ ++ ++typedef struct BlkPreadvPart { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ QEMUIOVector *qiov; ++ size_t qiov_offset; ++ BdrvRequestFlags flags; ++} BlkPreadvPart; ++ ++static void coroutine_fn blk_co_preadv_part_entry(void *opaque) ++{ ++ BlkPreadvPart *s = opaque; ++ ++ ++ s->ret = blk_co_preadv_part(s->blk, s->offset, s->bytes, s->qiov, s->qiov_offset, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_preadv_part(blk, offset, bytes, qiov, qiov_offset, flags); ++ } else { ++ BlkPreadvPart s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .qiov = qiov, ++ .qiov_offset = qiov_offset, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_preadv_part_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pwrite ++ */ ++ ++typedef struct BlkPwrite { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ const void *buf; ++ BdrvRequestFlags flags; ++} BlkPwrite; ++ ++static void coroutine_fn blk_co_pwrite_entry(void *opaque) ++{ ++ BlkPwrite *s = opaque; ++ ++ ++ s->ret = blk_co_pwrite(s->blk, s->offset, s->bytes, s->buf, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pwrite(BlockBackend *blk, int64_t offset, int64_t bytes, const void *buf, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pwrite(blk, offset, bytes, buf, flags); ++ } else { ++ BlkPwrite s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .buf = buf, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pwrite_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pwritev ++ */ ++ ++typedef struct BlkPwritev { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ QEMUIOVector *qiov; ++ BdrvRequestFlags flags; ++} BlkPwritev; ++ ++static void coroutine_fn blk_co_pwritev_entry(void *opaque) ++{ ++ BlkPwritev *s = opaque; ++ ++ ++ s->ret = blk_co_pwritev(s->blk, s->offset, s->bytes, s->qiov, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pwritev(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pwritev(blk, offset, bytes, qiov, flags); ++ } else { ++ BlkPwritev s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .qiov = qiov, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pwritev_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pwritev_part ++ */ ++ ++typedef struct BlkPwritevPart { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ QEMUIOVector *qiov; ++ size_t qiov_offset; ++ BdrvRequestFlags flags; ++} BlkPwritevPart; ++ ++static void coroutine_fn blk_co_pwritev_part_entry(void *opaque) ++{ ++ BlkPwritevPart *s = opaque; ++ ++ ++ s->ret = blk_co_pwritev_part(s->blk, s->offset, s->bytes, s->qiov, s->qiov_offset, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pwritev_part(blk, offset, bytes, qiov, qiov_offset, flags); ++ } else { ++ BlkPwritevPart s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .qiov = qiov, ++ .qiov_offset = qiov_offset, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pwritev_part_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pwrite_compressed ++ */ ++ ++typedef struct BlkPwriteCompressed { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ const void *buf; ++} BlkPwriteCompressed; ++ ++static void coroutine_fn blk_co_pwrite_compressed_entry(void *opaque) ++{ ++ BlkPwriteCompressed *s = opaque; ++ ++ ++ s->ret = blk_co_pwrite_compressed(s->blk, s->offset, s->bytes, s->buf); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, int64_t bytes, const void *buf) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pwrite_compressed(blk, offset, bytes, buf); ++ } else { ++ BlkPwriteCompressed s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .buf = buf, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pwrite_compressed_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pwrite_zeroes ++ */ ++ ++typedef struct BlkPwriteZeroes { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ BdrvRequestFlags flags; ++} BlkPwriteZeroes; ++ ++static void coroutine_fn blk_co_pwrite_zeroes_entry(void *opaque) ++{ ++ BlkPwriteZeroes *s = opaque; ++ ++ ++ s->ret = blk_co_pwrite_zeroes(s->blk, s->offset, s->bytes, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, int64_t bytes, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pwrite_zeroes(blk, offset, bytes, flags); ++ } else { ++ BlkPwriteZeroes s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pwrite_zeroes_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_zone_report ++ */ ++ ++typedef struct BlkZoneReport { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ unsigned int *nr_zones; ++ BlockZoneDescriptor *zones; ++} BlkZoneReport; ++ ++static void coroutine_fn blk_co_zone_report_entry(void *opaque) ++{ ++ BlkZoneReport *s = opaque; ++ ++ ++ s->ret = blk_co_zone_report(s->blk, s->offset, s->nr_zones, s->zones); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_zone_report(BlockBackend *blk, int64_t offset, unsigned int *nr_zones, BlockZoneDescriptor *zones) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_zone_report(blk, offset, nr_zones, zones); ++ } else { ++ BlkZoneReport s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .nr_zones = nr_zones, ++ .zones = zones, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_zone_report_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_zone_mgmt ++ */ ++ ++typedef struct BlkZoneMgmt { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ BlockZoneOp op; ++ int64_t offset; ++ int64_t len; ++} BlkZoneMgmt; ++ ++static void coroutine_fn blk_co_zone_mgmt_entry(void *opaque) ++{ ++ BlkZoneMgmt *s = opaque; ++ ++ ++ s->ret = blk_co_zone_mgmt(s->blk, s->op, s->offset, s->len); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_zone_mgmt(BlockBackend *blk, BlockZoneOp op, int64_t offset, int64_t len) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_zone_mgmt(blk, op, offset, len); ++ } else { ++ BlkZoneMgmt s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .op = op, ++ .offset = offset, ++ .len = len, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_zone_mgmt_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_zone_append ++ */ ++ ++typedef struct BlkZoneAppend { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t *offset; ++ QEMUIOVector *qiov; ++ BdrvRequestFlags flags; ++} BlkZoneAppend; ++ ++static void coroutine_fn blk_co_zone_append_entry(void *opaque) ++{ ++ BlkZoneAppend *s = opaque; ++ ++ ++ s->ret = blk_co_zone_append(s->blk, s->offset, s->qiov, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_zone_append(BlockBackend *blk, int64_t *offset, QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_zone_append(blk, offset, qiov, flags); ++ } else { ++ BlkZoneAppend s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .qiov = qiov, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_zone_append_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pdiscard ++ */ ++ ++typedef struct BlkPdiscard { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++} BlkPdiscard; ++ ++static void coroutine_fn blk_co_pdiscard_entry(void *opaque) ++{ ++ BlkPdiscard *s = opaque; ++ ++ ++ s->ret = blk_co_pdiscard(s->blk, s->offset, s->bytes); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pdiscard(blk, offset, bytes); ++ } else { ++ BlkPdiscard s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pdiscard_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_flush ++ */ ++ ++typedef struct BlkFlush { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++} BlkFlush; ++ ++static void coroutine_fn blk_co_flush_entry(void *opaque) ++{ ++ BlkFlush *s = opaque; ++ ++ ++ s->ret = blk_co_flush(s->blk); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_flush(BlockBackend *blk) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_flush(blk); ++ } else { ++ BlkFlush s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_flush_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_ioctl ++ */ ++ ++typedef struct BlkIoctl { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ unsigned long int req; ++ void *buf; ++} BlkIoctl; ++ ++static void coroutine_fn blk_co_ioctl_entry(void *opaque) ++{ ++ BlkIoctl *s = opaque; ++ ++ ++ s->ret = blk_co_ioctl(s->blk, s->req, s->buf); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_ioctl(blk, req, buf); ++ } else { ++ BlkIoctl s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .req = req, ++ .buf = buf, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_ioctl_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_truncate ++ */ ++ ++typedef struct BlkTruncate { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ bool exact; ++ PreallocMode prealloc; ++ BdrvRequestFlags flags; ++ Error **errp; ++} BlkTruncate; ++ ++static void coroutine_fn blk_co_truncate_entry(void *opaque) ++{ ++ BlkTruncate *s = opaque; ++ ++ ++ s->ret = blk_co_truncate(s->blk, s->offset, s->exact, s->prealloc, s->flags, s->errp); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_truncate(blk, offset, exact, prealloc, flags, errp); ++ } else { ++ BlkTruncate s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .exact = exact, ++ .prealloc = prealloc, ++ .flags = flags, ++ .errp = errp, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_truncate_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++ ++/* ++ * Wrappers for bdrv_co_common_block_status_above ++ */ ++ ++typedef struct BdrvCommonBlockStatusAbove { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ BlockDriverState *base; ++ bool include_base; ++ bool want_zero; ++ int64_t offset; ++ int64_t bytes; ++ int64_t *pnum; ++ int64_t *map; ++ BlockDriverState **file; ++ int *depth; ++} BdrvCommonBlockStatusAbove; ++ ++static void coroutine_fn bdrv_co_common_block_status_above_entry(void *opaque) ++{ ++ BdrvCommonBlockStatusAbove *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_common_block_status_above(s->bs, s->base, s->include_base, s->want_zero, s->offset, s->bytes, s->pnum, s->map, s->file, s->depth); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, bool include_base, bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file, int *depth) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_common_block_status_above(bs, base, include_base, want_zero, offset, bytes, pnum, map, file, depth); ++ } else { ++ BdrvCommonBlockStatusAbove s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .base = base, ++ .include_base = include_base, ++ .want_zero = want_zero, ++ .offset = offset, ++ .bytes = bytes, ++ .pnum = pnum, ++ .map = map, ++ .file = file, ++ .depth = depth, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_common_block_status_above_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for nbd_co_do_establish_connection ++ */ ++ ++typedef struct NbdDoEstablishConnection { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ bool blocking; ++ Error **errp; ++} NbdDoEstablishConnection; ++ ++static void coroutine_fn nbd_co_do_establish_connection_entry(void *opaque) ++{ ++ NbdDoEstablishConnection *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = nbd_co_do_establish_connection(s->bs, s->blocking, s->errp); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int nbd_do_establish_connection(BlockDriverState *bs, bool blocking, Error **errp) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return nbd_co_do_establish_connection(bs, blocking, errp); ++ } else { ++ NbdDoEstablishConnection s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .blocking = blocking, ++ .errp = errp, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(nbd_co_do_establish_connection_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} +diff --git a/qcow2/lib/block/block-gen.h b/qcow2/lib/block/block-gen.h +new file mode 100644 +index 00000000..89b7daaa +--- /dev/null ++++ b/qcow2/lib/block/block-gen.h +@@ -0,0 +1,46 @@ ++/* ++ * Block coroutine wrapping core, used by auto-generated block/block-gen.c ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * Copyright (c) 2020 Virtuozzo International GmbH ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCK_BLOCK_GEN_H ++#define BLOCK_BLOCK_GEN_H ++ ++#include "block/block_int.h" ++ ++/* Base structure for argument packing structures */ ++typedef struct BdrvPollCo { ++ AioContext *ctx; ++ bool in_progress; ++ Coroutine *co; /* Keep pointer here for debugging */ ++} BdrvPollCo; ++ ++static inline void bdrv_poll_co(BdrvPollCo *s) ++{ ++ assert(!qemu_in_coroutine()); ++ ++ aio_co_enter(s->ctx, s->co); ++ AIO_WAIT_WHILE(s->ctx, s->in_progress); ++} ++ ++#endif /* BLOCK_BLOCK_GEN_H */ +diff --git a/qcow2/lib/block/commit.c b/qcow2/lib/block/commit.c +new file mode 100644 +index 00000000..7c3fdcb0 +--- /dev/null ++++ b/qcow2/lib/block/commit.c +@@ -0,0 +1,600 @@ ++/* ++ * Live block commit ++ * ++ * Copyright Red Hat, Inc. 2012 ++ * ++ * Authors: ++ * Jeff Cody ++ * Based on stream.c by Stefan Hajnoczi ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++#include "trace.h" ++#include "block/block_int.h" ++#include "block/blockjob_int.h" ++#include "qapi/error.h" ++#include "qemu/ratelimit.h" ++#include "qemu/memalign.h" ++#include "sysemu/block-backend.h" ++ ++enum { ++ /* ++ * Size of data buffer for populating the image file. This should be large ++ * enough to process multiple clusters in a single call, so that populating ++ * contiguous regions of the image is efficient. ++ */ ++ COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ ++}; ++ ++typedef struct CommitBlockJob { ++ BlockJob common; ++ BlockDriverState *commit_top_bs; ++ BlockBackend *top; ++ BlockBackend *base; ++ BlockDriverState *base_bs; ++ BlockDriverState *base_overlay; ++ BlockdevOnError on_error; ++ bool base_read_only; ++ bool chain_frozen; ++ char *backing_file_str; ++ bool backing_mask_protocol; ++} CommitBlockJob; ++ ++static int commit_prepare(Job *job) ++{ ++ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); ++ ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); ++ s->chain_frozen = false; ++ bdrv_graph_rdunlock_main_loop(); ++ ++ /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before ++ * the normal backing chain can be restored. */ ++ blk_unref(s->base); ++ s->base = NULL; ++ ++ /* FIXME: bdrv_drop_intermediate treats total failures and partial failures ++ * identically. Further work is needed to disambiguate these cases. */ ++ return bdrv_drop_intermediate(s->commit_top_bs, s->base_bs, ++ s->backing_file_str, ++ s->backing_mask_protocol); ++} ++ ++static void commit_abort(Job *job) ++{ ++ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); ++ BlockDriverState *top_bs = blk_bs(s->top); ++ BlockDriverState *commit_top_backing_bs; ++ ++ if (s->chain_frozen) { ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); ++ bdrv_graph_rdunlock_main_loop(); ++ } ++ ++ /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ ++ bdrv_ref(top_bs); ++ bdrv_ref(s->commit_top_bs); ++ ++ if (s->base) { ++ blk_unref(s->base); ++ } ++ ++ /* free the blockers on the intermediate nodes so that bdrv_replace_nodes ++ * can succeed */ ++ block_job_remove_all_bdrv(&s->common); ++ ++ /* If bdrv_drop_intermediate() failed (or was not invoked), remove the ++ * commit filter driver from the backing chain now. Do this as the final ++ * step so that the 'consistent read' permission can be granted. ++ * ++ * XXX Can (or should) we somehow keep 'consistent read' blocked even ++ * after the failed/cancelled commit job is gone? If we already wrote ++ * something to base, the intermediate images aren't valid any more. */ ++ bdrv_graph_rdlock_main_loop(); ++ commit_top_backing_bs = s->commit_top_bs->backing->bs; ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_drained_begin(commit_top_backing_bs); ++ bdrv_graph_wrlock(); ++ bdrv_replace_node(s->commit_top_bs, commit_top_backing_bs, &error_abort); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(commit_top_backing_bs); ++ ++ bdrv_unref(s->commit_top_bs); ++ bdrv_unref(top_bs); ++} ++ ++static void commit_clean(Job *job) ++{ ++ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); ++ ++ /* restore base open flags here if appropriate (e.g., change the base back ++ * to r/o). These reopens do not need to be atomic, since we won't abort ++ * even on failure here */ ++ if (s->base_read_only) { ++ bdrv_reopen_set_read_only(s->base_bs, true, NULL); ++ } ++ ++ g_free(s->backing_file_str); ++ blk_unref(s->top); ++} ++ ++static int coroutine_fn commit_run(Job *job, Error **errp) ++{ ++ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); ++ int64_t offset; ++ int ret = 0; ++ int64_t n = 0; /* bytes */ ++ QEMU_AUTO_VFREE void *buf = NULL; ++ int64_t len, base_len; ++ ++ len = blk_co_getlength(s->top); ++ if (len < 0) { ++ return len; ++ } ++ job_progress_set_remaining(&s->common.job, len); ++ ++ base_len = blk_co_getlength(s->base); ++ if (base_len < 0) { ++ return base_len; ++ } ++ ++ if (base_len < len) { ++ ret = blk_co_truncate(s->base, len, false, PREALLOC_MODE_OFF, 0, NULL); ++ if (ret) { ++ return ret; ++ } ++ } ++ ++ buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); ++ ++ for (offset = 0; offset < len; offset += n) { ++ bool copy; ++ bool error_in_source = true; ++ ++ /* Note that even when no rate limit is applied we need to yield ++ * with no pending I/O here so that bdrv_drain_all() returns. ++ */ ++ block_job_ratelimit_sleep(&s->common); ++ if (job_is_cancelled(&s->common.job)) { ++ break; ++ } ++ /* Copy if allocated above the base */ ++ ret = blk_co_is_allocated_above(s->top, s->base_overlay, true, ++ offset, COMMIT_BUFFER_SIZE, &n); ++ copy = (ret > 0); ++ trace_commit_one_iteration(s, offset, n, ret); ++ if (copy) { ++ assert(n < SIZE_MAX); ++ ++ ret = blk_co_pread(s->top, offset, n, buf, 0); ++ if (ret >= 0) { ++ ret = blk_co_pwrite(s->base, offset, n, buf, 0); ++ if (ret < 0) { ++ error_in_source = false; ++ } ++ } ++ } ++ if (ret < 0) { ++ BlockErrorAction action = ++ block_job_error_action(&s->common, s->on_error, ++ error_in_source, -ret); ++ if (action == BLOCK_ERROR_ACTION_REPORT) { ++ return ret; ++ } else { ++ n = 0; ++ continue; ++ } ++ } ++ /* Publish progress */ ++ job_progress_update(&s->common.job, n); ++ ++ if (copy) { ++ block_job_ratelimit_processed_bytes(&s->common, n); ++ } ++ } ++ ++ return 0; ++} ++ ++static const BlockJobDriver commit_job_driver = { ++ .job_driver = { ++ .instance_size = sizeof(CommitBlockJob), ++ .job_type = JOB_TYPE_COMMIT, ++ .free = block_job_free, ++ .user_resume = block_job_user_resume, ++ .run = commit_run, ++ .prepare = commit_prepare, ++ .abort = commit_abort, ++ .clean = commit_clean ++ }, ++}; ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_commit_top_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); ++} ++ ++static GRAPH_RDLOCK void bdrv_commit_top_refresh_filename(BlockDriverState *bs) ++{ ++ pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), ++ bs->backing->bs->filename); ++} ++ ++static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ *nperm = 0; ++ *nshared = BLK_PERM_ALL; ++} ++ ++/* Dummy node that provides consistent read to its users without requiring it ++ * from its backing file and that allows writes on the backing file chain. */ ++static BlockDriver bdrv_commit_top = { ++ .format_name = "commit_top", ++ .bdrv_co_preadv = bdrv_commit_top_preadv, ++ .bdrv_refresh_filename = bdrv_commit_top_refresh_filename, ++ .bdrv_child_perm = bdrv_commit_top_child_perm, ++ ++ .is_filter = true, ++ .filtered_child_is_backing = true, ++}; ++ ++void commit_start(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *base, BlockDriverState *top, ++ int creation_flags, int64_t speed, ++ BlockdevOnError on_error, const char *backing_file_str, ++ bool backing_mask_protocol, ++ const char *filter_node_name, Error **errp) ++{ ++ CommitBlockJob *s; ++ BlockDriverState *iter; ++ BlockDriverState *commit_top_bs = NULL; ++ BlockDriverState *filtered_base; ++ int64_t base_size, top_size; ++ uint64_t base_perms, iter_shared_perms; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ assert(top != bs); ++ bdrv_graph_rdlock_main_loop(); ++ if (bdrv_skip_filters(top) == bdrv_skip_filters(base)) { ++ error_setg(errp, "Invalid files for merge: top and base are the same"); ++ bdrv_graph_rdunlock_main_loop(); ++ return; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ base_size = bdrv_getlength(base); ++ if (base_size < 0) { ++ error_setg_errno(errp, -base_size, "Could not inquire base image size"); ++ return; ++ } ++ ++ top_size = bdrv_getlength(top); ++ if (top_size < 0) { ++ error_setg_errno(errp, -top_size, "Could not inquire top image size"); ++ return; ++ } ++ ++ base_perms = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE; ++ if (base_size < top_size) { ++ base_perms |= BLK_PERM_RESIZE; ++ } ++ ++ s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL, ++ speed, creation_flags, NULL, NULL, errp); ++ if (!s) { ++ return; ++ } ++ ++ /* convert base to r/w, if necessary */ ++ s->base_read_only = bdrv_is_read_only(base); ++ if (s->base_read_only) { ++ if (bdrv_reopen_set_read_only(base, false, errp) != 0) { ++ goto fail; ++ } ++ } ++ ++ /* Insert commit_top block node above top, so we can block consistent read ++ * on the backing chain below it */ ++ commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, filter_node_name, 0, ++ errp); ++ if (commit_top_bs == NULL) { ++ goto fail; ++ } ++ if (!filter_node_name) { ++ commit_top_bs->implicit = true; ++ } ++ ++ /* So that we can always drop this node */ ++ commit_top_bs->never_freeze = true; ++ ++ commit_top_bs->total_sectors = top->total_sectors; ++ ++ ret = bdrv_append(commit_top_bs, top, errp); ++ bdrv_unref(commit_top_bs); /* referenced by new parents or failed */ ++ if (ret < 0) { ++ commit_top_bs = NULL; ++ goto fail; ++ } ++ ++ s->commit_top_bs = commit_top_bs; ++ ++ /* ++ * Block all nodes between top and base, because they will ++ * disappear from the chain after this operation. ++ * Note that this assumes that the user is fine with removing all ++ * nodes (including R/W filters) between top and base. Assuring ++ * this is the responsibility of the interface (i.e. whoever calls ++ * commit_start()). ++ */ ++ bdrv_graph_wrlock(); ++ s->base_overlay = bdrv_find_overlay(top, base); ++ assert(s->base_overlay); ++ ++ /* ++ * The topmost node with ++ * bdrv_skip_filters(filtered_base) == bdrv_skip_filters(base) ++ */ ++ filtered_base = bdrv_cow_bs(s->base_overlay); ++ assert(bdrv_skip_filters(filtered_base) == bdrv_skip_filters(base)); ++ ++ /* ++ * XXX BLK_PERM_WRITE needs to be allowed so we don't block ourselves ++ * at s->base (if writes are blocked for a node, they are also blocked ++ * for its backing file). The other options would be a second filter ++ * driver above s->base. ++ */ ++ iter_shared_perms = BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE; ++ ++ for (iter = top; iter != base; iter = bdrv_filter_or_cow_bs(iter)) { ++ if (iter == filtered_base) { ++ /* ++ * From here on, all nodes are filters on the base. This ++ * allows us to share BLK_PERM_CONSISTENT_READ. ++ */ ++ iter_shared_perms |= BLK_PERM_CONSISTENT_READ; ++ } ++ ++ ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, ++ iter_shared_perms, errp); ++ if (ret < 0) { ++ bdrv_graph_wrunlock(); ++ goto fail; ++ } ++ } ++ ++ if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) { ++ bdrv_graph_wrunlock(); ++ goto fail; ++ } ++ s->chain_frozen = true; ++ ++ ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp); ++ bdrv_graph_wrunlock(); ++ ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ s->base = blk_new(s->common.job.aio_context, ++ base_perms, ++ BLK_PERM_CONSISTENT_READ ++ | BLK_PERM_WRITE_UNCHANGED); ++ ret = blk_insert_bs(s->base, base, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ blk_set_disable_request_queuing(s->base, true); ++ s->base_bs = base; ++ ++ /* Required permissions are already taken with block_job_add_bdrv() */ ++ s->top = blk_new(s->common.job.aio_context, 0, BLK_PERM_ALL); ++ ret = blk_insert_bs(s->top, top, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ blk_set_disable_request_queuing(s->top, true); ++ ++ s->backing_file_str = g_strdup(backing_file_str); ++ s->backing_mask_protocol = backing_mask_protocol; ++ s->on_error = on_error; ++ ++ trace_commit_start(bs, base, top, s); ++ job_start(&s->common.job); ++ return; ++ ++fail: ++ if (s->chain_frozen) { ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_unfreeze_backing_chain(commit_top_bs, base); ++ bdrv_graph_rdunlock_main_loop(); ++ } ++ if (s->base) { ++ blk_unref(s->base); ++ } ++ if (s->top) { ++ blk_unref(s->top); ++ } ++ if (s->base_read_only) { ++ bdrv_reopen_set_read_only(base, true, NULL); ++ } ++ job_early_fail(&s->common.job); ++ /* commit_top_bs has to be replaced after deleting the block job, ++ * otherwise this would fail because of lack of permissions. */ ++ if (commit_top_bs) { ++ bdrv_drained_begin(top); ++ bdrv_graph_wrlock(); ++ bdrv_replace_node(commit_top_bs, top, &error_abort); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(top); ++ } ++} ++ ++ ++#define COMMIT_BUF_SIZE (2048 * BDRV_SECTOR_SIZE) ++ ++/* commit COW file into the raw image */ ++int bdrv_commit(BlockDriverState *bs) ++{ ++ BlockBackend *src, *backing; ++ BlockDriverState *backing_file_bs = NULL; ++ BlockDriverState *commit_top_bs = NULL; ++ BlockDriver *drv = bs->drv; ++ AioContext *ctx; ++ int64_t offset, length, backing_length; ++ int ro; ++ int64_t n; ++ int ret = 0; ++ QEMU_AUTO_VFREE uint8_t *buf = NULL; ++ Error *local_err = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!drv) ++ return -ENOMEDIUM; ++ ++ backing_file_bs = bdrv_cow_bs(bs); ++ ++ if (!backing_file_bs) { ++ return -ENOTSUP; ++ } ++ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, NULL) || ++ bdrv_op_is_blocked(backing_file_bs, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) ++ { ++ return -EBUSY; ++ } ++ ++ ro = bdrv_is_read_only(backing_file_bs); ++ ++ if (ro) { ++ if (bdrv_reopen_set_read_only(backing_file_bs, false, NULL)) { ++ return -EACCES; ++ } ++ } ++ ++ ctx = bdrv_get_aio_context(bs); ++ /* WRITE_UNCHANGED is required for bdrv_make_empty() */ ++ src = blk_new(ctx, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED, ++ BLK_PERM_ALL); ++ backing = blk_new(ctx, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); ++ ++ ret = blk_insert_bs(src, bs, &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto ro_cleanup; ++ } ++ ++ /* Insert commit_top block node above backing, so we can write to it */ ++ commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, NULL, BDRV_O_RDWR, ++ &local_err); ++ if (commit_top_bs == NULL) { ++ error_report_err(local_err); ++ goto ro_cleanup; ++ } ++ ++ bdrv_set_backing_hd(commit_top_bs, backing_file_bs, &error_abort); ++ bdrv_set_backing_hd(bs, commit_top_bs, &error_abort); ++ ++ ret = blk_insert_bs(backing, backing_file_bs, &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto ro_cleanup; ++ } ++ ++ length = blk_getlength(src); ++ if (length < 0) { ++ ret = length; ++ goto ro_cleanup; ++ } ++ ++ backing_length = blk_getlength(backing); ++ if (backing_length < 0) { ++ ret = backing_length; ++ goto ro_cleanup; ++ } ++ ++ /* If our top snapshot is larger than the backing file image, ++ * grow the backing file image if possible. If not possible, ++ * we must return an error */ ++ if (length > backing_length) { ++ ret = blk_truncate(backing, length, false, PREALLOC_MODE_OFF, 0, ++ &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto ro_cleanup; ++ } ++ } ++ ++ /* blk_try_blockalign() for src will choose an alignment that works for ++ * backing as well, so no need to compare the alignment manually. */ ++ buf = blk_try_blockalign(src, COMMIT_BUF_SIZE); ++ if (buf == NULL) { ++ ret = -ENOMEM; ++ goto ro_cleanup; ++ } ++ ++ for (offset = 0; offset < length; offset += n) { ++ ret = bdrv_is_allocated(bs, offset, COMMIT_BUF_SIZE, &n); ++ if (ret < 0) { ++ goto ro_cleanup; ++ } ++ if (ret) { ++ ret = blk_pread(src, offset, n, buf, 0); ++ if (ret < 0) { ++ goto ro_cleanup; ++ } ++ ++ ret = blk_pwrite(backing, offset, n, buf, 0); ++ if (ret < 0) { ++ goto ro_cleanup; ++ } ++ } ++ } ++ ++ ret = blk_make_empty(src, NULL); ++ /* Ignore -ENOTSUP */ ++ if (ret < 0 && ret != -ENOTSUP) { ++ goto ro_cleanup; ++ } ++ ++ blk_flush(src); ++ ++ /* ++ * Make sure all data we wrote to the backing device is actually ++ * stable on disk. ++ */ ++ blk_flush(backing); ++ ++ ret = 0; ++ro_cleanup: ++ blk_unref(backing); ++ if (bdrv_cow_bs(bs) != backing_file_bs) { ++ bdrv_set_backing_hd(bs, backing_file_bs, &error_abort); ++ } ++ bdrv_unref(commit_top_bs); ++ blk_unref(src); ++ ++ if (ro) { ++ /* ignoring error return here */ ++ bdrv_reopen_set_read_only(backing_file_bs, true, NULL); ++ } ++ ++ return ret; ++} +diff --git a/qcow2/lib/block/coroutines.h b/qcow2/lib/block/coroutines.h +new file mode 100644 +index 00000000..f3226682 +--- /dev/null ++++ b/qcow2/lib/block/coroutines.h +@@ -0,0 +1,92 @@ ++/* ++ * Block layer I/O functions ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCK_COROUTINES_H ++#define BLOCK_COROUTINES_H ++ ++#include "block/block_int.h" ++ ++/* For blk_bs() in generated block/block-gen.c */ ++#include "sysemu/block-backend.h" ++ ++/* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_common_block_status_above(BlockDriverState *bs, ++ BlockDriverState *base, ++ bool include_base, ++ bool want_zero, ++ int64_t offset, ++ int64_t bytes, ++ int64_t *pnum, ++ int64_t *map, ++ BlockDriverState **file, ++ int *depth); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); ++ ++int coroutine_fn GRAPH_RDLOCK ++nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking, ++ Error **errp); ++ ++ ++/* ++ * "I/O or GS" API functions. These functions can run without ++ * the BQL, but only in one specific iothread/main loop. ++ * ++ * See include/block/block-io.h for more information about ++ * the "I/O or GS" API. ++ */ ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_common_block_status_above(BlockDriverState *bs, ++ BlockDriverState *base, ++ bool include_base, ++ bool want_zero, ++ int64_t offset, ++ int64_t bytes, ++ int64_t *pnum, ++ int64_t *map, ++ BlockDriverState **file, ++ int *depth); ++ ++int co_wrapper_mixed_bdrv_rdlock ++nbd_do_establish_connection(BlockDriverState *bs, bool blocking, Error **errp); ++ ++#endif /* BLOCK_COROUTINES_H */ +diff --git a/qcow2/lib/block/dirty-bitmap.c b/qcow2/lib/block/dirty-bitmap.c +new file mode 100644 +index 00000000..13a19797 +--- /dev/null ++++ b/qcow2/lib/block/dirty-bitmap.c +@@ -0,0 +1,890 @@ ++/* ++ * Block Dirty Bitmap ++ * ++ * Copyright (c) 2016-2017 Red Hat. Inc ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "trace.h" ++#include "block/block-io.h" ++#include "block/block_int.h" ++#include "block/blockjob.h" ++#include "block/dirty-bitmap.h" ++#include "qemu/main-loop.h" ++ ++struct BdrvDirtyBitmap { ++ BlockDriverState *bs; ++ HBitmap *bitmap; /* Dirty bitmap implementation */ ++ bool busy; /* Bitmap is busy, it can't be used via QMP */ ++ BdrvDirtyBitmap *successor; /* Anonymous child, if any. */ ++ char *name; /* Optional non-empty unique ID */ ++ int64_t size; /* Size of the bitmap, in bytes */ ++ bool disabled; /* Bitmap is disabled. It ignores all writes to ++ the device */ ++ int active_iterators; /* How many iterators are active */ ++ bool readonly; /* Bitmap is read-only. This field also ++ prevents the respective image from being ++ modified (i.e. blocks writes and discards). ++ Such operations must fail and both the image ++ and this bitmap must remain unchanged while ++ this flag is set. */ ++ bool persistent; /* bitmap must be saved to owner disk image */ ++ bool inconsistent; /* bitmap is persistent, but inconsistent. ++ It cannot be used at all in any way, except ++ a QMP user can remove it. */ ++ bool skip_store; /* We are either migrating or deleting this ++ * bitmap; it should not be stored on the next ++ * inactivation. */ ++ QLIST_ENTRY(BdrvDirtyBitmap) list; ++}; ++ ++struct BdrvDirtyBitmapIter { ++ HBitmapIter hbi; ++ BdrvDirtyBitmap *bitmap; ++}; ++ ++static inline void bdrv_dirty_bitmaps_lock(BlockDriverState *bs) ++{ ++ qemu_mutex_lock(&bs->dirty_bitmap_mutex); ++} ++ ++static inline void bdrv_dirty_bitmaps_unlock(BlockDriverState *bs) ++{ ++ qemu_mutex_unlock(&bs->dirty_bitmap_mutex); ++} ++ ++void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++} ++ ++void bdrv_dirty_bitmap_unlock(BdrvDirtyBitmap *bitmap) ++{ ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++/* Called with BQL or dirty_bitmap lock taken. */ ++BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) ++{ ++ BdrvDirtyBitmap *bm; ++ ++ assert(name); ++ QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { ++ if (bm->name && !strcmp(name, bm->name)) { ++ return bm; ++ } ++ } ++ return NULL; ++} ++ ++/* Called with BQL taken. */ ++BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, ++ uint32_t granularity, ++ const char *name, ++ Error **errp) ++{ ++ int64_t bitmap_size; ++ BdrvDirtyBitmap *bitmap; ++ ++ assert(is_power_of_2(granularity) && granularity >= BDRV_SECTOR_SIZE); ++ ++ if (name) { ++ if (bdrv_find_dirty_bitmap(bs, name)) { ++ error_setg(errp, "Bitmap already exists: %s", name); ++ return NULL; ++ } ++ if (strlen(name) > BDRV_BITMAP_MAX_NAME_SIZE) { ++ error_setg(errp, "Bitmap name too long: %s", name); ++ return NULL; ++ } ++ } ++ bitmap_size = bdrv_getlength(bs); ++ if (bitmap_size < 0) { ++ error_setg_errno(errp, -bitmap_size, "could not get length of device"); ++ errno = -bitmap_size; ++ return NULL; ++ } ++ bitmap = g_new0(BdrvDirtyBitmap, 1); ++ bitmap->bs = bs; ++ bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(granularity)); ++ bitmap->size = bitmap_size; ++ bitmap->name = g_strdup(name); ++ bitmap->disabled = false; ++ bdrv_dirty_bitmaps_lock(bs); ++ QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list); ++ bdrv_dirty_bitmaps_unlock(bs); ++ return bitmap; ++} ++ ++int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->size; ++} ++ ++const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->name; ++} ++ ++/* Called with BQL taken. */ ++bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->successor; ++} ++ ++static bool bdrv_dirty_bitmap_busy(const BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->busy; ++} ++ ++void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bitmap->busy = busy; ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++/* Called with BQL taken. */ ++bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) ++{ ++ return !bitmap->disabled; ++} ++ ++/* Called with BQL taken. */ ++static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap) ++{ ++ return !bitmap->disabled || (bitmap->successor && ++ !bitmap->successor->disabled); ++} ++ ++int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags, ++ Error **errp) ++{ ++ if ((flags & BDRV_BITMAP_BUSY) && bdrv_dirty_bitmap_busy(bitmap)) { ++ error_setg(errp, "Bitmap '%s' is currently in use by another" ++ " operation and cannot be used", bitmap->name); ++ return -1; ++ } ++ ++ if ((flags & BDRV_BITMAP_RO) && bdrv_dirty_bitmap_readonly(bitmap)) { ++ error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", ++ bitmap->name); ++ return -1; ++ } ++ ++ if ((flags & BDRV_BITMAP_INCONSISTENT) && ++ bdrv_dirty_bitmap_inconsistent(bitmap)) { ++ error_setg(errp, "Bitmap '%s' is inconsistent and cannot be used", ++ bitmap->name); ++ error_append_hint(errp, "Try block-dirty-bitmap-remove to delete" ++ " this bitmap from disk\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Create a successor bitmap destined to replace this bitmap after an operation. ++ * Requires that the bitmap is not marked busy and has no successor. ++ * The successor will be enabled if the parent bitmap was. ++ * Called with BQL taken. ++ */ ++int bdrv_dirty_bitmap_create_successor(BdrvDirtyBitmap *bitmap, Error **errp) ++{ ++ uint64_t granularity; ++ BdrvDirtyBitmap *child; ++ ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { ++ return -1; ++ } ++ if (bdrv_dirty_bitmap_has_successor(bitmap)) { ++ error_setg(errp, "Cannot create a successor for a bitmap that already " ++ "has one"); ++ return -1; ++ } ++ ++ /* Create an anonymous successor */ ++ granularity = bdrv_dirty_bitmap_granularity(bitmap); ++ child = bdrv_create_dirty_bitmap(bitmap->bs, granularity, NULL, errp); ++ if (!child) { ++ return -1; ++ } ++ ++ /* Successor will be on or off based on our current state. */ ++ child->disabled = bitmap->disabled; ++ bitmap->disabled = true; ++ ++ /* Install the successor and mark the parent as busy */ ++ bitmap->successor = child; ++ bitmap->busy = true; ++ return 0; ++} ++ ++void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) ++{ ++ bitmap->disabled = false; ++} ++ ++/* Called with BQL taken. */ ++void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) ++{ ++ assert(bitmap->bs == bitmap->successor->bs); ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bdrv_enable_dirty_bitmap_locked(bitmap->successor); ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++/* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. */ ++static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) ++{ ++ assert(!bitmap->active_iterators); ++ assert(!bdrv_dirty_bitmap_busy(bitmap)); ++ assert(!bdrv_dirty_bitmap_has_successor(bitmap)); ++ QLIST_REMOVE(bitmap, list); ++ hbitmap_free(bitmap->bitmap); ++ g_free(bitmap->name); ++ g_free(bitmap); ++} ++ ++/** ++ * For a bitmap with a successor, yield our name to the successor, ++ * delete the old bitmap, and return a handle to the new bitmap. ++ * Called with BQL taken. ++ */ ++BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BdrvDirtyBitmap *bitmap, ++ Error **errp) ++{ ++ char *name; ++ BdrvDirtyBitmap *successor = bitmap->successor; ++ ++ if (successor == NULL) { ++ error_setg(errp, "Cannot relinquish control if " ++ "there's no successor present"); ++ return NULL; ++ } ++ ++ name = bitmap->name; ++ bitmap->name = NULL; ++ successor->name = name; ++ bitmap->successor = NULL; ++ successor->persistent = bitmap->persistent; ++ bitmap->persistent = false; ++ bitmap->busy = false; ++ bdrv_release_dirty_bitmap(bitmap); ++ ++ return successor; ++} ++ ++/** ++ * In cases of failure where we can no longer safely delete the parent, ++ * we may wish to re-join the parent and child/successor. ++ * The merged parent will be marked as not busy. ++ * The marged parent will be enabled if and only if the successor was enabled. ++ * Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. ++ */ ++BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *parent, ++ Error **errp) ++{ ++ BdrvDirtyBitmap *successor = parent->successor; ++ ++ if (!successor) { ++ error_setg(errp, "Cannot reclaim a successor when none is present"); ++ return NULL; ++ } ++ ++ hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap); ++ ++ parent->disabled = successor->disabled; ++ parent->busy = false; ++ bdrv_release_dirty_bitmap_locked(successor); ++ parent->successor = NULL; ++ ++ return parent; ++} ++ ++/* Called with BQL taken. */ ++BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BdrvDirtyBitmap *parent, ++ Error **errp) ++{ ++ BdrvDirtyBitmap *ret; ++ ++ bdrv_dirty_bitmaps_lock(parent->bs); ++ ret = bdrv_reclaim_dirty_bitmap_locked(parent, errp); ++ bdrv_dirty_bitmaps_unlock(parent->bs); ++ ++ return ret; ++} ++ ++/** ++ * Truncates _all_ bitmaps attached to a BDS. ++ * Called with BQL taken. ++ */ ++void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) ++{ ++ BdrvDirtyBitmap *bitmap; ++ ++ bdrv_dirty_bitmaps_lock(bs); ++ QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { ++ assert(!bdrv_dirty_bitmap_busy(bitmap)); ++ assert(!bdrv_dirty_bitmap_has_successor(bitmap)); ++ assert(!bitmap->active_iterators); ++ hbitmap_truncate(bitmap->bitmap, bytes); ++ bitmap->size = bytes; ++ } ++ bdrv_dirty_bitmaps_unlock(bs); ++} ++ ++/* Called with BQL taken. */ ++void bdrv_release_dirty_bitmap(BdrvDirtyBitmap *bitmap) ++{ ++ BlockDriverState *bs = bitmap->bs; ++ ++ bdrv_dirty_bitmaps_lock(bs); ++ bdrv_release_dirty_bitmap_locked(bitmap); ++ bdrv_dirty_bitmaps_unlock(bs); ++} ++ ++/** ++ * Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()). ++ * There must not be any busy bitmaps attached. ++ * This function does not remove persistent bitmaps from the storage. ++ * Called with BQL taken. ++ */ ++void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) ++{ ++ BdrvDirtyBitmap *bm, *next; ++ ++ bdrv_dirty_bitmaps_lock(bs); ++ QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { ++ if (bdrv_dirty_bitmap_name(bm)) { ++ bdrv_release_dirty_bitmap_locked(bm); ++ } ++ } ++ bdrv_dirty_bitmaps_unlock(bs); ++} ++ ++/** ++ * Remove persistent dirty bitmap from the storage if it exists. ++ * Absence of bitmap is not an error, because we have the following scenario: ++ * BdrvDirtyBitmap can have .persistent = true but not yet saved and have no ++ * stored version. For such bitmap bdrv_remove_persistent_dirty_bitmap() should ++ * not fail. ++ * This function doesn't release corresponding BdrvDirtyBitmap. ++ */ ++int coroutine_fn ++bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, ++ Error **errp) ++{ ++ assert_bdrv_graph_readable(); ++ if (bs->drv && bs->drv->bdrv_co_remove_persistent_dirty_bitmap) { ++ return bs->drv->bdrv_co_remove_persistent_dirty_bitmap(bs, name, errp); ++ } ++ ++ return 0; ++} ++ ++bool ++bdrv_supports_persistent_dirty_bitmap(BlockDriverState *bs) ++{ ++ if (bs->drv && bs->drv->bdrv_supports_persistent_dirty_bitmap) { ++ return bs->drv->bdrv_supports_persistent_dirty_bitmap(bs); ++ } ++ return false; ++} ++ ++bool coroutine_fn ++bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, ++ uint32_t granularity, Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ error_setg_errno(errp, ENOMEDIUM, ++ "Can't store persistent bitmaps to %s", ++ bdrv_get_device_or_node_name(bs)); ++ return false; ++ } ++ ++ if (!drv->bdrv_co_can_store_new_dirty_bitmap) { ++ error_setg_errno(errp, ENOTSUP, ++ "Can't store persistent bitmaps to %s", ++ bdrv_get_device_or_node_name(bs)); ++ return false; ++ } ++ ++ return drv->bdrv_co_can_store_new_dirty_bitmap(bs, name, granularity, errp); ++} ++ ++void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bitmap->disabled = true; ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bdrv_enable_dirty_bitmap_locked(bitmap); ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) ++{ ++ BdrvDirtyBitmap *bm; ++ BlockDirtyInfoList *list = NULL; ++ BlockDirtyInfoList **tail = &list; ++ ++ bdrv_dirty_bitmaps_lock(bs); ++ QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { ++ BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1); ++ ++ info->count = bdrv_get_dirty_count(bm); ++ info->granularity = bdrv_dirty_bitmap_granularity(bm); ++ info->name = g_strdup(bm->name); ++ info->recording = bdrv_dirty_bitmap_recording(bm); ++ info->busy = bdrv_dirty_bitmap_busy(bm); ++ info->persistent = bm->persistent; ++ info->has_inconsistent = bm->inconsistent; ++ info->inconsistent = bm->inconsistent; ++ QAPI_LIST_APPEND(tail, info); ++ } ++ bdrv_dirty_bitmaps_unlock(bs); ++ ++ return list; ++} ++ ++/* Called within bdrv_dirty_bitmap_lock..unlock */ ++bool bdrv_dirty_bitmap_get_locked(BdrvDirtyBitmap *bitmap, int64_t offset) ++{ ++ return hbitmap_get(bitmap->bitmap, offset); ++} ++ ++bool bdrv_dirty_bitmap_get(BdrvDirtyBitmap *bitmap, int64_t offset) ++{ ++ bool ret; ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ ret = bdrv_dirty_bitmap_get_locked(bitmap, offset); ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++ ++ return ret; ++} ++ ++/** ++ * Chooses a default granularity based on the existing cluster size, ++ * but clamped between [4K, 64K]. Defaults to 64K in the case that there ++ * is no cluster size information available. ++ */ ++uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs) ++{ ++ BlockDriverInfo bdi; ++ uint32_t granularity; ++ ++ if (bdrv_get_info(bs, &bdi) >= 0 && bdi.cluster_size > 0) { ++ granularity = MAX(4096, bdi.cluster_size); ++ granularity = MIN(65536, granularity); ++ } else { ++ granularity = 65536; ++ } ++ ++ return granularity; ++} ++ ++uint32_t bdrv_dirty_bitmap_granularity(const BdrvDirtyBitmap *bitmap) ++{ ++ return 1U << hbitmap_granularity(bitmap->bitmap); ++} ++ ++BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap) ++{ ++ BdrvDirtyBitmapIter *iter = g_new(BdrvDirtyBitmapIter, 1); ++ hbitmap_iter_init(&iter->hbi, bitmap->bitmap, 0); ++ iter->bitmap = bitmap; ++ bitmap->active_iterators++; ++ return iter; ++} ++ ++void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter) ++{ ++ if (!iter) { ++ return; ++ } ++ assert(iter->bitmap->active_iterators > 0); ++ iter->bitmap->active_iterators--; ++ g_free(iter); ++} ++ ++int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) ++{ ++ return hbitmap_iter_next(&iter->hbi); ++} ++ ++/* Called within bdrv_dirty_bitmap_lock..unlock */ ++void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes) ++{ ++ assert(!bdrv_dirty_bitmap_readonly(bitmap)); ++ hbitmap_set(bitmap->bitmap, offset, bytes); ++} ++ ++void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bdrv_set_dirty_bitmap_locked(bitmap, offset, bytes); ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++/* Called within bdrv_dirty_bitmap_lock..unlock */ ++void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes) ++{ ++ assert(!bdrv_dirty_bitmap_readonly(bitmap)); ++ hbitmap_reset(bitmap->bitmap, offset, bytes); ++} ++ ++void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bdrv_reset_dirty_bitmap_locked(bitmap, offset, bytes); ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) ++{ ++ IO_CODE(); ++ assert(!bdrv_dirty_bitmap_readonly(bitmap)); ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ if (!out) { ++ hbitmap_reset_all(bitmap->bitmap); ++ } else { ++ HBitmap *backup = bitmap->bitmap; ++ bitmap->bitmap = hbitmap_alloc(bitmap->size, ++ hbitmap_granularity(backup)); ++ *out = backup; ++ } ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup) ++{ ++ HBitmap *tmp = bitmap->bitmap; ++ assert(!bdrv_dirty_bitmap_readonly(bitmap)); ++ GLOBAL_STATE_CODE(); ++ bitmap->bitmap = backup; ++ hbitmap_free(tmp); ++} ++ ++uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap, ++ uint64_t offset, uint64_t bytes) ++{ ++ return hbitmap_serialization_size(bitmap->bitmap, offset, bytes); ++} ++ ++uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap) ++{ ++ return hbitmap_serialization_align(bitmap->bitmap); ++} ++ ++/* Return the disk size covered by a chunk of serialized bitmap data. */ ++uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size, ++ const BdrvDirtyBitmap *bitmap) ++{ ++ uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap); ++ uint64_t limit = granularity * (serialized_chunk_size << 3); ++ ++ assert(QEMU_IS_ALIGNED(limit, ++ bdrv_dirty_bitmap_serialization_align(bitmap))); ++ return limit; ++} ++ ++ ++void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap, ++ uint8_t *buf, uint64_t offset, ++ uint64_t bytes) ++{ ++ hbitmap_serialize_part(bitmap->bitmap, buf, offset, bytes); ++} ++ ++void bdrv_dirty_bitmap_deserialize_part(BdrvDirtyBitmap *bitmap, ++ uint8_t *buf, uint64_t offset, ++ uint64_t bytes, bool finish) ++{ ++ hbitmap_deserialize_part(bitmap->bitmap, buf, offset, bytes, finish); ++} ++ ++void bdrv_dirty_bitmap_deserialize_zeroes(BdrvDirtyBitmap *bitmap, ++ uint64_t offset, uint64_t bytes, ++ bool finish) ++{ ++ hbitmap_deserialize_zeroes(bitmap->bitmap, offset, bytes, finish); ++} ++ ++void bdrv_dirty_bitmap_deserialize_ones(BdrvDirtyBitmap *bitmap, ++ uint64_t offset, uint64_t bytes, ++ bool finish) ++{ ++ hbitmap_deserialize_ones(bitmap->bitmap, offset, bytes, finish); ++} ++ ++void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap) ++{ ++ hbitmap_deserialize_finish(bitmap->bitmap); ++} ++ ++void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ BdrvDirtyBitmap *bitmap; ++ IO_CODE(); ++ ++ if (QLIST_EMPTY(&bs->dirty_bitmaps)) { ++ return; ++ } ++ ++ bdrv_dirty_bitmaps_lock(bs); ++ QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { ++ if (!bdrv_dirty_bitmap_enabled(bitmap)) { ++ continue; ++ } ++ assert(!bdrv_dirty_bitmap_readonly(bitmap)); ++ hbitmap_set(bitmap->bitmap, offset, bytes); ++ } ++ bdrv_dirty_bitmaps_unlock(bs); ++} ++ ++/** ++ * Advance a BdrvDirtyBitmapIter to an arbitrary offset. ++ */ ++void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *iter, int64_t offset) ++{ ++ hbitmap_iter_init(&iter->hbi, iter->hbi.hb, offset); ++} ++ ++int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap) ++{ ++ return hbitmap_count(bitmap->bitmap); ++} ++ ++bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->readonly; ++} ++ ++/* Called with BQL taken. */ ++void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bitmap->readonly = value; ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++bool bdrv_has_readonly_bitmaps(BlockDriverState *bs) ++{ ++ BdrvDirtyBitmap *bm; ++ QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { ++ if (bm->readonly) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++bool bdrv_has_named_bitmaps(BlockDriverState *bs) ++{ ++ BdrvDirtyBitmap *bm; ++ ++ QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { ++ if (bdrv_dirty_bitmap_name(bm)) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++/* Called with BQL taken. */ ++void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bitmap->persistent = persistent; ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++/* Called with BQL taken. */ ++void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ assert(bitmap->persistent == true); ++ bitmap->inconsistent = true; ++ bitmap->disabled = true; ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++/* Called with BQL taken. */ ++void bdrv_dirty_bitmap_skip_store(BdrvDirtyBitmap *bitmap, bool skip) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bitmap->skip_store = skip; ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->persistent && !bitmap->skip_store; ++} ++ ++bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->inconsistent; ++} ++ ++BdrvDirtyBitmap *bdrv_dirty_bitmap_first(BlockDriverState *bs) ++{ ++ return QLIST_FIRST(&bs->dirty_bitmaps); ++} ++ ++BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BdrvDirtyBitmap *bitmap) ++{ ++ return QLIST_NEXT(bitmap, list); ++} ++ ++char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp) ++{ ++ return hbitmap_sha256(bitmap->bitmap, errp); ++} ++ ++int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset, ++ int64_t bytes) ++{ ++ return hbitmap_next_dirty(bitmap->bitmap, offset, bytes); ++} ++ ++int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset, ++ int64_t bytes) ++{ ++ return hbitmap_next_zero(bitmap->bitmap, offset, bytes); ++} ++ ++bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, ++ int64_t start, int64_t end, int64_t max_dirty_count, ++ int64_t *dirty_start, int64_t *dirty_count) ++{ ++ return hbitmap_next_dirty_area(bitmap->bitmap, start, end, max_dirty_count, ++ dirty_start, dirty_count); ++} ++ ++bool bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap, int64_t offset, ++ int64_t bytes, int64_t *count) ++{ ++ return hbitmap_status(bitmap->bitmap, offset, bytes, count); ++} ++ ++/** ++ * bdrv_merge_dirty_bitmap: merge src into dest. ++ * Ensures permissions on bitmaps are reasonable; use for public API. ++ * ++ * @backup: If provided, make a copy of dest here prior to merge. ++ * ++ * Returns true on success, false on failure. In case of failure bitmaps are ++ * untouched. ++ */ ++bool bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, ++ HBitmap **backup, Error **errp) ++{ ++ bool ret = false; ++ ++ bdrv_dirty_bitmaps_lock(dest->bs); ++ if (src->bs != dest->bs) { ++ bdrv_dirty_bitmaps_lock(src->bs); ++ } ++ ++ if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) { ++ goto out; ++ } ++ ++ if (bdrv_dirty_bitmap_check(src, BDRV_BITMAP_ALLOW_RO, errp)) { ++ goto out; ++ } ++ ++ if (bdrv_dirty_bitmap_size(src) != bdrv_dirty_bitmap_size(dest)) { ++ error_setg(errp, "Bitmaps are of different sizes (destination size is %" ++ PRId64 ", source size is %" PRId64 ") and can't be merged", ++ bdrv_dirty_bitmap_size(dest), bdrv_dirty_bitmap_size(src)); ++ goto out; ++ } ++ ++ bdrv_dirty_bitmap_merge_internal(dest, src, backup, false); ++ ret = true; ++ ++out: ++ bdrv_dirty_bitmaps_unlock(dest->bs); ++ if (src->bs != dest->bs) { ++ bdrv_dirty_bitmaps_unlock(src->bs); ++ } ++ ++ return ret; ++} ++ ++/** ++ * bdrv_dirty_bitmap_merge_internal: merge src into dest. ++ * Does NOT check bitmap permissions; not suitable for use as public API. ++ * @dest, @src and @backup (if not NULL) must have same size. ++ * ++ * @backup: If provided, make a copy of dest here prior to merge. ++ * @lock: If true, lock and unlock bitmaps on the way in/out. ++ */ ++void bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, ++ const BdrvDirtyBitmap *src, ++ HBitmap **backup, ++ bool lock) ++{ ++ IO_CODE(); ++ ++ assert(!bdrv_dirty_bitmap_readonly(dest)); ++ assert(!bdrv_dirty_bitmap_inconsistent(dest)); ++ assert(!bdrv_dirty_bitmap_inconsistent(src)); ++ ++ if (lock) { ++ bdrv_dirty_bitmaps_lock(dest->bs); ++ if (src->bs != dest->bs) { ++ bdrv_dirty_bitmaps_lock(src->bs); ++ } ++ } ++ ++ if (backup) { ++ *backup = dest->bitmap; ++ dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup)); ++ hbitmap_merge(*backup, src->bitmap, dest->bitmap); ++ } else { ++ hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap); ++ } ++ ++ if (lock) { ++ bdrv_dirty_bitmaps_unlock(dest->bs); ++ if (src->bs != dest->bs) { ++ bdrv_dirty_bitmaps_unlock(src->bs); ++ } ++ } ++} +diff --git a/qcow2/lib/block/file-posix.c b/qcow2/lib/block/file-posix.c +new file mode 100644 +index 00000000..ff928b5e +--- /dev/null ++++ b/qcow2/lib/block/file-posix.c +@@ -0,0 +1,4565 @@ ++/* ++ * Block driver for RAW files (posix) ++ * ++ * Copyright (c) 2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/cutils.h" ++#include "qemu/error-report.h" ++#include "block/block-io.h" ++#include "block/block_int.h" ++#include "qemu/module.h" ++#include "qemu/option.h" ++#include "qemu/units.h" ++#include "qemu/memalign.h" ++#include "trace.h" ++#include "block/thread-pool.h" ++#include "qemu/iov.h" ++#include "block/raw-aio.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qstring.h" ++ ++#include "scsi/pr-manager.h" ++#include "scsi/constants.h" ++ ++#if defined(__APPLE__) && (__MACH__) ++#include ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++//#include ++#include ++#include ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#endif ++ ++#ifdef __sun__ ++#define _POSIX_PTHREAD_SEMANTICS 1 ++#include ++#endif ++#ifdef __linux__ ++#include ++#include ++#include ++#include ++#if defined(CONFIG_BLKZONED) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef __s390__ ++#include ++#endif ++#ifndef FS_NOCOW_FL ++#define FS_NOCOW_FL 0x00800000 /* Do not cow file */ ++#endif ++#endif ++#if defined(CONFIG_FALLOCATE_PUNCH_HOLE) || defined(CONFIG_FALLOCATE_ZERO_RANGE) ++#include ++#endif ++#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) ++#include ++#include ++#endif ++ ++#ifdef __OpenBSD__ ++#include ++#include ++#include ++#endif ++ ++#ifdef __NetBSD__ ++#include ++#include ++#include ++#include ++#endif ++ ++#ifdef __DragonFly__ ++#include ++#include ++#endif ++ ++/* OS X does not have O_DSYNC */ ++#ifndef O_DSYNC ++#ifdef O_SYNC ++#define O_DSYNC O_SYNC ++#elif defined(O_FSYNC) ++#define O_DSYNC O_FSYNC ++#endif ++#endif ++ ++/* Approximate O_DIRECT with O_DSYNC if O_DIRECT isn't available */ ++#ifndef O_DIRECT ++#define O_DIRECT O_DSYNC ++#endif ++ ++#define FTYPE_FILE 0 ++#define FTYPE_CD 1 ++ ++#define MAX_BLOCKSIZE 4096 ++ ++/* Posix file locking bytes. Libvirt takes byte 0, we start from higher bytes, ++ * leaving a few more bytes for its future use. */ ++#define RAW_LOCK_PERM_BASE 100 ++#define RAW_LOCK_SHARED_BASE 200 ++ ++typedef struct BDRVRawState { ++ int fd; ++ bool use_lock; ++ int type; ++ int open_flags; ++ size_t buf_align; ++ ++ /* The current permissions. */ ++ uint64_t perm; ++ uint64_t shared_perm; ++ ++ /* The perms bits whose corresponding bytes are already locked in ++ * s->fd. */ ++ uint64_t locked_perm; ++ uint64_t locked_shared_perm; ++ ++ uint64_t aio_max_batch; ++ ++ int perm_change_fd; ++ int perm_change_flags; ++ BDRVReopenState *reopen_state; ++ ++ bool has_discard:1; ++ bool has_write_zeroes:1; ++ bool use_linux_aio:1; ++ bool has_laio_fdsync:1; ++ bool use_linux_io_uring:1; ++ int page_cache_inconsistent; /* errno from fdatasync failure */ ++ bool has_fallocate; ++ bool needs_alignment; ++ bool force_alignment; ++ bool drop_cache; ++ bool check_cache_dropped; ++ struct { ++ uint64_t discard_nb_ok; ++ uint64_t discard_nb_failed; ++ uint64_t discard_bytes_ok; ++ } stats; ++ ++ PRManager *pr_mgr; ++} BDRVRawState; ++ ++typedef struct BDRVRawReopenState { ++ int open_flags; ++ bool drop_cache; ++ bool check_cache_dropped; ++} BDRVRawReopenState; ++ ++static int fd_open(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ /* this is just to ensure s->fd is sane (its called by io ops) */ ++ if (s->fd >= 0) { ++ return 0; ++ } ++ return -EIO; ++} ++ ++static int64_t raw_getlength(BlockDriverState *bs); ++ ++typedef struct RawPosixAIOData { ++ BlockDriverState *bs; ++ int aio_type; ++ int aio_fildes; ++ ++ off_t aio_offset; ++ uint64_t aio_nbytes; ++ ++ union { ++ struct { ++ struct iovec *iov; ++ int niov; ++ } io; ++ struct { ++ uint64_t cmd; ++ void *buf; ++ } ioctl; ++ struct { ++ int aio_fd2; ++ off_t aio_offset2; ++ } copy_range; ++ struct { ++ PreallocMode prealloc; ++ Error **errp; ++ } truncate; ++ struct { ++ unsigned int *nr_zones; ++ BlockZoneDescriptor *zones; ++ } zone_report; ++ struct { ++ unsigned long op; ++ } zone_mgmt; ++ }; ++} RawPosixAIOData; ++ ++#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) ++static int cdrom_reopen(BlockDriverState *bs); ++#endif ++ ++/* ++ * Elide EAGAIN and EACCES details when failing to lock, as this ++ * indicates that the specified file region is already locked by ++ * another process, which is considered a common scenario. ++ */ ++#define raw_lock_error_setg_errno(errp, err, fmt, ...) \ ++ do { \ ++ if ((err) == EAGAIN || (err) == EACCES) { \ ++ error_setg((errp), (fmt), ## __VA_ARGS__); \ ++ } else { \ ++ error_setg_errno((errp), (err), (fmt), ## __VA_ARGS__); \ ++ } \ ++ } while (0) ++ ++#if defined(__NetBSD__) ++static int raw_normalize_devicepath(const char **filename, Error **errp) ++{ ++ static char namebuf[PATH_MAX]; ++ const char *dp, *fname; ++ struct stat sb; ++ ++ fname = *filename; ++ dp = strrchr(fname, '/'); ++ if (lstat(fname, &sb) < 0) { ++ error_setg_file_open(errp, errno, fname); ++ return -errno; ++ } ++ ++ if (!S_ISBLK(sb.st_mode)) { ++ return 0; ++ } ++ ++ if (dp == NULL) { ++ snprintf(namebuf, PATH_MAX, "r%s", fname); ++ } else { ++ snprintf(namebuf, PATH_MAX, "%.*s/r%s", ++ (int)(dp - fname), fname, dp + 1); ++ } ++ *filename = namebuf; ++ warn_report("%s is a block device, using %s", fname, *filename); ++ ++ return 0; ++} ++#else ++static int raw_normalize_devicepath(const char **filename, Error **errp) ++{ ++ return 0; ++} ++#endif ++ ++/* ++ * Get logical block size via ioctl. On success store it in @sector_size_p. ++ */ ++static int probe_logical_blocksize(int fd, unsigned int *sector_size_p) ++{ ++ unsigned int sector_size; ++ bool success = false; ++ int i; ++ ++ errno = ENOTSUP; ++ static const unsigned long ioctl_list[] = { ++#ifdef BLKSSZGET ++ BLKSSZGET, ++#endif ++#ifdef DKIOCGETBLOCKSIZE ++ DKIOCGETBLOCKSIZE, ++#endif ++#ifdef DIOCGSECTORSIZE ++ DIOCGSECTORSIZE, ++#endif ++ }; ++ ++ /* Try a few ioctls to get the right size */ ++ for (i = 0; i < (int)ARRAY_SIZE(ioctl_list); i++) { ++ if (ioctl(fd, ioctl_list[i], §or_size) >= 0) { ++ *sector_size_p = sector_size; ++ success = true; ++ } ++ } ++ ++ return success ? 0 : -errno; ++} ++ ++/** ++ * Get physical block size of @fd. ++ * On success, store it in @blk_size and return 0. ++ * On failure, return -errno. ++ */ ++static int probe_physical_blocksize(int fd, unsigned int *blk_size) ++{ ++#ifdef BLKPBSZGET ++ if (ioctl(fd, BLKPBSZGET, blk_size) < 0) { ++ return -errno; ++ } ++ return 0; ++#else ++ return -ENOTSUP; ++#endif ++} ++ ++/* ++ * Returns true if no alignment restrictions are necessary even for files ++ * opened with O_DIRECT. ++ * ++ * raw_probe_alignment() probes the required alignment and assume that 1 means ++ * the probing failed, so it falls back to a safe default of 4k. This can be ++ * avoided if we know that byte alignment is okay for the file. ++ */ ++static bool dio_byte_aligned(int fd) ++{ ++#ifdef __linux__ ++ struct statfs buf; ++ int ret; ++ ++ ret = fstatfs(fd, &buf); ++ if (ret == 0 && buf.f_type == NFS_SUPER_MAGIC) { ++ return true; ++ } ++#endif ++ return false; ++} ++ ++static bool raw_needs_alignment(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if ((bs->open_flags & BDRV_O_NOCACHE) != 0 && !dio_byte_aligned(s->fd)) { ++ return true; ++ } ++ ++ return s->force_alignment; ++} ++ ++/* Check if read is allowed with given memory buffer and length. ++ * ++ * This function is used to check O_DIRECT memory buffer and request alignment. ++ */ ++static bool raw_is_io_aligned(int fd, void *buf, size_t len) ++{ ++ ssize_t ret = pread(fd, buf, len, 0); ++ ++ if (ret >= 0) { ++ return true; ++ } ++ ++#ifdef __linux__ ++ /* The Linux kernel returns EINVAL for misaligned O_DIRECT reads. Ignore ++ * other errors (e.g. real I/O error), which could happen on a failed ++ * drive, since we only care about probing alignment. ++ */ ++ if (errno != EINVAL) { ++ return true; ++ } ++#endif ++ ++ return false; ++} ++ ++static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ char *buf; ++ size_t max_align = MAX(MAX_BLOCKSIZE, qemu_real_host_page_size()); ++ size_t alignments[] = {1, 512, 1024, 2048, 4096}; ++ ++ /* For SCSI generic devices the alignment is not really used. ++ With buffered I/O, we don't have any restrictions. */ ++ if (bdrv_is_sg(bs) || !s->needs_alignment) { ++ bs->bl.request_alignment = 1; ++ s->buf_align = 1; ++ return; ++ } ++ ++ bs->bl.request_alignment = 0; ++ s->buf_align = 0; ++ /* Let's try to use the logical blocksize for the alignment. */ ++ if (probe_logical_blocksize(fd, &bs->bl.request_alignment) < 0) { ++ bs->bl.request_alignment = 0; ++ } ++ ++#ifdef __linux__ ++ /* ++ * The XFS ioctl definitions are shipped in extra packages that might ++ * not always be available. Since we just need the XFS_IOC_DIOINFO ioctl ++ * here, we simply use our own definition instead: ++ */ ++ struct xfs_dioattr { ++ uint32_t d_mem; ++ uint32_t d_miniosz; ++ uint32_t d_maxiosz; ++ } da; ++ if (ioctl(fd, _IOR('X', 30, struct xfs_dioattr), &da) >= 0) { ++ bs->bl.request_alignment = da.d_miniosz; ++ /* The kernel returns wrong information for d_mem */ ++ /* s->buf_align = da.d_mem; */ ++ } ++#endif ++ ++ /* ++ * If we could not get the sizes so far, we can only guess them. First try ++ * to detect request alignment, since it is more likely to succeed. Then ++ * try to detect buf_align, which cannot be detected in some cases (e.g. ++ * Gluster). If buf_align cannot be detected, we fallback to the value of ++ * request_alignment. ++ */ ++ ++ if (!bs->bl.request_alignment) { ++ int i; ++ size_t align; ++ buf = qemu_memalign(max_align, max_align); ++ for (i = 0; i < ARRAY_SIZE(alignments); i++) { ++ align = alignments[i]; ++ if (raw_is_io_aligned(fd, buf, align)) { ++ /* Fallback to safe value. */ ++ bs->bl.request_alignment = (align != 1) ? align : max_align; ++ break; ++ } ++ } ++ qemu_vfree(buf); ++ } ++ ++ if (!s->buf_align) { ++ int i; ++ size_t align; ++ buf = qemu_memalign(max_align, 2 * max_align); ++ for (i = 0; i < ARRAY_SIZE(alignments); i++) { ++ align = alignments[i]; ++ if (raw_is_io_aligned(fd, buf + align, max_align)) { ++ /* Fallback to request_alignment. */ ++ s->buf_align = (align != 1) ? align : bs->bl.request_alignment; ++ break; ++ } ++ } ++ qemu_vfree(buf); ++ } ++ ++ if (!s->buf_align || !bs->bl.request_alignment) { ++ error_setg(errp, "Could not find working O_DIRECT alignment"); ++ error_append_hint(errp, "Try cache.direct=off\n"); ++ } ++} ++ ++static int check_hdev_writable(int fd) ++{ ++#if defined(BLKROGET) ++ /* Linux block devices can be configured "read-only" using blockdev(8). ++ * This is independent of device node permissions and therefore open(2) ++ * with O_RDWR succeeds. Actual writes fail with EPERM. ++ * ++ * bdrv_open() is supposed to fail if the disk is read-only. Explicitly ++ * check for read-only block devices so that Linux block devices behave ++ * properly. ++ */ ++ struct stat st; ++ int readonly = 0; ++ ++ if (fstat(fd, &st)) { ++ return -errno; ++ } ++ ++ if (!S_ISBLK(st.st_mode)) { ++ return 0; ++ } ++ ++ if (ioctl(fd, BLKROGET, &readonly) < 0) { ++ return -errno; ++ } ++ ++ if (readonly) { ++ return -EACCES; ++ } ++#endif /* defined(BLKROGET) */ ++ return 0; ++} ++ ++static void raw_parse_flags(int bdrv_flags, int *open_flags, bool has_writers) ++{ ++ bool read_write = false; ++ assert(open_flags != NULL); ++ ++ *open_flags |= O_BINARY; ++ *open_flags &= ~O_ACCMODE; ++ ++ if (bdrv_flags & BDRV_O_AUTO_RDONLY) { ++ read_write = has_writers; ++ } else if (bdrv_flags & BDRV_O_RDWR) { ++ read_write = true; ++ } ++ ++ if (read_write) { ++ *open_flags |= O_RDWR; ++ } else { ++ *open_flags |= O_RDONLY; ++ } ++ ++ /* Use O_DSYNC for write-through caching, no flags for write-back caching, ++ * and O_DIRECT for no caching. */ ++ if ((bdrv_flags & BDRV_O_NOCACHE)) { ++ *open_flags |= O_DIRECT; ++ } ++} ++ ++static void raw_parse_filename(const char *filename, QDict *options, ++ Error **errp) ++{ ++ bdrv_parse_filename_strip_prefix(filename, "file:", options); ++} ++ ++static QemuOptsList raw_runtime_opts = { ++ .name = "raw", ++ .head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head), ++ .desc = { ++ { ++ .name = "filename", ++ .type = QEMU_OPT_STRING, ++ .help = "File name of the image", ++ }, ++ { ++ .name = "aio", ++ .type = QEMU_OPT_STRING, ++ .help = "host AIO implementation (threads, native, io_uring)", ++ }, ++ { ++ .name = "aio-max-batch", ++ .type = QEMU_OPT_NUMBER, ++ .help = "AIO max batch size (0 = auto handled by AIO backend, default: 0)", ++ }, ++ { ++ .name = "locking", ++ .type = QEMU_OPT_STRING, ++ .help = "file locking mode (on/off/auto, default: auto)", ++ }, ++ { ++ .name = "pr-manager", ++ .type = QEMU_OPT_STRING, ++ .help = "id of persistent reservation manager object (default: none)", ++ }, ++#if defined(__linux__) ++ { ++ .name = "drop-cache", ++ .type = QEMU_OPT_BOOL, ++ .help = "invalidate page cache during live migration (default: on)", ++ }, ++#endif ++ { ++ .name = "x-check-cache-dropped", ++ .type = QEMU_OPT_BOOL, ++ .help = "check that page cache was dropped on live migration (default: off)" ++ }, ++ { /* end of list */ } ++ }, ++}; ++ ++static const char *const mutable_opts[] = { "x-check-cache-dropped", NULL }; ++ ++static int raw_open_common(BlockDriverState *bs, QDict *options, ++ int bdrv_flags, int open_flags, ++ bool device, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ QemuOpts *opts; ++ Error *local_err = NULL; ++ const char *filename = NULL; ++ const char *str; ++ BlockdevAioOptions aio, aio_default; ++ int fd, ret; ++ struct stat st; ++ OnOffAuto locking; ++ ++ opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_absorb_qdict(opts, options, errp)) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ filename = qemu_opt_get(opts, "filename"); ++ ++ ret = raw_normalize_devicepath(&filename, errp); ++ if (ret != 0) { ++ goto fail; ++ } ++ ++ if (bdrv_flags & BDRV_O_NATIVE_AIO) { ++ aio_default = BLOCKDEV_AIO_OPTIONS_NATIVE; ++#ifdef CONFIG_LINUX_IO_URING ++ } else if (bdrv_flags & BDRV_O_IO_URING) { ++ aio_default = BLOCKDEV_AIO_OPTIONS_IO_URING; ++#endif ++ } else { ++ aio_default = BLOCKDEV_AIO_OPTIONS_THREADS; ++ } ++ ++ aio = qapi_enum_parse(&BlockdevAioOptions_lookup, ++ qemu_opt_get(opts, "aio"), ++ aio_default, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ s->use_linux_aio = (aio == BLOCKDEV_AIO_OPTIONS_NATIVE); ++#ifdef CONFIG_LINUX_IO_URING ++ s->use_linux_io_uring = (aio == BLOCKDEV_AIO_OPTIONS_IO_URING); ++#endif ++ ++ s->aio_max_batch = qemu_opt_get_number(opts, "aio-max-batch", 0); ++ ++ locking = qapi_enum_parse(&OnOffAuto_lookup, ++ qemu_opt_get(opts, "locking"), ++ ON_OFF_AUTO_AUTO, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ ret = -EINVAL; ++ goto fail; ++ } ++ switch (locking) { ++ case ON_OFF_AUTO_ON: ++ s->use_lock = true; ++ if (!qemu_has_ofd_lock()) { ++ warn_report("File lock requested but OFD locking syscall is " ++ "unavailable, falling back to POSIX file locks"); ++ error_printf("Due to the implementation, locks can be lost " ++ "unexpectedly.\n"); ++ } ++ break; ++ case ON_OFF_AUTO_OFF: ++ s->use_lock = false; ++ break; ++ case ON_OFF_AUTO_AUTO: ++ s->use_lock = qemu_has_ofd_lock(); ++ break; ++ default: ++ abort(); ++ } ++ ++ str = qemu_opt_get(opts, "pr-manager"); ++ if (str) { ++ s->pr_mgr = pr_manager_lookup(str, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ ret = -EINVAL; ++ goto fail; ++ } ++ } ++ ++ s->drop_cache = qemu_opt_get_bool(opts, "drop-cache", true); ++ s->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped", ++ false); ++ ++ s->open_flags = open_flags; ++ raw_parse_flags(bdrv_flags, &s->open_flags, false); ++ ++ s->fd = -1; ++ fd = qemu_open(filename, s->open_flags, errp); ++ ret = fd < 0 ? -errno : 0; ++ ++ if (ret < 0) { ++ if (ret == -EROFS) { ++ ret = -EACCES; ++ } ++ goto fail; ++ } ++ s->fd = fd; ++ ++ /* Check s->open_flags rather than bdrv_flags due to auto-read-only */ ++ if (s->open_flags & O_RDWR) { ++ ret = check_hdev_writable(s->fd); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "The device is not writable"); ++ goto fail; ++ } ++ } ++ ++ s->perm = 0; ++ s->shared_perm = BLK_PERM_ALL; ++ ++#ifdef CONFIG_LINUX_AIO ++ /* Currently Linux does AIO only for files opened with O_DIRECT */ ++ if (s->use_linux_aio && !(s->open_flags & O_DIRECT)) { ++ error_setg(errp, "aio=native was specified, but it requires " ++ "cache.direct=on, which was not specified."); ++ ret = -EINVAL; ++ goto fail; ++ } ++ if (s->use_linux_aio) { ++ s->has_laio_fdsync = laio_has_fdsync(s->fd); ++ } ++#else ++ if (s->use_linux_aio) { ++ error_setg(errp, "aio=native was specified, but is not supported " ++ "in this build."); ++ ret = -EINVAL; ++ goto fail; ++ } ++#endif /* !defined(CONFIG_LINUX_AIO) */ ++ ++#ifndef CONFIG_LINUX_IO_URING ++ if (s->use_linux_io_uring) { ++ error_setg(errp, "aio=io_uring was specified, but is not supported " ++ "in this build."); ++ ret = -EINVAL; ++ goto fail; ++ } ++#endif /* !defined(CONFIG_LINUX_IO_URING) */ ++ ++ s->has_discard = true; ++ s->has_write_zeroes = true; ++ ++ if (fstat(s->fd, &st) < 0) { ++ ret = -errno; ++ error_setg_errno(errp, errno, "Could not stat file"); ++ goto fail; ++ } ++ ++ if (!device) { ++ if (!S_ISREG(st.st_mode)) { ++ error_setg(errp, "'%s' driver requires '%s' to be a regular file", ++ bs->drv->format_name, bs->filename); ++ ret = -EINVAL; ++ goto fail; ++ } else { ++ s->has_fallocate = true; ++ } ++ } else { ++ if (!(S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) { ++ error_setg(errp, "'%s' driver requires '%s' to be either " ++ "a character or block device", ++ bs->drv->format_name, bs->filename); ++ ret = -EINVAL; ++ goto fail; ++ } ++ } ++#ifdef CONFIG_BLKZONED ++ /* ++ * The kernel page cache does not reliably work for writes to SWR zones ++ * of zoned block device because it can not guarantee the order of writes. ++ */ ++ if ((bs->bl.zoned != BLK_Z_NONE) && ++ (!(s->open_flags & O_DIRECT))) { ++ error_setg(errp, "The driver supports zoned devices, and it requires " ++ "cache.direct=on, which was not specified."); ++ return -EINVAL; /* No host kernel page cache */ ++ } ++#endif ++ ++ if (S_ISBLK(st.st_mode)) { ++#ifdef __linux__ ++ /* On Linux 3.10, BLKDISCARD leaves stale data in the page cache. Do ++ * not rely on the contents of discarded blocks unless using O_DIRECT. ++ * Same for BLKZEROOUT. ++ */ ++ if (!(bs->open_flags & BDRV_O_NOCACHE)) { ++ s->has_write_zeroes = false; ++ } ++#endif ++ } ++#ifdef __FreeBSD__ ++ if (S_ISCHR(st.st_mode)) { ++ /* ++ * The file is a char device (disk), which on FreeBSD isn't behind ++ * a pager, so force all requests to be aligned. This is needed ++ * so QEMU makes sure all IO operations on the device are aligned ++ * to sector size, or else FreeBSD will reject them with EINVAL. ++ */ ++ s->force_alignment = true; ++ } ++#endif ++ s->needs_alignment = raw_needs_alignment(bs); ++ ++ bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK; ++ if (S_ISREG(st.st_mode)) { ++ /* When extending regular files, we get zeros from the OS */ ++ bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; ++ } ++ ret = 0; ++fail: ++ if (ret < 0 && s->fd != -1) { ++ qemu_close(s->fd); ++ } ++ if (filename && (bdrv_flags & BDRV_O_TEMPORARY)) { ++ unlink(filename); ++ } ++ qemu_opts_del(opts); ++ return ret; ++} ++ ++static int raw_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ s->type = FTYPE_FILE; ++ return raw_open_common(bs, options, flags, 0, false, errp); ++} ++ ++typedef enum { ++ RAW_PL_PREPARE, ++ RAW_PL_COMMIT, ++ RAW_PL_ABORT, ++} RawPermLockOp; ++ ++#define PERM_FOREACH(i) \ ++ for ((i) = 0; (1ULL << (i)) <= BLK_PERM_ALL; i++) ++ ++/* Lock bytes indicated by @perm_lock_bits and @shared_perm_lock_bits in the ++ * file; if @unlock == true, also unlock the unneeded bytes. ++ * @shared_perm_lock_bits is the mask of all permissions that are NOT shared. ++ */ ++static int raw_apply_lock_bytes(BDRVRawState *s, int fd, ++ uint64_t perm_lock_bits, ++ uint64_t shared_perm_lock_bits, ++ bool unlock, Error **errp) ++{ ++ int ret; ++ int i; ++ uint64_t locked_perm, locked_shared_perm; ++ ++ if (s) { ++ locked_perm = s->locked_perm; ++ locked_shared_perm = s->locked_shared_perm; ++ } else { ++ /* ++ * We don't have the previous bits, just lock/unlock for each of the ++ * requested bits. ++ */ ++ if (unlock) { ++ locked_perm = BLK_PERM_ALL; ++ locked_shared_perm = BLK_PERM_ALL; ++ } else { ++ locked_perm = 0; ++ locked_shared_perm = 0; ++ } ++ } ++ ++ PERM_FOREACH(i) { ++ int off = RAW_LOCK_PERM_BASE + i; ++ uint64_t bit = (1ULL << i); ++ if ((perm_lock_bits & bit) && !(locked_perm & bit)) { ++ ret = qemu_lock_fd(fd, off, 1, false); ++ if (ret) { ++ raw_lock_error_setg_errno(errp, -ret, "Failed to lock byte %d", ++ off); ++ return ret; ++ } else if (s) { ++ s->locked_perm |= bit; ++ } ++ } else if (unlock && (locked_perm & bit) && !(perm_lock_bits & bit)) { ++ ret = qemu_unlock_fd(fd, off, 1); ++ if (ret) { ++ error_setg_errno(errp, -ret, "Failed to unlock byte %d", off); ++ return ret; ++ } else if (s) { ++ s->locked_perm &= ~bit; ++ } ++ } ++ } ++ PERM_FOREACH(i) { ++ int off = RAW_LOCK_SHARED_BASE + i; ++ uint64_t bit = (1ULL << i); ++ if ((shared_perm_lock_bits & bit) && !(locked_shared_perm & bit)) { ++ ret = qemu_lock_fd(fd, off, 1, false); ++ if (ret) { ++ raw_lock_error_setg_errno(errp, -ret, "Failed to lock byte %d", ++ off); ++ return ret; ++ } else if (s) { ++ s->locked_shared_perm |= bit; ++ } ++ } else if (unlock && (locked_shared_perm & bit) && ++ !(shared_perm_lock_bits & bit)) { ++ ret = qemu_unlock_fd(fd, off, 1); ++ if (ret) { ++ error_setg_errno(errp, -ret, "Failed to unlock byte %d", off); ++ return ret; ++ } else if (s) { ++ s->locked_shared_perm &= ~bit; ++ } ++ } ++ } ++ return 0; ++} ++ ++/* Check "unshared" bytes implied by @perm and ~@shared_perm in the file. */ ++static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm, ++ Error **errp) ++{ ++ int ret; ++ int i; ++ ++ PERM_FOREACH(i) { ++ int off = RAW_LOCK_SHARED_BASE + i; ++ uint64_t p = 1ULL << i; ++ if (perm & p) { ++ ret = qemu_lock_fd_test(fd, off, 1, true); ++ if (ret) { ++ char *perm_name = bdrv_perm_names(p); ++ ++ raw_lock_error_setg_errno(errp, -ret, ++ "Failed to get \"%s\" lock", ++ perm_name); ++ g_free(perm_name); ++ return ret; ++ } ++ } ++ } ++ PERM_FOREACH(i) { ++ int off = RAW_LOCK_PERM_BASE + i; ++ uint64_t p = 1ULL << i; ++ if (!(shared_perm & p)) { ++ ret = qemu_lock_fd_test(fd, off, 1, true); ++ if (ret) { ++ char *perm_name = bdrv_perm_names(p); ++ ++ raw_lock_error_setg_errno(errp, -ret, ++ "Failed to get shared \"%s\" lock", ++ perm_name); ++ g_free(perm_name); ++ return ret; ++ } ++ } ++ } ++ return 0; ++} ++ ++static int raw_handle_perm_lock(BlockDriverState *bs, ++ RawPermLockOp op, ++ uint64_t new_perm, uint64_t new_shared, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret = 0; ++ Error *local_err = NULL; ++ ++ if (!s->use_lock) { ++ return 0; ++ } ++ ++ if (bdrv_get_flags(bs) & BDRV_O_INACTIVE) { ++ return 0; ++ } ++ ++ switch (op) { ++ case RAW_PL_PREPARE: ++ if ((s->perm | new_perm) == s->perm && ++ (s->shared_perm & new_shared) == s->shared_perm) ++ { ++ /* ++ * We are going to unlock bytes, it should not fail. If it fail due ++ * to some fs-dependent permission-unrelated reasons (which occurs ++ * sometimes on NFS and leads to abort in bdrv_replace_child) we ++ * can't prevent such errors by any check here. And we ignore them ++ * anyway in ABORT and COMMIT. ++ */ ++ return 0; ++ } ++ ret = raw_apply_lock_bytes(s, s->fd, s->perm | new_perm, ++ ~s->shared_perm | ~new_shared, ++ false, errp); ++ if (!ret) { ++ ret = raw_check_lock_bytes(s->fd, new_perm, new_shared, errp); ++ if (!ret) { ++ return 0; ++ } ++ error_append_hint(errp, ++ "Is another process using the image [%s]?\n", ++ bs->filename); ++ } ++ /* fall through to unlock bytes. */ ++ case RAW_PL_ABORT: ++ raw_apply_lock_bytes(s, s->fd, s->perm, ~s->shared_perm, ++ true, &local_err); ++ if (local_err) { ++ /* Theoretically the above call only unlocks bytes and it cannot ++ * fail. Something weird happened, report it. ++ */ ++ warn_report_err(local_err); ++ } ++ break; ++ case RAW_PL_COMMIT: ++ raw_apply_lock_bytes(s, s->fd, new_perm, ~new_shared, ++ true, &local_err); ++ if (local_err) { ++ /* Theoretically the above call only unlocks bytes and it cannot ++ * fail. Something weird happened, report it. ++ */ ++ warn_report_err(local_err); ++ } ++ break; ++ } ++ return ret; ++} ++ ++/* Sets a specific flag */ ++static int fcntl_setfl(int fd, int flag) ++{ ++ int flags; ++ ++ flags = fcntl(fd, F_GETFL); ++ if (flags == -1) { ++ return -errno; ++ } ++ if (fcntl(fd, F_SETFL, flags | flag) == -1) { ++ return -errno; ++ } ++ return 0; ++} ++ ++static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, ++ int *open_flags, uint64_t perm, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ int fd = -1; ++ int ret; ++ bool has_writers = perm & ++ (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_RESIZE); ++ int fcntl_flags = O_APPEND | O_NONBLOCK; ++#ifdef O_NOATIME ++ fcntl_flags |= O_NOATIME; ++#endif ++ ++ *open_flags = 0; ++ if (s->type == FTYPE_CD) { ++ *open_flags |= O_NONBLOCK; ++ } ++ ++ raw_parse_flags(flags, open_flags, has_writers); ++ ++#ifdef O_ASYNC ++ /* Not all operating systems have O_ASYNC, and those that don't ++ * will not let us track the state into rs->open_flags (typically ++ * you achieve the same effect with an ioctl, for example I_SETSIG ++ * on Solaris). But we do not use O_ASYNC, so that's fine. ++ */ ++ assert((s->open_flags & O_ASYNC) == 0); ++#endif ++ ++ if (*open_flags == s->open_flags) { ++ /* We're lucky, the existing fd is fine */ ++ return s->fd; ++ } ++ ++ if ((*open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) { ++ /* dup the original fd */ ++ fd = qemu_dup(s->fd); ++ if (fd >= 0) { ++ ret = fcntl_setfl(fd, *open_flags); ++ if (ret) { ++ qemu_close(fd); ++ fd = -1; ++ } ++ } ++ } ++ ++ /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */ ++ if (fd == -1) { ++ const char *normalized_filename = bs->filename; ++ ret = raw_normalize_devicepath(&normalized_filename, errp); ++ if (ret >= 0) { ++ fd = qemu_open(normalized_filename, *open_flags, errp); ++ if (fd == -1) { ++ return -1; ++ } ++ } ++ } ++ ++ if (fd != -1 && (*open_flags & O_RDWR)) { ++ ret = check_hdev_writable(fd); ++ if (ret < 0) { ++ qemu_close(fd); ++ error_setg_errno(errp, -ret, "The device is not writable"); ++ return -1; ++ } ++ } ++ ++ return fd; ++} ++ ++static int raw_reopen_prepare(BDRVReopenState *state, ++ BlockReopenQueue *queue, Error **errp) ++{ ++ BDRVRawState *s; ++ BDRVRawReopenState *rs; ++ QemuOpts *opts; ++ int ret; ++ ++ assert(state != NULL); ++ assert(state->bs != NULL); ++ ++ s = state->bs->opaque; ++ ++ state->opaque = g_new0(BDRVRawReopenState, 1); ++ rs = state->opaque; ++ ++ /* Handle options changes */ ++ opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_absorb_qdict(opts, state->options, errp)) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ rs->drop_cache = qemu_opt_get_bool_del(opts, "drop-cache", true); ++ rs->check_cache_dropped = ++ qemu_opt_get_bool_del(opts, "x-check-cache-dropped", false); ++ ++ /* This driver's reopen function doesn't currently allow changing ++ * other options, so let's put them back in the original QDict and ++ * bdrv_reopen_prepare() will detect changes and complain. */ ++ qemu_opts_to_qdict(opts, state->options); ++ ++ /* ++ * As part of reopen prepare we also want to create new fd by ++ * raw_reconfigure_getfd(). But it wants updated "perm", when in ++ * bdrv_reopen_multiple() .bdrv_reopen_prepare() callback called prior to ++ * permission update. Happily, permission update is always a part ++ * (a separate stage) of bdrv_reopen_multiple() so we can rely on this ++ * fact and reconfigure fd in raw_check_perm(). ++ */ ++ ++ s->reopen_state = state; ++ ret = 0; ++ ++out: ++ qemu_opts_del(opts); ++ return ret; ++} ++ ++static void raw_reopen_commit(BDRVReopenState *state) ++{ ++ BDRVRawReopenState *rs = state->opaque; ++ BDRVRawState *s = state->bs->opaque; ++ ++ s->drop_cache = rs->drop_cache; ++ s->check_cache_dropped = rs->check_cache_dropped; ++ s->open_flags = rs->open_flags; ++ g_free(state->opaque); ++ state->opaque = NULL; ++ ++ assert(s->reopen_state == state); ++ s->reopen_state = NULL; ++} ++ ++ ++static void raw_reopen_abort(BDRVReopenState *state) ++{ ++ BDRVRawReopenState *rs = state->opaque; ++ BDRVRawState *s = state->bs->opaque; ++ ++ /* nothing to do if NULL, we didn't get far enough */ ++ if (rs == NULL) { ++ return; ++ } ++ ++ g_free(state->opaque); ++ state->opaque = NULL; ++ ++ assert(s->reopen_state == state); ++ s->reopen_state = NULL; ++} ++ ++static int hdev_get_max_hw_transfer(int fd, struct stat *st) ++{ ++#ifdef BLKSECTGET ++ if (S_ISBLK(st->st_mode)) { ++ unsigned short max_sectors = 0; ++ if (ioctl(fd, BLKSECTGET, &max_sectors) == 0) { ++ return max_sectors * 512; ++ } ++ } else { ++ int max_bytes = 0; ++ if (ioctl(fd, BLKSECTGET, &max_bytes) == 0) { ++ return max_bytes; ++ } ++ } ++ return -errno; ++#else ++ return -ENOSYS; ++#endif ++} ++ ++/* ++ * Get a sysfs attribute value as character string. ++ */ ++#ifdef CONFIG_LINUX ++static int get_sysfs_str_val(struct stat *st, const char *attribute, ++ char **val) { ++ g_autofree char *sysfspath = NULL; ++ size_t len; ++ ++ if (!S_ISBLK(st->st_mode)) { ++ return -ENOTSUP; ++ } ++ ++ sysfspath = g_strdup_printf("/sys/dev/block/%u:%u/queue/%s", ++ major(st->st_rdev), minor(st->st_rdev), ++ attribute); ++ if (!g_file_get_contents(sysfspath, val, &len, NULL)) { ++ return -ENOENT; ++ } ++ ++ /* The file is ended with '\n' */ ++ char *p; ++ p = *val; ++ if (*(p + len - 1) == '\n') { ++ *(p + len - 1) = '\0'; ++ } ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_BLKZONED) ++static int get_sysfs_zoned_model(struct stat *st, BlockZoneModel *zoned) ++{ ++ g_autofree char *val = NULL; ++ int ret; ++ ++ ret = get_sysfs_str_val(st, "zoned", &val); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (strcmp(val, "host-managed") == 0) { ++ *zoned = BLK_Z_HM; ++ } else if (strcmp(val, "host-aware") == 0) { ++ *zoned = BLK_Z_HA; ++ } else if (strcmp(val, "none") == 0) { ++ *zoned = BLK_Z_NONE; ++ } else { ++ return -ENOTSUP; ++ } ++ return 0; ++} ++#endif /* defined(CONFIG_BLKZONED) */ ++ ++/* ++ * Get a sysfs attribute value as a long integer. ++ */ ++#ifdef CONFIG_LINUX ++static long get_sysfs_long_val(struct stat *st, const char *attribute) ++{ ++ g_autofree char *str = NULL; ++ const char *end; ++ long val; ++ int ret; ++ ++ ret = get_sysfs_str_val(st, attribute, &str); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* The file is ended with '\n', pass 'end' to accept that. */ ++ ret = qemu_strtol(str, &end, 10, &val); ++ if (ret == 0 && end && *end == '\0') { ++ ret = val; ++ } ++ return ret; ++} ++#endif ++ ++static int hdev_get_max_segments(int fd, struct stat *st) ++{ ++#ifdef CONFIG_LINUX ++ int ret; ++ ++ if (S_ISCHR(st->st_mode)) { ++ if (ioctl(fd, SG_GET_SG_TABLESIZE, &ret) == 0) { ++ return ret; ++ } ++ return -ENOTSUP; ++ } ++ return get_sysfs_long_val(st, "max_segments"); ++#else ++ return -ENOTSUP; ++#endif ++} ++ ++#if defined(CONFIG_BLKZONED) ++/* ++ * If the reset_all flag is true, then the wps of zone whose state is ++ * not readonly or offline should be all reset to the start sector. ++ * Else, take the real wp of the device. ++ */ ++static int get_zones_wp(BlockDriverState *bs, int fd, int64_t offset, ++ unsigned int nrz, bool reset_all) ++{ ++ struct blk_zone *blkz; ++ size_t rep_size; ++ uint64_t sector = offset >> BDRV_SECTOR_BITS; ++ BlockZoneWps *wps = bs->wps; ++ unsigned int j = offset / bs->bl.zone_size; ++ unsigned int n = 0, i = 0; ++ int ret; ++ rep_size = sizeof(struct blk_zone_report) + nrz * sizeof(struct blk_zone); ++ g_autofree struct blk_zone_report *rep = NULL; ++ ++ rep = g_malloc(rep_size); ++ blkz = (struct blk_zone *)(rep + 1); ++ while (n < nrz) { ++ memset(rep, 0, rep_size); ++ rep->sector = sector; ++ rep->nr_zones = nrz - n; ++ ++ do { ++ ret = ioctl(fd, BLKREPORTZONE, rep); ++ } while (ret != 0 && errno == EINTR); ++ if (ret != 0) { ++ error_report("%d: ioctl BLKREPORTZONE at %" PRId64 " failed %d", ++ fd, offset, errno); ++ return -errno; ++ } ++ ++ if (!rep->nr_zones) { ++ break; ++ } ++ ++ for (i = 0; i < rep->nr_zones; ++i, ++n, ++j) { ++ /* ++ * The wp tracking cares only about sequential writes required and ++ * sequential write preferred zones so that the wp can advance to ++ * the right location. ++ * Use the most significant bit of the wp location to indicate the ++ * zone type: 0 for SWR/SWP zones and 1 for conventional zones. ++ */ ++ if (blkz[i].type == BLK_ZONE_TYPE_CONVENTIONAL) { ++ wps->wp[j] |= 1ULL << 63; ++ } else { ++ switch(blkz[i].cond) { ++ case BLK_ZONE_COND_FULL: ++ case BLK_ZONE_COND_READONLY: ++ /* Zone not writable */ ++ wps->wp[j] = (blkz[i].start + blkz[i].len) << BDRV_SECTOR_BITS; ++ break; ++ case BLK_ZONE_COND_OFFLINE: ++ /* Zone not writable nor readable */ ++ wps->wp[j] = (blkz[i].start) << BDRV_SECTOR_BITS; ++ break; ++ default: ++ if (reset_all) { ++ wps->wp[j] = blkz[i].start << BDRV_SECTOR_BITS; ++ } else { ++ wps->wp[j] = blkz[i].wp << BDRV_SECTOR_BITS; ++ } ++ break; ++ } ++ } ++ } ++ sector = blkz[i - 1].start + blkz[i - 1].len; ++ } ++ ++ return 0; ++} ++ ++static void update_zones_wp(BlockDriverState *bs, int fd, int64_t offset, ++ unsigned int nrz) ++{ ++ if (get_zones_wp(bs, fd, offset, nrz, 0) < 0) { ++ error_report("update zone wp failed"); ++ } ++} ++ ++static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ BlockZoneModel zoned; ++ int ret; ++ ++ ret = get_sysfs_zoned_model(st, &zoned); ++ if (ret < 0 || zoned == BLK_Z_NONE) { ++ goto no_zoned; ++ } ++ bs->bl.zoned = zoned; ++ ++ ret = get_sysfs_long_val(st, "max_open_zones"); ++ if (ret >= 0) { ++ bs->bl.max_open_zones = ret; ++ } ++ ++ ret = get_sysfs_long_val(st, "max_active_zones"); ++ if (ret >= 0) { ++ bs->bl.max_active_zones = ret; ++ } ++ ++ /* ++ * The zoned device must at least have zone size and nr_zones fields. ++ */ ++ ret = get_sysfs_long_val(st, "chunk_sectors"); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Unable to read chunk_sectors " ++ "sysfs attribute"); ++ goto no_zoned; ++ } else if (!ret) { ++ error_setg(errp, "Read 0 from chunk_sectors sysfs attribute"); ++ goto no_zoned; ++ } ++ bs->bl.zone_size = ret << BDRV_SECTOR_BITS; ++ ++ ret = get_sysfs_long_val(st, "nr_zones"); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Unable to read nr_zones " ++ "sysfs attribute"); ++ goto no_zoned; ++ } else if (!ret) { ++ error_setg(errp, "Read 0 from nr_zones sysfs attribute"); ++ goto no_zoned; ++ } ++ bs->bl.nr_zones = ret; ++ ++ ret = get_sysfs_long_val(st, "zone_append_max_bytes"); ++ if (ret > 0) { ++ bs->bl.max_append_sectors = ret >> BDRV_SECTOR_BITS; ++ } ++ ++ ret = get_sysfs_long_val(st, "physical_block_size"); ++ if (ret >= 0) { ++ bs->bl.write_granularity = ret; ++ } ++ ++ /* The refresh_limits() function can be called multiple times. */ ++ g_free(bs->wps); ++ bs->wps = g_malloc(sizeof(BlockZoneWps) + ++ sizeof(int64_t) * bs->bl.nr_zones); ++ ret = get_zones_wp(bs, s->fd, 0, bs->bl.nr_zones, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "report wps failed"); ++ goto no_zoned; ++ } ++ qemu_co_mutex_init(&bs->wps->colock); ++ return; ++ ++no_zoned: ++ bs->bl.zoned = BLK_Z_NONE; ++ g_free(bs->wps); ++ bs->wps = NULL; ++} ++#else /* !defined(CONFIG_BLKZONED) */ ++static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st, ++ Error **errp) ++{ ++ bs->bl.zoned = BLK_Z_NONE; ++} ++#endif /* !defined(CONFIG_BLKZONED) */ ++ ++static void raw_refresh_limits(BlockDriverState *bs, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ struct stat st; ++ ++ s->needs_alignment = raw_needs_alignment(bs); ++ raw_probe_alignment(bs, s->fd, errp); ++ ++ bs->bl.min_mem_alignment = s->buf_align; ++ bs->bl.opt_mem_alignment = MAX(s->buf_align, qemu_real_host_page_size()); ++ ++ /* ++ * Maximum transfers are best effort, so it is okay to ignore any ++ * errors. That said, based on the man page errors in fstat would be ++ * very much unexpected; the only possible case seems to be ENOMEM. ++ */ ++ if (fstat(s->fd, &st)) { ++ return; ++ } ++ ++#if defined(__APPLE__) && (__MACH__) ++ struct statfs buf; ++ ++ if (!fstatfs(s->fd, &buf)) { ++ bs->bl.opt_transfer = buf.f_iosize; ++ bs->bl.pdiscard_alignment = buf.f_bsize; ++ } ++#endif ++ ++ if (bdrv_is_sg(bs) || S_ISBLK(st.st_mode)) { ++ int ret = hdev_get_max_hw_transfer(s->fd, &st); ++ ++ if (ret > 0 && ret <= BDRV_REQUEST_MAX_BYTES) { ++ bs->bl.max_hw_transfer = ret; ++ } ++ ++ ret = hdev_get_max_segments(s->fd, &st); ++ if (ret > 0) { ++ bs->bl.max_hw_iov = ret; ++ } ++ } ++ ++ raw_refresh_zoned_limits(bs, &st, errp); ++} ++ ++static int check_for_dasd(int fd) ++{ ++#ifdef BIODASDINFO2 ++ struct dasd_information2_t info = {0}; ++ ++ return ioctl(fd, BIODASDINFO2, &info); ++#else ++ return -1; ++#endif ++} ++ ++/** ++ * Try to get @bs's logical and physical block size. ++ * On success, store them in @bsz and return zero. ++ * On failure, return negative errno. ++ */ ++static int hdev_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++ /* If DASD or zoned devices, get blocksizes */ ++ if (check_for_dasd(s->fd) < 0) { ++ /* zoned devices are not DASD */ ++ if (bs->bl.zoned == BLK_Z_NONE) { ++ return -ENOTSUP; ++ } ++ } ++ ret = probe_logical_blocksize(s->fd, &bsz->log); ++ if (ret < 0) { ++ return ret; ++ } ++ return probe_physical_blocksize(s->fd, &bsz->phys); ++} ++ ++/** ++ * Try to get @bs's geometry: cyls, heads, sectors. ++ * On success, store them in @geo and return 0. ++ * On failure return -errno. ++ * (Allows block driver to assign default geometry values that guest sees) ++ */ ++#ifdef __linux__ ++static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) ++{ ++ BDRVRawState *s = bs->opaque; ++ struct hd_geometry ioctl_geo = {0}; ++ ++ /* If DASD, get its geometry */ ++ if (check_for_dasd(s->fd) < 0) { ++ return -ENOTSUP; ++ } ++ if (ioctl(s->fd, HDIO_GETGEO, &ioctl_geo) < 0) { ++ return -errno; ++ } ++ /* HDIO_GETGEO may return success even though geo contains zeros ++ (e.g. certain multipath setups) */ ++ if (!ioctl_geo.heads || !ioctl_geo.sectors || !ioctl_geo.cylinders) { ++ return -ENOTSUP; ++ } ++ /* Do not return a geometry for partition */ ++ if (ioctl_geo.start != 0) { ++ return -ENOTSUP; ++ } ++ geo->heads = ioctl_geo.heads; ++ geo->sectors = ioctl_geo.sectors; ++ geo->cylinders = ioctl_geo.cylinders; ++ ++ return 0; ++} ++#else /* __linux__ */ ++static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) ++{ ++ return -ENOTSUP; ++} ++#endif ++ ++#if defined(__linux__) ++static int handle_aiocb_ioctl(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ int ret; ++ ++ ret = RETRY_ON_EINTR( ++ ioctl(aiocb->aio_fildes, aiocb->ioctl.cmd, aiocb->ioctl.buf) ++ ); ++ if (ret == -1) { ++ return -errno; ++ } ++ ++ return 0; ++} ++#endif /* linux */ ++ ++static int handle_aiocb_flush(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ BDRVRawState *s = aiocb->bs->opaque; ++ int ret; ++ ++ if (s->page_cache_inconsistent) { ++ return -s->page_cache_inconsistent; ++ } ++ ++ ret = qemu_fdatasync(aiocb->aio_fildes); ++ if (ret == -1) { ++ trace_file_flush_fdatasync_failed(errno); ++ ++ /* There is no clear definition of the semantics of a failing fsync(), ++ * so we may have to assume the worst. The sad truth is that this ++ * assumption is correct for Linux. Some pages are now probably marked ++ * clean in the page cache even though they are inconsistent with the ++ * on-disk contents. The next fdatasync() call would succeed, but no ++ * further writeback attempt will be made. We can't get back to a state ++ * in which we know what is on disk (we would have to rewrite ++ * everything that was touched since the last fdatasync() at least), so ++ * make bdrv_flush() fail permanently. Given that the behaviour isn't ++ * really defined, I have little hope that other OSes are doing better. ++ * ++ * Obviously, this doesn't affect O_DIRECT, which bypasses the page ++ * cache. */ ++ if ((s->open_flags & O_DIRECT) == 0) { ++ s->page_cache_inconsistent = errno; ++ } ++ return -errno; ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_PREADV ++ ++static bool preadv_present = true; ++ ++static ssize_t ++qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset) ++{ ++ return preadv(fd, iov, nr_iov, offset); ++} ++ ++static ssize_t ++qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset) ++{ ++ return pwritev(fd, iov, nr_iov, offset); ++} ++ ++#else ++ ++static bool preadv_present = false; ++ ++static ssize_t ++qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset) ++{ ++ return -ENOSYS; ++} ++ ++static ssize_t ++qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset) ++{ ++ return -ENOSYS; ++} ++ ++#endif ++ ++static ssize_t handle_aiocb_rw_vector(RawPosixAIOData *aiocb) ++{ ++ ssize_t len; ++ ++ len = RETRY_ON_EINTR( ++ (aiocb->aio_type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) ? ++ qemu_pwritev(aiocb->aio_fildes, ++ aiocb->io.iov, ++ aiocb->io.niov, ++ aiocb->aio_offset) : ++ qemu_preadv(aiocb->aio_fildes, ++ aiocb->io.iov, ++ aiocb->io.niov, ++ aiocb->aio_offset) ++ ); ++ ++ if (len == -1) { ++ return -errno; ++ } ++ return len; ++} ++ ++/* ++ * Read/writes the data to/from a given linear buffer. ++ * ++ * Returns the number of bytes handles or -errno in case of an error. Short ++ * reads are only returned if the end of the file is reached. ++ */ ++static ssize_t handle_aiocb_rw_linear(RawPosixAIOData *aiocb, char *buf) ++{ ++ ssize_t offset = 0; ++ ssize_t len; ++ ++ while (offset < aiocb->aio_nbytes) { ++ if (aiocb->aio_type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) { ++ len = pwrite(aiocb->aio_fildes, ++ (const char *)buf + offset, ++ aiocb->aio_nbytes - offset, ++ aiocb->aio_offset + offset); ++ } else { ++ len = pread(aiocb->aio_fildes, ++ buf + offset, ++ aiocb->aio_nbytes - offset, ++ aiocb->aio_offset + offset); ++ } ++ if (len == -1 && errno == EINTR) { ++ continue; ++ } else if (len == -1 && errno == EINVAL && ++ (aiocb->bs->open_flags & BDRV_O_NOCACHE) && ++ !(aiocb->aio_type & QEMU_AIO_WRITE) && ++ offset > 0) { ++ /* O_DIRECT pread() may fail with EINVAL when offset is unaligned ++ * after a short read. Assume that O_DIRECT short reads only occur ++ * at EOF. Therefore this is a short read, not an I/O error. ++ */ ++ break; ++ } else if (len == -1) { ++ offset = -errno; ++ break; ++ } else if (len == 0) { ++ break; ++ } ++ offset += len; ++ } ++ ++ return offset; ++} ++ ++static int handle_aiocb_rw(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ ssize_t nbytes; ++ char *buf; ++ ++ if (!(aiocb->aio_type & QEMU_AIO_MISALIGNED)) { ++ /* ++ * If there is just a single buffer, and it is properly aligned ++ * we can just use plain pread/pwrite without any problems. ++ */ ++ if (aiocb->io.niov == 1) { ++ nbytes = handle_aiocb_rw_linear(aiocb, aiocb->io.iov->iov_base); ++ goto out; ++ } ++ /* ++ * We have more than one iovec, and all are properly aligned. ++ * ++ * Try preadv/pwritev first and fall back to linearizing the ++ * buffer if it's not supported. ++ */ ++ if (preadv_present) { ++ nbytes = handle_aiocb_rw_vector(aiocb); ++ if (nbytes == aiocb->aio_nbytes || ++ (nbytes < 0 && nbytes != -ENOSYS)) { ++ goto out; ++ } ++ preadv_present = false; ++ } ++ ++ /* ++ * XXX(hch): short read/write. no easy way to handle the reminder ++ * using these interfaces. For now retry using plain ++ * pread/pwrite? ++ */ ++ } ++ ++ /* ++ * Ok, we have to do it the hard way, copy all segments into ++ * a single aligned buffer. ++ */ ++ buf = qemu_try_blockalign(aiocb->bs, aiocb->aio_nbytes); ++ if (buf == NULL) { ++ nbytes = -ENOMEM; ++ goto out; ++ } ++ ++ if (aiocb->aio_type & QEMU_AIO_WRITE) { ++ char *p = buf; ++ int i; ++ ++ for (i = 0; i < aiocb->io.niov; ++i) { ++ memcpy(p, aiocb->io.iov[i].iov_base, aiocb->io.iov[i].iov_len); ++ p += aiocb->io.iov[i].iov_len; ++ } ++ assert(p - buf == aiocb->aio_nbytes); ++ } ++ ++ nbytes = handle_aiocb_rw_linear(aiocb, buf); ++ if (!(aiocb->aio_type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND))) { ++ char *p = buf; ++ size_t count = aiocb->aio_nbytes, copy; ++ int i; ++ ++ for (i = 0; i < aiocb->io.niov && count; ++i) { ++ copy = count; ++ if (copy > aiocb->io.iov[i].iov_len) { ++ copy = aiocb->io.iov[i].iov_len; ++ } ++ memcpy(aiocb->io.iov[i].iov_base, p, copy); ++ assert(count >= copy); ++ p += copy; ++ count -= copy; ++ } ++ assert(count == 0); ++ } ++ qemu_vfree(buf); ++ ++out: ++ if (nbytes == aiocb->aio_nbytes) { ++ return 0; ++ } else if (nbytes >= 0 && nbytes < aiocb->aio_nbytes) { ++ if (aiocb->aio_type & QEMU_AIO_WRITE) { ++ return -EINVAL; ++ } else { ++ iov_memset(aiocb->io.iov, aiocb->io.niov, nbytes, ++ 0, aiocb->aio_nbytes - nbytes); ++ return 0; ++ } ++ } else { ++ assert(nbytes < 0); ++ return nbytes; ++ } ++} ++ ++#if defined(CONFIG_FALLOCATE) || defined(BLKZEROOUT) || defined(BLKDISCARD) ++static int translate_err(int err) ++{ ++ if (err == -ENODEV || err == -ENOSYS || err == -EOPNOTSUPP || ++ err == -ENOTTY) { ++ err = -ENOTSUP; ++ } ++ return err; ++} ++#endif ++ ++#ifdef CONFIG_FALLOCATE ++static int do_fallocate(int fd, int mode, off_t offset, off_t len) ++{ ++ do { ++ if (fallocate(fd, mode, offset, len) == 0) { ++ return 0; ++ } ++ } while (errno == EINTR); ++ return translate_err(-errno); ++} ++#endif ++ ++static ssize_t handle_aiocb_write_zeroes_block(RawPosixAIOData *aiocb) ++{ ++ int ret = -ENOTSUP; ++ BDRVRawState *s = aiocb->bs->opaque; ++ ++ if (!s->has_write_zeroes) { ++ return -ENOTSUP; ++ } ++ ++#ifdef BLKZEROOUT ++ /* The BLKZEROOUT implementation in the kernel doesn't set ++ * BLKDEV_ZERO_NOFALLBACK, so we can't call this if we have to avoid slow ++ * fallbacks. */ ++ if (!(aiocb->aio_type & QEMU_AIO_NO_FALLBACK)) { ++ do { ++ uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes }; ++ if (ioctl(aiocb->aio_fildes, BLKZEROOUT, range) == 0) { ++ return 0; ++ } ++ } while (errno == EINTR); ++ ++ ret = translate_err(-errno); ++ if (ret == -ENOTSUP) { ++ s->has_write_zeroes = false; ++ } ++ } ++#endif ++ ++ return ret; ++} ++ ++static int handle_aiocb_write_zeroes(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++#ifdef CONFIG_FALLOCATE ++ BDRVRawState *s = aiocb->bs->opaque; ++ int64_t len; ++#endif ++ ++ if (aiocb->aio_type & QEMU_AIO_BLKDEV) { ++ return handle_aiocb_write_zeroes_block(aiocb); ++ } ++ ++#ifdef CONFIG_FALLOCATE_ZERO_RANGE ++ if (s->has_write_zeroes) { ++ int ret = do_fallocate(s->fd, FALLOC_FL_ZERO_RANGE, ++ aiocb->aio_offset, aiocb->aio_nbytes); ++ if (ret == -ENOTSUP) { ++ s->has_write_zeroes = false; ++ } else if (ret == 0 || ret != -EINVAL) { ++ return ret; ++ } ++ /* ++ * Note: Some file systems do not like unaligned byte ranges, and ++ * return EINVAL in such a case, though they should not do it according ++ * to the man-page of fallocate(). Thus we simply ignore this return ++ * value and try the other fallbacks instead. ++ */ ++ } ++#endif ++ ++#ifdef CONFIG_FALLOCATE_PUNCH_HOLE ++ if (s->has_discard && s->has_fallocate) { ++ int ret = do_fallocate(s->fd, ++ FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, ++ aiocb->aio_offset, aiocb->aio_nbytes); ++ if (ret == 0) { ++ ret = do_fallocate(s->fd, 0, aiocb->aio_offset, aiocb->aio_nbytes); ++ if (ret == 0 || ret != -ENOTSUP) { ++ return ret; ++ } ++ s->has_fallocate = false; ++ } else if (ret == -EINVAL) { ++ /* ++ * Some file systems like older versions of GPFS do not like un- ++ * aligned byte ranges, and return EINVAL in such a case, though ++ * they should not do it according to the man-page of fallocate(). ++ * Warn about the bad filesystem and try the final fallback instead. ++ */ ++ warn_report_once("Your file system is misbehaving: " ++ "fallocate(FALLOC_FL_PUNCH_HOLE) returned EINVAL. " ++ "Please report this bug to your file system " ++ "vendor."); ++ } else if (ret != -ENOTSUP) { ++ return ret; ++ } else { ++ s->has_discard = false; ++ } ++ } ++#endif ++ ++#ifdef CONFIG_FALLOCATE ++ /* Last resort: we are trying to extend the file with zeroed data. This ++ * can be done via fallocate(fd, 0) */ ++ len = raw_getlength(aiocb->bs); ++ if (s->has_fallocate && len >= 0 && aiocb->aio_offset >= len) { ++ int ret = do_fallocate(s->fd, 0, aiocb->aio_offset, aiocb->aio_nbytes); ++ if (ret == 0 || ret != -ENOTSUP) { ++ return ret; ++ } ++ s->has_fallocate = false; ++ } ++#endif ++ ++ return -ENOTSUP; ++} ++ ++static int handle_aiocb_write_zeroes_unmap(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ BDRVRawState *s G_GNUC_UNUSED = aiocb->bs->opaque; ++ ++ /* First try to write zeros and unmap at the same time */ ++ ++#ifdef CONFIG_FALLOCATE_PUNCH_HOLE ++ int ret = do_fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, ++ aiocb->aio_offset, aiocb->aio_nbytes); ++ switch (ret) { ++ case -ENOTSUP: ++ case -EINVAL: ++ case -EBUSY: ++ break; ++ default: ++ return ret; ++ } ++#endif ++ ++ /* If we couldn't manage to unmap while guaranteed that the area reads as ++ * all-zero afterwards, just write zeroes without unmapping */ ++ return handle_aiocb_write_zeroes(aiocb); ++} ++ ++#ifndef HAVE_COPY_FILE_RANGE ++static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, ++ off_t *out_off, size_t len, unsigned int flags) ++{ ++#ifdef __NR_copy_file_range ++ return syscall(__NR_copy_file_range, in_fd, in_off, out_fd, ++ out_off, len, flags); ++#else ++ errno = ENOSYS; ++ return -1; ++#endif ++} ++#endif ++ ++/* ++ * parse_zone - Fill a zone descriptor ++ */ ++#if defined(CONFIG_BLKZONED) ++static inline int parse_zone(struct BlockZoneDescriptor *zone, ++ const struct blk_zone *blkz) { ++ zone->start = blkz->start << BDRV_SECTOR_BITS; ++ zone->length = blkz->len << BDRV_SECTOR_BITS; ++ zone->wp = blkz->wp << BDRV_SECTOR_BITS; ++ ++#ifdef HAVE_BLK_ZONE_REP_CAPACITY ++ zone->cap = blkz->capacity << BDRV_SECTOR_BITS; ++#else ++ zone->cap = blkz->len << BDRV_SECTOR_BITS; ++#endif ++ ++ switch (blkz->type) { ++ case BLK_ZONE_TYPE_SEQWRITE_REQ: ++ zone->type = BLK_ZT_SWR; ++ break; ++ case BLK_ZONE_TYPE_SEQWRITE_PREF: ++ zone->type = BLK_ZT_SWP; ++ break; ++ case BLK_ZONE_TYPE_CONVENTIONAL: ++ zone->type = BLK_ZT_CONV; ++ break; ++ default: ++ error_report("Unsupported zone type: 0x%x", blkz->type); ++ return -ENOTSUP; ++ } ++ ++ switch (blkz->cond) { ++ case BLK_ZONE_COND_NOT_WP: ++ zone->state = BLK_ZS_NOT_WP; ++ break; ++ case BLK_ZONE_COND_EMPTY: ++ zone->state = BLK_ZS_EMPTY; ++ break; ++ case BLK_ZONE_COND_IMP_OPEN: ++ zone->state = BLK_ZS_IOPEN; ++ break; ++ case BLK_ZONE_COND_EXP_OPEN: ++ zone->state = BLK_ZS_EOPEN; ++ break; ++ case BLK_ZONE_COND_CLOSED: ++ zone->state = BLK_ZS_CLOSED; ++ break; ++ case BLK_ZONE_COND_READONLY: ++ zone->state = BLK_ZS_RDONLY; ++ break; ++ case BLK_ZONE_COND_FULL: ++ zone->state = BLK_ZS_FULL; ++ break; ++ case BLK_ZONE_COND_OFFLINE: ++ zone->state = BLK_ZS_OFFLINE; ++ break; ++ default: ++ error_report("Unsupported zone state: 0x%x", blkz->cond); ++ return -ENOTSUP; ++ } ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_BLKZONED) ++static int handle_aiocb_zone_report(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ int fd = aiocb->aio_fildes; ++ unsigned int *nr_zones = aiocb->zone_report.nr_zones; ++ BlockZoneDescriptor *zones = aiocb->zone_report.zones; ++ /* zoned block devices use 512-byte sectors */ ++ uint64_t sector = aiocb->aio_offset / 512; ++ ++ struct blk_zone *blkz; ++ size_t rep_size; ++ unsigned int nrz; ++ int ret; ++ unsigned int n = 0, i = 0; ++ ++ nrz = *nr_zones; ++ rep_size = sizeof(struct blk_zone_report) + nrz * sizeof(struct blk_zone); ++ g_autofree struct blk_zone_report *rep = NULL; ++ rep = g_malloc(rep_size); ++ ++ blkz = (struct blk_zone *)(rep + 1); ++ while (n < nrz) { ++ memset(rep, 0, rep_size); ++ rep->sector = sector; ++ rep->nr_zones = nrz - n; ++ ++ do { ++ ret = ioctl(fd, BLKREPORTZONE, rep); ++ } while (ret != 0 && errno == EINTR); ++ if (ret != 0) { ++ error_report("%d: ioctl BLKREPORTZONE at %" PRId64 " failed %d", ++ fd, sector, errno); ++ return -errno; ++ } ++ ++ if (!rep->nr_zones) { ++ break; ++ } ++ ++ for (i = 0; i < rep->nr_zones; i++, n++) { ++ ret = parse_zone(&zones[n], &blkz[i]); ++ if (ret != 0) { ++ return ret; ++ } ++ ++ /* The next report should start after the last zone reported */ ++ sector = blkz[i].start + blkz[i].len; ++ } ++ } ++ ++ *nr_zones = n; ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_BLKZONED) ++static int handle_aiocb_zone_mgmt(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ int fd = aiocb->aio_fildes; ++ uint64_t sector = aiocb->aio_offset / 512; ++ int64_t nr_sectors = aiocb->aio_nbytes / 512; ++ struct blk_zone_range range; ++ int ret; ++ ++ /* Execute the operation */ ++ range.sector = sector; ++ range.nr_sectors = nr_sectors; ++ do { ++ ret = ioctl(fd, aiocb->zone_mgmt.op, &range); ++ } while (ret != 0 && errno == EINTR); ++ ++ return ret < 0 ? -errno : ret; ++} ++#endif ++ ++static int handle_aiocb_copy_range(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ uint64_t bytes = aiocb->aio_nbytes; ++ off_t in_off = aiocb->aio_offset; ++ off_t out_off = aiocb->copy_range.aio_offset2; ++ ++ while (bytes) { ++ ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off, ++ aiocb->copy_range.aio_fd2, &out_off, ++ bytes, 0); ++ trace_file_copy_file_range(aiocb->bs, aiocb->aio_fildes, in_off, ++ aiocb->copy_range.aio_fd2, out_off, bytes, ++ 0, ret); ++ if (ret == 0) { ++ /* No progress (e.g. when beyond EOF), let the caller fall back to ++ * buffer I/O. */ ++ return -ENOSPC; ++ } ++ if (ret < 0) { ++ switch (errno) { ++ case ENOSYS: ++ return -ENOTSUP; ++ case EINTR: ++ continue; ++ default: ++ return -errno; ++ } ++ } ++ bytes -= ret; ++ } ++ return 0; ++} ++ ++static int handle_aiocb_discard(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ int ret = -ENOTSUP; ++ BDRVRawState *s = aiocb->bs->opaque; ++ ++ if (!s->has_discard) { ++ return -ENOTSUP; ++ } ++ ++ if (aiocb->aio_type & QEMU_AIO_BLKDEV) { ++#ifdef BLKDISCARD ++ do { ++ uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes }; ++ if (ioctl(aiocb->aio_fildes, BLKDISCARD, range) == 0) { ++ return 0; ++ } ++ } while (errno == EINTR); ++ ++ ret = translate_err(-errno); ++#endif ++ } else { ++#ifdef CONFIG_FALLOCATE_PUNCH_HOLE ++ ret = do_fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, ++ aiocb->aio_offset, aiocb->aio_nbytes); ++ ret = translate_err(ret); ++#elif defined(__APPLE__) && (__MACH__) ++ fpunchhole_t fpunchhole; ++ fpunchhole.fp_flags = 0; ++ fpunchhole.reserved = 0; ++ fpunchhole.fp_offset = aiocb->aio_offset; ++ fpunchhole.fp_length = aiocb->aio_nbytes; ++ if (fcntl(s->fd, F_PUNCHHOLE, &fpunchhole) == -1) { ++ ret = errno == ENODEV ? -ENOTSUP : -errno; ++ } else { ++ ret = 0; ++ } ++#endif ++ } ++ ++ if (ret == -ENOTSUP) { ++ s->has_discard = false; ++ } ++ return ret; ++} ++ ++/* ++ * Help alignment probing by allocating the first block. ++ * ++ * When reading with direct I/O from unallocated area on Gluster backed by XFS, ++ * reading succeeds regardless of request length. In this case we fallback to ++ * safe alignment which is not optimal. Allocating the first block avoids this ++ * fallback. ++ * ++ * fd may be opened with O_DIRECT, but we don't know the buffer alignment or ++ * request alignment, so we use safe values. ++ * ++ * Returns: 0 on success, -errno on failure. Since this is an optimization, ++ * caller may ignore failures. ++ */ ++static int allocate_first_block(int fd, size_t max_size) ++{ ++ size_t write_size = (max_size < MAX_BLOCKSIZE) ++ ? BDRV_SECTOR_SIZE ++ : MAX_BLOCKSIZE; ++ size_t max_align = MAX(MAX_BLOCKSIZE, qemu_real_host_page_size()); ++ void *buf; ++ ssize_t n; ++ int ret; ++ ++ buf = qemu_memalign(max_align, write_size); ++ memset(buf, 0, write_size); ++ ++ n = RETRY_ON_EINTR(pwrite(fd, buf, write_size, 0)); ++ ++ ret = (n == -1) ? -errno : 0; ++ ++ qemu_vfree(buf); ++ return ret; ++} ++ ++static int handle_aiocb_truncate(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ int result = 0; ++ int64_t current_length = 0; ++ char *buf = NULL; ++ struct stat st; ++ int fd = aiocb->aio_fildes; ++ int64_t offset = aiocb->aio_offset; ++ PreallocMode prealloc = aiocb->truncate.prealloc; ++ Error **errp = aiocb->truncate.errp; ++ ++ if (fstat(fd, &st) < 0) { ++ result = -errno; ++ error_setg_errno(errp, -result, "Could not stat file"); ++ return result; ++ } ++ ++ current_length = st.st_size; ++ if (current_length > offset && prealloc != PREALLOC_MODE_OFF) { ++ error_setg(errp, "Cannot use preallocation for shrinking files"); ++ return -ENOTSUP; ++ } ++ ++ switch (prealloc) { ++#ifdef CONFIG_POSIX_FALLOCATE ++ case PREALLOC_MODE_FALLOC: ++ /* ++ * Truncating before posix_fallocate() makes it about twice slower on ++ * file systems that do not support fallocate(), trying to check if a ++ * block is allocated before allocating it, so don't do that here. ++ */ ++ if (offset != current_length) { ++ result = -posix_fallocate(fd, current_length, ++ offset - current_length); ++ if (result != 0) { ++ /* posix_fallocate() doesn't set errno. */ ++ error_setg_errno(errp, -result, ++ "Could not preallocate new data"); ++ } else if (current_length == 0) { ++ /* ++ * posix_fallocate() uses fallocate() if the filesystem ++ * supports it, or fallback to manually writing zeroes. If ++ * fallocate() was used, unaligned reads from the fallocated ++ * area in raw_probe_alignment() will succeed, hence we need to ++ * allocate the first block. ++ * ++ * Optimize future alignment probing; ignore failures. ++ */ ++ allocate_first_block(fd, offset); ++ } ++ } else { ++ result = 0; ++ } ++ goto out; ++#endif ++ case PREALLOC_MODE_FULL: ++ { ++ int64_t num = 0, left = offset - current_length; ++ off_t seek_result; ++ ++ /* ++ * Knowing the final size from the beginning could allow the file ++ * system driver to do less allocations and possibly avoid ++ * fragmentation of the file. ++ */ ++ if (ftruncate(fd, offset) != 0) { ++ result = -errno; ++ error_setg_errno(errp, -result, "Could not resize file"); ++ goto out; ++ } ++ ++ buf = g_malloc0(65536); ++ ++ seek_result = lseek(fd, current_length, SEEK_SET); ++ if (seek_result < 0) { ++ result = -errno; ++ error_setg_errno(errp, -result, ++ "Failed to seek to the old end of file"); ++ goto out; ++ } ++ ++ while (left > 0) { ++ num = MIN(left, 65536); ++ result = write(fd, buf, num); ++ if (result < 0) { ++ if (errno == EINTR) { ++ continue; ++ } ++ result = -errno; ++ error_setg_errno(errp, -result, ++ "Could not write zeros for preallocation"); ++ goto out; ++ } ++ left -= result; ++ } ++ if (result >= 0) { ++ result = fsync(fd); ++ if (result < 0) { ++ result = -errno; ++ error_setg_errno(errp, -result, ++ "Could not flush file to disk"); ++ goto out; ++ } ++ } ++ goto out; ++ } ++ case PREALLOC_MODE_OFF: ++ if (ftruncate(fd, offset) != 0) { ++ result = -errno; ++ error_setg_errno(errp, -result, "Could not resize file"); ++ } else if (current_length == 0 && offset > current_length) { ++ /* Optimize future alignment probing; ignore failures. */ ++ allocate_first_block(fd, offset); ++ } ++ return result; ++ default: ++ result = -ENOTSUP; ++ error_setg(errp, "Unsupported preallocation mode: %s", ++ PreallocMode_str(prealloc)); ++ return result; ++ } ++ ++out: ++ if (result < 0) { ++ if (ftruncate(fd, current_length) < 0) { ++ error_report("Failed to restore old file length: %s", ++ strerror(errno)); ++ } ++ } ++ ++ g_free(buf); ++ return result; ++} ++ ++static int coroutine_fn raw_thread_pool_submit(ThreadPoolFunc func, void *arg) ++{ ++ return thread_pool_submit_co(func, arg); ++} ++ ++/* ++ * Check if all memory in this vector is sector aligned. ++ */ ++static bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) ++{ ++ int i; ++ size_t alignment = bdrv_min_mem_align(bs); ++ size_t len = bs->bl.request_alignment; ++ IO_CODE(); ++ ++ for (i = 0; i < qiov->niov; i++) { ++ if ((uintptr_t) qiov->iov[i].iov_base % alignment) { ++ return false; ++ } ++ if (qiov->iov[i].iov_len % len) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++#ifdef CONFIG_LINUX_IO_URING ++static inline bool raw_check_linux_io_uring(BDRVRawState *s) ++{ ++ Error *local_err = NULL; ++ AioContext *ctx; ++ ++ if (!s->use_linux_io_uring) { ++ return false; ++ } ++ ++ ctx = qemu_get_current_aio_context(); ++ if (unlikely(!aio_setup_linux_io_uring(ctx, &local_err))) { ++ error_reportf_err(local_err, "Unable to use linux io_uring, " ++ "falling back to thread pool: "); ++ s->use_linux_io_uring = false; ++ return false; ++ } ++ return true; ++} ++#endif ++ ++#ifdef CONFIG_LINUX_AIO ++static inline bool raw_check_linux_aio(BDRVRawState *s) ++{ ++ Error *local_err = NULL; ++ AioContext *ctx; ++ ++ if (!s->use_linux_aio) { ++ return false; ++ } ++ ++ ctx = qemu_get_current_aio_context(); ++ if (unlikely(!aio_setup_linux_aio(ctx, &local_err))) { ++ error_reportf_err(local_err, "Unable to use Linux AIO, " ++ "falling back to thread pool: "); ++ s->use_linux_aio = false; ++ return false; ++ } ++ return true; ++} ++#endif ++ ++static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, ++ uint64_t bytes, QEMUIOVector *qiov, int type) ++{ ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb; ++ int ret; ++ uint64_t offset = *offset_ptr; ++ ++ if (fd_open(bs) < 0) ++ return -EIO; ++#if defined(CONFIG_BLKZONED) ++ if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) && ++ bs->bl.zoned != BLK_Z_NONE) { ++ qemu_co_mutex_lock(&bs->wps->colock); ++ if (type & QEMU_AIO_ZONE_APPEND) { ++ int index = offset / bs->bl.zone_size; ++ offset = bs->wps->wp[index]; ++ } ++ } ++#endif ++ ++ /* ++ * When using O_DIRECT, the request must be aligned to be able to use ++ * either libaio or io_uring interface. If not fail back to regular thread ++ * pool read/write code which emulates this for us if we ++ * set QEMU_AIO_MISALIGNED. ++ */ ++ if (s->needs_alignment && !bdrv_qiov_is_aligned(bs, qiov)) { ++ type |= QEMU_AIO_MISALIGNED; ++#ifdef CONFIG_LINUX_IO_URING ++ } else if (raw_check_linux_io_uring(s)) { ++ assert(qiov->size == bytes); ++ ret = luring_co_submit(bs, s->fd, offset, qiov, type); ++ goto out; ++#endif ++#ifdef CONFIG_LINUX_AIO ++ } else if (raw_check_linux_aio(s)) { ++ assert(qiov->size == bytes); ++ ret = laio_co_submit(s->fd, offset, qiov, type, ++ s->aio_max_batch); ++ goto out; ++#endif ++ } ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = s->fd, ++ .aio_type = type, ++ .aio_offset = offset, ++ .aio_nbytes = bytes, ++ .io = { ++ .iov = qiov->iov, ++ .niov = qiov->niov, ++ }, ++ }; ++ ++ assert(qiov->size == bytes); ++ ret = raw_thread_pool_submit(handle_aiocb_rw, &acb); ++ goto out; /* Avoid the compiler err of unused label */ ++ ++out: ++#if defined(CONFIG_BLKZONED) ++ if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) && ++ bs->bl.zoned != BLK_Z_NONE) { ++ BlockZoneWps *wps = bs->wps; ++ if (ret == 0) { ++ uint64_t *wp = &wps->wp[offset / bs->bl.zone_size]; ++ if (!BDRV_ZT_IS_CONV(*wp)) { ++ if (type & QEMU_AIO_ZONE_APPEND) { ++ *offset_ptr = *wp; ++ trace_zbd_zone_append_complete(bs, *offset_ptr ++ >> BDRV_SECTOR_BITS); ++ } ++ /* Advance the wp if needed */ ++ if (offset + bytes > *wp) { ++ *wp = offset + bytes; ++ } ++ } ++ } else { ++ /* ++ * write and append write are not allowed to cross zone boundaries ++ */ ++ update_zones_wp(bs, s->fd, offset, 1); ++ } ++ ++ qemu_co_mutex_unlock(&wps->colock); ++ } ++#endif ++ return ret; ++} ++ ++static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_READ); ++} ++ ++static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_WRITE); ++} ++ ++static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb; ++ int ret; ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = s->fd, ++ .aio_type = QEMU_AIO_FLUSH, ++ }; ++ ++#ifdef CONFIG_LINUX_IO_URING ++ if (raw_check_linux_io_uring(s)) { ++ return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH); ++ } ++#endif ++#ifdef CONFIG_LINUX_AIO ++ if (s->has_laio_fdsync && raw_check_linux_aio(s)) { ++ return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0); ++ } ++#endif ++ return raw_thread_pool_submit(handle_aiocb_flush, &acb); ++} ++ ++static void raw_close(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (s->fd >= 0) { ++#if defined(CONFIG_BLKZONED) ++ g_free(bs->wps); ++#endif ++ qemu_close(s->fd); ++ s->fd = -1; ++ } ++} ++ ++/** ++ * Truncates the given regular file @fd to @offset and, when growing, fills the ++ * new space according to @prealloc. ++ * ++ * Returns: 0 on success, -errno on failure. ++ */ ++static int coroutine_fn ++raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset, ++ PreallocMode prealloc, Error **errp) ++{ ++ RawPosixAIOData acb; ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = fd, ++ .aio_type = QEMU_AIO_TRUNCATE, ++ .aio_offset = offset, ++ .truncate = { ++ .prealloc = prealloc, ++ .errp = errp, ++ }, ++ }; ++ ++ return raw_thread_pool_submit(handle_aiocb_truncate, &acb); ++} ++ ++static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, ++ bool exact, PreallocMode prealloc, ++ BdrvRequestFlags flags, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ struct stat st; ++ int ret; ++ ++ if (fstat(s->fd, &st)) { ++ ret = -errno; ++ error_setg_errno(errp, -ret, "Failed to fstat() the file"); ++ return ret; ++ } ++ ++ if (S_ISREG(st.st_mode)) { ++ /* Always resizes to the exact @offset */ ++ return raw_regular_truncate(bs, s->fd, offset, prealloc, errp); ++ } ++ ++ if (prealloc != PREALLOC_MODE_OFF) { ++ error_setg(errp, "Preallocation mode '%s' unsupported for this " ++ "non-regular file", PreallocMode_str(prealloc)); ++ return -ENOTSUP; ++ } ++ ++ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { ++ int64_t cur_length = raw_getlength(bs); ++ ++ if (offset != cur_length && exact) { ++ error_setg(errp, "Cannot resize device files"); ++ return -ENOTSUP; ++ } else if (offset > cur_length) { ++ error_setg(errp, "Cannot grow device files"); ++ return -EINVAL; ++ } ++ } else { ++ error_setg(errp, "Resizing this file is not supported"); ++ return -ENOTSUP; ++ } ++ ++ return 0; ++} ++ ++#ifdef __OpenBSD__ ++static int64_t raw_getlength(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ int fd = s->fd; ++ struct stat st; ++ ++ if (fstat(fd, &st)) ++ return -errno; ++ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { ++ struct disklabel dl; ++ ++ if (ioctl(fd, DIOCGDINFO, &dl)) ++ return -errno; ++ return (uint64_t)dl.d_secsize * ++ dl.d_partitions[DISKPART(st.st_rdev)].p_size; ++ } else ++ return st.st_size; ++} ++#elif defined(__NetBSD__) ++static int64_t raw_getlength(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ int fd = s->fd; ++ struct stat st; ++ ++ if (fstat(fd, &st)) ++ return -errno; ++ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { ++ struct dkwedge_info dkw; ++ ++ if (ioctl(fd, DIOCGWEDGEINFO, &dkw) != -1) { ++ return dkw.dkw_size * 512; ++ } else { ++ struct disklabel dl; ++ ++ if (ioctl(fd, DIOCGDINFO, &dl)) ++ return -errno; ++ return (uint64_t)dl.d_secsize * ++ dl.d_partitions[DISKPART(st.st_rdev)].p_size; ++ } ++ } else ++ return st.st_size; ++} ++#elif defined(__sun__) ++static int64_t raw_getlength(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ struct dk_minfo minfo; ++ int ret; ++ int64_t size; ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* ++ * Use the DKIOCGMEDIAINFO ioctl to read the size. ++ */ ++ ret = ioctl(s->fd, DKIOCGMEDIAINFO, &minfo); ++ if (ret != -1) { ++ return minfo.dki_lbsize * minfo.dki_capacity; ++ } ++ ++ /* ++ * There are reports that lseek on some devices fails, but ++ * irc discussion said that contingency on contingency was overkill. ++ */ ++ size = lseek(s->fd, 0, SEEK_END); ++ if (size < 0) { ++ return -errno; ++ } ++ return size; ++} ++#elif defined(CONFIG_BSD) ++static int64_t raw_getlength(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ int fd = s->fd; ++ int64_t size; ++ struct stat sb; ++#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) ++ int reopened = 0; ++#endif ++ int ret; ++ ++ ret = fd_open(bs); ++ if (ret < 0) ++ return ret; ++ ++#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) ++again: ++#endif ++ if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) { ++ size = 0; ++#ifdef DIOCGMEDIASIZE ++ if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size)) { ++ size = 0; ++ } ++#endif ++#ifdef DIOCGPART ++ if (size == 0) { ++ struct partinfo pi; ++ if (ioctl(fd, DIOCGPART, &pi) == 0) { ++ size = pi.media_size; ++ } ++ } ++#endif ++#if defined(DKIOCGETBLOCKCOUNT) && defined(DKIOCGETBLOCKSIZE) ++ if (size == 0) { ++ uint64_t sectors = 0; ++ uint32_t sector_size = 0; ++ ++ if (ioctl(fd, DKIOCGETBLOCKCOUNT, §ors) == 0 ++ && ioctl(fd, DKIOCGETBLOCKSIZE, §or_size) == 0) { ++ size = sectors * sector_size; ++ } ++ } ++#endif ++ if (size == 0) { ++ size = lseek(fd, 0LL, SEEK_END); ++ } ++ if (size < 0) { ++ return -errno; ++ } ++#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) ++ switch(s->type) { ++ case FTYPE_CD: ++ /* XXX FreeBSD acd returns UINT_MAX sectors for an empty drive */ ++ if (size == 2048LL * (unsigned)-1) ++ size = 0; ++ /* XXX no disc? maybe we need to reopen... */ ++ if (size <= 0 && !reopened && cdrom_reopen(bs) >= 0) { ++ reopened = 1; ++ goto again; ++ } ++ } ++#endif ++ } else { ++ size = lseek(fd, 0, SEEK_END); ++ if (size < 0) { ++ return -errno; ++ } ++ } ++ return size; ++} ++#else ++static int64_t raw_getlength(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ int64_t size; ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ size = lseek(s->fd, 0, SEEK_END); ++ if (size < 0) { ++ return -errno; ++ } ++ return size; ++} ++#endif ++ ++static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs) ++{ ++ return raw_getlength(bs); ++} ++ ++static int64_t coroutine_fn raw_co_get_allocated_file_size(BlockDriverState *bs) ++{ ++ struct stat st; ++ BDRVRawState *s = bs->opaque; ++ ++ if (fstat(s->fd, &st) < 0) { ++ return -errno; ++ } ++ return (int64_t)st.st_blocks * 512; ++} ++ ++static int coroutine_fn ++raw_co_create(BlockdevCreateOptions *options, Error **errp) ++{ ++ BlockdevCreateOptionsFile *file_opts; ++ Error *local_err = NULL; ++ int fd; ++ uint64_t perm, shared; ++ int result = 0; ++ ++ /* Validate options and set default values */ ++ assert(options->driver == BLOCKDEV_DRIVER_FILE); ++ file_opts = &options->u.file; ++ ++ if (!file_opts->has_nocow) { ++ file_opts->nocow = false; ++ } ++ if (!file_opts->has_preallocation) { ++ file_opts->preallocation = PREALLOC_MODE_OFF; ++ } ++ if (!file_opts->has_extent_size_hint) { ++ file_opts->extent_size_hint = 1 * MiB; ++ } ++ if (file_opts->extent_size_hint > UINT32_MAX) { ++ result = -EINVAL; ++ error_setg(errp, "Extent size hint is too large"); ++ goto out; ++ } ++ ++ /* Create file */ ++ fd = qemu_create(file_opts->filename, O_RDWR | O_BINARY, 0644, errp); ++ if (fd < 0) { ++ result = -errno; ++ goto out; ++ } ++ ++ /* Take permissions: We want to discard everything, so we need ++ * BLK_PERM_WRITE; and truncation to the desired size requires ++ * BLK_PERM_RESIZE. ++ * On the other hand, we cannot share the RESIZE permission ++ * because we promise that after this function, the file has the ++ * size given in the options. If someone else were to resize it ++ * concurrently, we could not guarantee that. ++ * Note that after this function, we can no longer guarantee that ++ * the file is not touched by a third party, so it may be resized ++ * then. */ ++ perm = BLK_PERM_WRITE | BLK_PERM_RESIZE; ++ shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; ++ ++ /* Step one: Take locks */ ++ result = raw_apply_lock_bytes(NULL, fd, perm, ~shared, false, errp); ++ if (result < 0) { ++ goto out_close; ++ } ++ ++ /* Step two: Check that nobody else has taken conflicting locks */ ++ result = raw_check_lock_bytes(fd, perm, shared, errp); ++ if (result < 0) { ++ error_append_hint(errp, ++ "Is another process using the image [%s]?\n", ++ file_opts->filename); ++ goto out_unlock; ++ } ++ ++ /* Clear the file by truncating it to 0 */ ++ result = raw_regular_truncate(NULL, fd, 0, PREALLOC_MODE_OFF, errp); ++ if (result < 0) { ++ goto out_unlock; ++ } ++ ++ if (file_opts->nocow) { ++#ifdef __linux__ ++ /* Set NOCOW flag to solve performance issue on fs like btrfs. ++ * This is an optimisation. The FS_IOC_SETFLAGS ioctl return value ++ * will be ignored since any failure of this operation should not ++ * block the left work. ++ */ ++ int attr; ++ if (ioctl(fd, FS_IOC_GETFLAGS, &attr) == 0) { ++ attr |= FS_NOCOW_FL; ++ ioctl(fd, FS_IOC_SETFLAGS, &attr); ++ } ++#endif ++ } ++#ifdef FS_IOC_FSSETXATTR ++ /* ++ * Try to set the extent size hint. Failure is not fatal, and a warning is ++ * only printed if the option was explicitly specified. ++ */ ++ { ++ struct fsxattr attr; ++ result = ioctl(fd, FS_IOC_FSGETXATTR, &attr); ++ if (result == 0) { ++ attr.fsx_xflags |= FS_XFLAG_EXTSIZE; ++ attr.fsx_extsize = file_opts->extent_size_hint; ++ result = ioctl(fd, FS_IOC_FSSETXATTR, &attr); ++ } ++ if (result < 0 && file_opts->has_extent_size_hint && ++ file_opts->extent_size_hint) ++ { ++ warn_report("Failed to set extent size hint: %s", ++ strerror(errno)); ++ } ++ } ++#endif ++ ++ /* Resize and potentially preallocate the file to the desired ++ * final size */ ++ result = raw_regular_truncate(NULL, fd, file_opts->size, ++ file_opts->preallocation, errp); ++ if (result < 0) { ++ goto out_unlock; ++ } ++ ++out_unlock: ++ raw_apply_lock_bytes(NULL, fd, 0, 0, true, &local_err); ++ if (local_err) { ++ /* The above call should not fail, and if it does, that does ++ * not mean the whole creation operation has failed. So ++ * report it the user for their convenience, but do not report ++ * it to the caller. */ ++ warn_report_err(local_err); ++ } ++ ++out_close: ++ if (qemu_close(fd) != 0 && result == 0) { ++ result = -errno; ++ error_setg_errno(errp, -result, "Could not close the new file"); ++ } ++out: ++ return result; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_create_opts(BlockDriver *drv, const char *filename, ++ QemuOpts *opts, Error **errp) ++{ ++ BlockdevCreateOptions options; ++ int64_t total_size = 0; ++ int64_t extent_size_hint = 0; ++ bool has_extent_size_hint = false; ++ bool nocow = false; ++ PreallocMode prealloc; ++ char *buf = NULL; ++ Error *local_err = NULL; ++ ++ /* Skip file: protocol prefix */ ++ strstart(filename, "file:", &filename); ++ ++ /* Read out options */ ++ total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), ++ BDRV_SECTOR_SIZE); ++ if (qemu_opt_get(opts, BLOCK_OPT_EXTENT_SIZE_HINT)) { ++ has_extent_size_hint = true; ++ extent_size_hint = ++ qemu_opt_get_size_del(opts, BLOCK_OPT_EXTENT_SIZE_HINT, -1); ++ } ++ nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false); ++ buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); ++ prealloc = qapi_enum_parse(&PreallocMode_lookup, buf, ++ PREALLOC_MODE_OFF, &local_err); ++ g_free(buf); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ ++ options = (BlockdevCreateOptions) { ++ .driver = BLOCKDEV_DRIVER_FILE, ++ .u.file = { ++ .filename = (char *) filename, ++ .size = total_size, ++ .has_preallocation = true, ++ .preallocation = prealloc, ++ .has_nocow = true, ++ .nocow = nocow, ++ .has_extent_size_hint = has_extent_size_hint, ++ .extent_size_hint = extent_size_hint, ++ }, ++ }; ++ return raw_co_create(&options, errp); ++} ++ ++static int coroutine_fn raw_co_delete_file(BlockDriverState *bs, ++ Error **errp) ++{ ++ struct stat st; ++ int ret; ++ ++ if (!(stat(bs->filename, &st) == 0) || !S_ISREG(st.st_mode)) { ++ error_setg_errno(errp, ENOENT, "%s is not a regular file", ++ bs->filename); ++ return -ENOENT; ++ } ++ ++ ret = unlink(bs->filename); ++ if (ret < 0) { ++ ret = -errno; ++ error_setg_errno(errp, -ret, "Error when deleting file %s", ++ bs->filename); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Find allocation range in @bs around offset @start. ++ * May change underlying file descriptor's file offset. ++ * If @start is not in a hole, store @start in @data, and the ++ * beginning of the next hole in @hole, and return 0. ++ * If @start is in a non-trailing hole, store @start in @hole and the ++ * beginning of the next non-hole in @data, and return 0. ++ * If @start is in a trailing hole or beyond EOF, return -ENXIO. ++ * If we can't find out, return a negative errno other than -ENXIO. ++ */ ++static int find_allocation(BlockDriverState *bs, off_t start, ++ off_t *data, off_t *hole) ++{ ++#if defined SEEK_HOLE && defined SEEK_DATA ++ BDRVRawState *s = bs->opaque; ++ off_t offs; ++ ++ /* ++ * SEEK_DATA cases: ++ * D1. offs == start: start is in data ++ * D2. offs > start: start is in a hole, next data at offs ++ * D3. offs < 0, errno = ENXIO: either start is in a trailing hole ++ * or start is beyond EOF ++ * If the latter happens, the file has been truncated behind ++ * our back since we opened it. All bets are off then. ++ * Treating like a trailing hole is simplest. ++ * D4. offs < 0, errno != ENXIO: we learned nothing ++ */ ++ offs = lseek(s->fd, start, SEEK_DATA); ++ if (offs < 0) { ++ return -errno; /* D3 or D4 */ ++ } ++ ++ if (offs < start) { ++ /* This is not a valid return by lseek(). We are safe to just return ++ * -EIO in this case, and we'll treat it like D4. */ ++ return -EIO; ++ } ++ ++ if (offs > start) { ++ /* D2: in hole, next data at offs */ ++ *hole = start; ++ *data = offs; ++ return 0; ++ } ++ ++ /* D1: in data, end not yet known */ ++ ++ /* ++ * SEEK_HOLE cases: ++ * H1. offs == start: start is in a hole ++ * If this happens here, a hole has been dug behind our back ++ * since the previous lseek(). ++ * H2. offs > start: either start is in data, next hole at offs, ++ * or start is in trailing hole, EOF at offs ++ * Linux treats trailing holes like any other hole: offs == ++ * start. Solaris seeks to EOF instead: offs > start (blech). ++ * If that happens here, a hole has been dug behind our back ++ * since the previous lseek(). ++ * H3. offs < 0, errno = ENXIO: start is beyond EOF ++ * If this happens, the file has been truncated behind our ++ * back since we opened it. Treat it like a trailing hole. ++ * H4. offs < 0, errno != ENXIO: we learned nothing ++ * Pretend we know nothing at all, i.e. "forget" about D1. ++ */ ++ offs = lseek(s->fd, start, SEEK_HOLE); ++ if (offs < 0) { ++ return -errno; /* D1 and (H3 or H4) */ ++ } ++ ++ if (offs < start) { ++ /* This is not a valid return by lseek(). We are safe to just return ++ * -EIO in this case, and we'll treat it like H4. */ ++ return -EIO; ++ } ++ ++ if (offs > start) { ++ /* ++ * D1 and H2: either in data, next hole at offs, or it was in ++ * data but is now in a trailing hole. In the latter case, ++ * all bets are off. Treating it as if it there was data all ++ * the way to EOF is safe, so simply do that. ++ */ ++ *data = start; ++ *hole = offs; ++ return 0; ++ } ++ ++ /* D1 and H1 */ ++ return -EBUSY; ++#else ++ return -ENOTSUP; ++#endif ++} ++ ++/* ++ * Returns the allocation status of the specified offset. ++ * ++ * The block layer guarantees 'offset' and 'bytes' are within bounds. ++ * ++ * 'pnum' is set to the number of bytes (including and immediately following ++ * the specified offset) that are known to be in the same ++ * allocated/unallocated state. ++ * ++ * 'bytes' is a soft cap for 'pnum'. If the information is free, 'pnum' may ++ * well exceed it. ++ */ ++static int coroutine_fn raw_co_block_status(BlockDriverState *bs, ++ bool want_zero, ++ int64_t offset, ++ int64_t bytes, int64_t *pnum, ++ int64_t *map, ++ BlockDriverState **file) ++{ ++ off_t data = 0, hole = 0; ++ int ret; ++ ++ assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment)); ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (!want_zero) { ++ *pnum = bytes; ++ *map = offset; ++ *file = bs; ++ return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; ++ } ++ ++ ret = find_allocation(bs, offset, &data, &hole); ++ if (ret == -ENXIO) { ++ /* Trailing hole */ ++ *pnum = bytes; ++ ret = BDRV_BLOCK_ZERO; ++ } else if (ret < 0) { ++ /* No info available, so pretend there are no holes */ ++ *pnum = bytes; ++ ret = BDRV_BLOCK_DATA; ++ } else if (data == offset) { ++ /* On a data extent, compute bytes to the end of the extent, ++ * possibly including a partial sector at EOF. */ ++ *pnum = hole - offset; ++ ++ /* ++ * We are not allowed to return partial sectors, though, so ++ * round up if necessary. ++ */ ++ if (!QEMU_IS_ALIGNED(*pnum, bs->bl.request_alignment)) { ++ int64_t file_length = raw_getlength(bs); ++ if (file_length > 0) { ++ /* Ignore errors, this is just a safeguard */ ++ assert(hole == file_length); ++ } ++ *pnum = ROUND_UP(*pnum, bs->bl.request_alignment); ++ } ++ ++ ret = BDRV_BLOCK_DATA; ++ } else { ++ /* On a hole, compute bytes to the beginning of the next extent. */ ++ assert(hole == offset); ++ *pnum = data - offset; ++ ret = BDRV_BLOCK_ZERO; ++ } ++ *map = offset; ++ *file = bs; ++ return ret | BDRV_BLOCK_OFFSET_VALID; ++} ++ ++#if defined(__linux__) ++/* Verify that the file is not in the page cache */ ++static void check_cache_dropped(BlockDriverState *bs, Error **errp) ++{ ++ const size_t window_size = 128 * 1024 * 1024; ++ BDRVRawState *s = bs->opaque; ++ void *window = NULL; ++ size_t length = 0; ++ unsigned char *vec; ++ size_t page_size; ++ off_t offset; ++ off_t end; ++ ++ /* mincore(2) page status information requires 1 byte per page */ ++ page_size = sysconf(_SC_PAGESIZE); ++ vec = g_malloc(DIV_ROUND_UP(window_size, page_size)); ++ ++ end = raw_getlength(bs); ++ ++ for (offset = 0; offset < end; offset += window_size) { ++ void *new_window; ++ size_t new_length; ++ size_t vec_end; ++ size_t i; ++ int ret; ++ ++ /* Unmap previous window if size has changed */ ++ new_length = MIN(end - offset, window_size); ++ if (new_length != length) { ++ munmap(window, length); ++ window = NULL; ++ length = 0; ++ } ++ ++ new_window = mmap(window, new_length, PROT_NONE, MAP_PRIVATE, ++ s->fd, offset); ++ if (new_window == MAP_FAILED) { ++ error_setg_errno(errp, errno, "mmap failed"); ++ break; ++ } ++ ++ window = new_window; ++ length = new_length; ++ ++ ret = mincore(window, length, vec); ++ if (ret < 0) { ++ error_setg_errno(errp, errno, "mincore failed"); ++ break; ++ } ++ ++ vec_end = DIV_ROUND_UP(length, page_size); ++ for (i = 0; i < vec_end; i++) { ++ if (vec[i] & 0x1) { ++ break; ++ } ++ } ++ if (i < vec_end) { ++ error_setg(errp, "page cache still in use!"); ++ break; ++ } ++ } ++ ++ if (window) { ++ munmap(window, length); ++ } ++ ++ g_free(vec); ++} ++#endif /* __linux__ */ ++ ++static void coroutine_fn GRAPH_RDLOCK ++raw_co_invalidate_cache(BlockDriverState *bs, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "The file descriptor is not open"); ++ return; ++ } ++ ++ if (!s->drop_cache) { ++ return; ++ } ++ ++ if (s->open_flags & O_DIRECT) { ++ return; /* No host kernel page cache */ ++ } ++ ++#if defined(__linux__) ++ /* This sets the scene for the next syscall... */ ++ ret = bdrv_co_flush(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "flush failed"); ++ return; ++ } ++ ++ /* Linux does not invalidate pages that are dirty, locked, or mmapped by a ++ * process. These limitations are okay because we just fsynced the file, ++ * we don't use mmap, and the file should not be in use by other processes. ++ */ ++ ret = posix_fadvise(s->fd, 0, 0, POSIX_FADV_DONTNEED); ++ if (ret != 0) { /* the return value is a positive errno */ ++ error_setg_errno(errp, ret, "fadvise failed"); ++ return; ++ } ++ ++ if (s->check_cache_dropped) { ++ check_cache_dropped(bs, errp); ++ } ++#else /* __linux__ */ ++ /* Do nothing. Live migration to a remote host with cache.direct=off is ++ * unsupported on other host operating systems. Cache consistency issues ++ * may occur but no error is reported here, partly because that's the ++ * historical behavior and partly because it's hard to differentiate valid ++ * configurations that should not cause errors. ++ */ ++#endif /* !__linux__ */ ++} ++ ++static void raw_account_discard(BDRVRawState *s, uint64_t nbytes, int ret) ++{ ++ if (ret) { ++ s->stats.discard_nb_failed++; ++ } else { ++ s->stats.discard_nb_ok++; ++ s->stats.discard_bytes_ok += nbytes; ++ } ++} ++ ++/* ++ * zone report - Get a zone block device's information in the form ++ * of an array of zone descriptors. ++ * zones is an array of zone descriptors to hold zone information on reply; ++ * offset can be any byte within the entire size of the device; ++ * nr_zones is the maximum number of sectors the command should operate on. ++ */ ++#if defined(CONFIG_BLKZONED) ++static int coroutine_fn raw_co_zone_report(BlockDriverState *bs, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones) { ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = s->fd, ++ .aio_type = QEMU_AIO_ZONE_REPORT, ++ .aio_offset = offset, ++ .zone_report = { ++ .nr_zones = nr_zones, ++ .zones = zones, ++ }, ++ }; ++ ++ trace_zbd_zone_report(bs, *nr_zones, offset >> BDRV_SECTOR_BITS); ++ return raw_thread_pool_submit(handle_aiocb_zone_report, &acb); ++} ++#endif ++ ++/* ++ * zone management operations - Execute an operation on a zone ++ */ ++#if defined(CONFIG_BLKZONED) ++static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, ++ int64_t offset, int64_t len) { ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb; ++ int64_t zone_size, zone_size_mask; ++ const char *op_name; ++ unsigned long zo; ++ int ret; ++ BlockZoneWps *wps = bs->wps; ++ int64_t capacity = bs->total_sectors << BDRV_SECTOR_BITS; ++ ++ zone_size = bs->bl.zone_size; ++ zone_size_mask = zone_size - 1; ++ if (offset & zone_size_mask) { ++ error_report("sector offset %" PRId64 " is not aligned to zone size " ++ "%" PRId64 "", offset / 512, zone_size / 512); ++ return -EINVAL; ++ } ++ ++ if (((offset + len) < capacity && len & zone_size_mask) || ++ offset + len > capacity) { ++ error_report("number of sectors %" PRId64 " is not aligned to zone size" ++ " %" PRId64 "", len / 512, zone_size / 512); ++ return -EINVAL; ++ } ++ ++ uint32_t i = offset / bs->bl.zone_size; ++ uint32_t nrz = len / bs->bl.zone_size; ++ uint64_t *wp = &wps->wp[i]; ++ if (BDRV_ZT_IS_CONV(*wp) && len != capacity) { ++ error_report("zone mgmt operations are not allowed for conventional zones"); ++ return -EIO; ++ } ++ ++ switch (op) { ++ case BLK_ZO_OPEN: ++ op_name = "BLKOPENZONE"; ++ zo = BLKOPENZONE; ++ break; ++ case BLK_ZO_CLOSE: ++ op_name = "BLKCLOSEZONE"; ++ zo = BLKCLOSEZONE; ++ break; ++ case BLK_ZO_FINISH: ++ op_name = "BLKFINISHZONE"; ++ zo = BLKFINISHZONE; ++ break; ++ case BLK_ZO_RESET: ++ op_name = "BLKRESETZONE"; ++ zo = BLKRESETZONE; ++ break; ++ default: ++ error_report("Unsupported zone op: 0x%x", op); ++ return -ENOTSUP; ++ } ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = s->fd, ++ .aio_type = QEMU_AIO_ZONE_MGMT, ++ .aio_offset = offset, ++ .aio_nbytes = len, ++ .zone_mgmt = { ++ .op = zo, ++ }, ++ }; ++ ++ trace_zbd_zone_mgmt(bs, op_name, offset >> BDRV_SECTOR_BITS, ++ len >> BDRV_SECTOR_BITS); ++ ret = raw_thread_pool_submit(handle_aiocb_zone_mgmt, &acb); ++ if (ret != 0) { ++ update_zones_wp(bs, s->fd, offset, nrz); ++ error_report("ioctl %s failed %d", op_name, ret); ++ return ret; ++ } ++ ++ if (zo == BLKRESETZONE && len == capacity) { ++ ret = get_zones_wp(bs, s->fd, 0, bs->bl.nr_zones, 1); ++ if (ret < 0) { ++ error_report("reporting single wp failed"); ++ return ret; ++ } ++ } else if (zo == BLKRESETZONE) { ++ for (unsigned int j = 0; j < nrz; ++j) { ++ wp[j] = offset + j * zone_size; ++ } ++ } else if (zo == BLKFINISHZONE) { ++ for (unsigned int j = 0; j < nrz; ++j) { ++ /* The zoned device allows the last zone smaller that the ++ * zone size. */ ++ wp[j] = MIN(offset + (j + 1) * zone_size, offset + len); ++ } ++ } ++ ++ return ret; ++} ++#endif ++ ++#if defined(CONFIG_BLKZONED) ++static int coroutine_fn raw_co_zone_append(BlockDriverState *bs, ++ int64_t *offset, ++ QEMUIOVector *qiov, ++ BdrvRequestFlags flags) { ++ assert(flags == 0); ++ int64_t zone_size_mask = bs->bl.zone_size - 1; ++ int64_t iov_len = 0; ++ int64_t len = 0; ++ ++ if (*offset & zone_size_mask) { ++ error_report("sector offset %" PRId64 " is not aligned to zone size " ++ "%" PRId32 "", *offset / 512, bs->bl.zone_size / 512); ++ return -EINVAL; ++ } ++ ++ int64_t wg = bs->bl.write_granularity; ++ int64_t wg_mask = wg - 1; ++ for (int i = 0; i < qiov->niov; i++) { ++ iov_len = qiov->iov[i].iov_len; ++ if (iov_len & wg_mask) { ++ error_report("len of IOVector[%d] %" PRId64 " is not aligned to " ++ "block size %" PRId64 "", i, iov_len, wg); ++ return -EINVAL; ++ } ++ len += iov_len; ++ } ++ ++ trace_zbd_zone_append(bs, *offset >> BDRV_SECTOR_BITS); ++ return raw_co_prw(bs, offset, len, qiov, QEMU_AIO_ZONE_APPEND); ++} ++#endif ++ ++static coroutine_fn int ++raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ bool blkdev) ++{ ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb; ++ int ret; ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = s->fd, ++ .aio_type = QEMU_AIO_DISCARD, ++ .aio_offset = offset, ++ .aio_nbytes = bytes, ++ }; ++ ++ if (blkdev) { ++ acb.aio_type |= QEMU_AIO_BLKDEV; ++ } ++ ++ ret = raw_thread_pool_submit(handle_aiocb_discard, &acb); ++ raw_account_discard(s, bytes, ret); ++ return ret; ++} ++ ++static coroutine_fn int ++raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ return raw_do_pdiscard(bs, offset, bytes, false); ++} ++ ++static int coroutine_fn ++raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags, bool blkdev) ++{ ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb; ++ ThreadPoolFunc *handler; ++ ++#ifdef CONFIG_FALLOCATE ++ if (offset + bytes > bs->total_sectors * BDRV_SECTOR_SIZE) { ++ BdrvTrackedRequest *req; ++ ++ /* ++ * This is a workaround for a bug in the Linux XFS driver, ++ * where writes submitted through the AIO interface will be ++ * discarded if they happen beyond a concurrently running ++ * fallocate() that increases the file length (i.e., both the ++ * write and the fallocate() happen beyond the EOF). ++ * ++ * To work around it, we extend the tracked request for this ++ * zero write until INT64_MAX (effectively infinity), and mark ++ * it as serializing. ++ * ++ * We have to enable this workaround for all filesystems and ++ * AIO modes (not just XFS with aio=native), because for ++ * remote filesystems we do not know the host configuration. ++ */ ++ ++ req = bdrv_co_get_self_request(bs); ++ assert(req); ++ assert(req->type == BDRV_TRACKED_WRITE); ++ assert(req->offset <= offset); ++ assert(req->offset + req->bytes >= offset + bytes); ++ ++ req->bytes = BDRV_MAX_LENGTH - req->offset; ++ ++ bdrv_check_request(req->offset, req->bytes, &error_abort); ++ ++ bdrv_make_request_serialising(req, bs->bl.request_alignment); ++ } ++#endif ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = s->fd, ++ .aio_type = QEMU_AIO_WRITE_ZEROES, ++ .aio_offset = offset, ++ .aio_nbytes = bytes, ++ }; ++ ++ if (blkdev) { ++ acb.aio_type |= QEMU_AIO_BLKDEV; ++ } ++ if (flags & BDRV_REQ_NO_FALLBACK) { ++ acb.aio_type |= QEMU_AIO_NO_FALLBACK; ++ } ++ ++ if (flags & BDRV_REQ_MAY_UNMAP) { ++ acb.aio_type |= QEMU_AIO_DISCARD; ++ handler = handle_aiocb_write_zeroes_unmap; ++ } else { ++ handler = handle_aiocb_write_zeroes; ++ } ++ ++ return raw_thread_pool_submit(handler, &acb); ++} ++ ++static int coroutine_fn raw_co_pwrite_zeroes( ++ BlockDriverState *bs, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags) ++{ ++ return raw_do_pwrite_zeroes(bs, offset, bytes, flags, false); ++} ++ ++static int coroutine_fn ++raw_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) ++{ ++ return 0; ++} ++ ++static ImageInfoSpecific *raw_get_specific_info(BlockDriverState *bs, ++ Error **errp) ++{ ++ ImageInfoSpecificFile *file_info = g_new0(ImageInfoSpecificFile, 1); ++ ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1); ++ ++ *spec_info = (ImageInfoSpecific){ ++ .type = IMAGE_INFO_SPECIFIC_KIND_FILE, ++ .u.file.data = file_info, ++ }; ++ ++#ifdef FS_IOC_FSGETXATTR ++ { ++ BDRVRawState *s = bs->opaque; ++ struct fsxattr attr; ++ int ret; ++ ++ ret = ioctl(s->fd, FS_IOC_FSGETXATTR, &attr); ++ if (!ret && attr.fsx_extsize != 0) { ++ file_info->has_extent_size_hint = true; ++ file_info->extent_size_hint = attr.fsx_extsize; ++ } ++ } ++#endif ++ ++ return spec_info; ++} ++ ++static BlockStatsSpecificFile get_blockstats_specific_file(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ return (BlockStatsSpecificFile) { ++ .discard_nb_ok = s->stats.discard_nb_ok, ++ .discard_nb_failed = s->stats.discard_nb_failed, ++ .discard_bytes_ok = s->stats.discard_bytes_ok, ++ }; ++} ++ ++static BlockStatsSpecific *raw_get_specific_stats(BlockDriverState *bs) ++{ ++ BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1); ++ ++ stats->driver = BLOCKDEV_DRIVER_FILE; ++ stats->u.file = get_blockstats_specific_file(bs); ++ ++ return stats; ++} ++ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++static BlockStatsSpecific *hdev_get_specific_stats(BlockDriverState *bs) ++{ ++ BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1); ++ ++ stats->driver = BLOCKDEV_DRIVER_HOST_DEVICE; ++ stats->u.host_device = get_blockstats_specific_file(bs); ++ ++ return stats; ++} ++#endif /* HAVE_HOST_BLOCK_DEVICE */ ++ ++static QemuOptsList raw_create_opts = { ++ .name = "raw-create-opts", ++ .head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head), ++ .desc = { ++ { ++ .name = BLOCK_OPT_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Virtual disk size" ++ }, ++ { ++ .name = BLOCK_OPT_NOCOW, ++ .type = QEMU_OPT_BOOL, ++ .help = "Turn off copy-on-write (valid only on btrfs)" ++ }, ++ { ++ .name = BLOCK_OPT_PREALLOC, ++ .type = QEMU_OPT_STRING, ++ .help = "Preallocation mode (allowed values: off" ++#ifdef CONFIG_POSIX_FALLOCATE ++ ", falloc" ++#endif ++ ", full)" ++ }, ++ { ++ .name = BLOCK_OPT_EXTENT_SIZE_HINT, ++ .type = QEMU_OPT_SIZE, ++ .help = "Extent size hint for the image file, 0 to disable" ++ }, ++ { /* end of list */ } ++ } ++}; ++ ++static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ int input_flags = s->reopen_state ? s->reopen_state->flags : bs->open_flags; ++ int open_flags; ++ int ret; ++ ++ /* We may need a new fd if auto-read-only switches the mode */ ++ ret = raw_reconfigure_getfd(bs, input_flags, &open_flags, perm, errp); ++ if (ret < 0) { ++ return ret; ++ } else if (ret != s->fd) { ++ Error *local_err = NULL; ++ ++ /* ++ * Fail already check_perm() if we can't get a working O_DIRECT ++ * alignment with the new fd. ++ */ ++ raw_probe_alignment(bs, ret, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ ++ s->perm_change_fd = ret; ++ s->perm_change_flags = open_flags; ++ } ++ ++ /* Prepare permissions on old fd to avoid conflicts between old and new, ++ * but keep everything locked that new will need. */ ++ ret = raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Copy locks to the new fd */ ++ if (s->perm_change_fd && s->use_lock) { ++ ret = raw_apply_lock_bytes(NULL, s->perm_change_fd, perm, ~shared, ++ false, errp); ++ if (ret < 0) { ++ raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); ++ goto fail; ++ } ++ } ++ return 0; ++ ++fail: ++ if (s->perm_change_fd) { ++ qemu_close(s->perm_change_fd); ++ } ++ s->perm_change_fd = 0; ++ return ret; ++} ++ ++static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ /* For reopen, we have already switched to the new fd (.bdrv_set_perm is ++ * called after .bdrv_reopen_commit) */ ++ if (s->perm_change_fd && s->fd != s->perm_change_fd) { ++ qemu_close(s->fd); ++ s->fd = s->perm_change_fd; ++ s->open_flags = s->perm_change_flags; ++ } ++ s->perm_change_fd = 0; ++ ++ raw_handle_perm_lock(bs, RAW_PL_COMMIT, perm, shared, NULL); ++ s->perm = perm; ++ s->shared_perm = shared; ++} ++ ++static void raw_abort_perm_update(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ /* For reopen, .bdrv_reopen_abort is called afterwards and will close ++ * the file descriptor. */ ++ if (s->perm_change_fd) { ++ qemu_close(s->perm_change_fd); ++ } ++ s->perm_change_fd = 0; ++ ++ raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK raw_co_copy_range_from( ++ BlockDriverState *bs, BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, int64_t bytes, ++ BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) ++{ ++ return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, ++ read_flags, write_flags); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_copy_range_to(BlockDriverState *bs, ++ BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ RawPosixAIOData acb; ++ BDRVRawState *s = bs->opaque; ++ BDRVRawState *src_s; ++ ++ assert(dst->bs == bs); ++ if (src->bs->drv->bdrv_co_copy_range_to != raw_co_copy_range_to) { ++ return -ENOTSUP; ++ } ++ ++ src_s = src->bs->opaque; ++ if (fd_open(src->bs) < 0 || fd_open(dst->bs) < 0) { ++ return -EIO; ++ } ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_type = QEMU_AIO_COPY_RANGE, ++ .aio_fildes = src_s->fd, ++ .aio_offset = src_offset, ++ .aio_nbytes = bytes, ++ .copy_range = { ++ .aio_fd2 = s->fd, ++ .aio_offset2 = dst_offset, ++ }, ++ }; ++ ++ return raw_thread_pool_submit(handle_aiocb_copy_range, &acb); ++} ++ ++BlockDriver bdrv_file = { ++ .format_name = "file", ++ .protocol_name = "file", ++ .instance_size = sizeof(BDRVRawState), ++ .bdrv_needs_filename = true, ++ .bdrv_probe = NULL, /* no probe for protocols */ ++ .bdrv_parse_filename = raw_parse_filename, ++ .bdrv_open = raw_open, ++ .bdrv_reopen_prepare = raw_reopen_prepare, ++ .bdrv_reopen_commit = raw_reopen_commit, ++ .bdrv_reopen_abort = raw_reopen_abort, ++ .bdrv_close = raw_close, ++ .bdrv_co_create = raw_co_create, ++ .bdrv_co_create_opts = raw_co_create_opts, ++ .bdrv_has_zero_init = bdrv_has_zero_init_1, ++ .bdrv_co_block_status = raw_co_block_status, ++ .bdrv_co_invalidate_cache = raw_co_invalidate_cache, ++ .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes, ++ .bdrv_co_delete_file = raw_co_delete_file, ++ ++ .bdrv_co_preadv = raw_co_preadv, ++ .bdrv_co_pwritev = raw_co_pwritev, ++ .bdrv_co_flush_to_disk = raw_co_flush_to_disk, ++ .bdrv_co_pdiscard = raw_co_pdiscard, ++ .bdrv_co_copy_range_from = raw_co_copy_range_from, ++ .bdrv_co_copy_range_to = raw_co_copy_range_to, ++ .bdrv_refresh_limits = raw_refresh_limits, ++ ++ .bdrv_co_truncate = raw_co_truncate, ++ .bdrv_co_getlength = raw_co_getlength, ++ .bdrv_co_get_info = raw_co_get_info, ++ .bdrv_get_specific_info = raw_get_specific_info, ++ .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, ++ .bdrv_get_specific_stats = raw_get_specific_stats, ++ .bdrv_check_perm = raw_check_perm, ++ .bdrv_set_perm = raw_set_perm, ++ .bdrv_abort_perm_update = raw_abort_perm_update, ++ .create_opts = &raw_create_opts, ++ .mutable_opts = mutable_opts, ++}; ++ ++/***********************************************/ ++/* host device */ ++ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ ++#if defined(__APPLE__) && defined(__MACH__) ++static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, ++ CFIndex maxPathSize, int flags); ++ ++static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) ++{ ++ kern_return_t kernResult = KERN_FAILURE; ++ mach_port_t mainPort; ++ CFMutableDictionaryRef classesToMatch; ++ const char *matching_array[] = {kIODVDMediaClass, kIOCDMediaClass}; ++ char *mediaType = NULL; ++ ++ kernResult = IOMainPort(MACH_PORT_NULL, &mainPort); ++ if ( KERN_SUCCESS != kernResult ) { ++ printf("IOMainPort returned %d\n", kernResult); ++ } ++ ++ int index; ++ for (index = 0; index < ARRAY_SIZE(matching_array); index++) { ++ classesToMatch = IOServiceMatching(matching_array[index]); ++ if (classesToMatch == NULL) { ++ error_report("IOServiceMatching returned NULL for %s", ++ matching_array[index]); ++ continue; ++ } ++ CFDictionarySetValue(classesToMatch, CFSTR(kIOMediaEjectableKey), ++ kCFBooleanTrue); ++ kernResult = IOServiceGetMatchingServices(mainPort, classesToMatch, ++ mediaIterator); ++ if (kernResult != KERN_SUCCESS) { ++ error_report("Note: IOServiceGetMatchingServices returned %d", ++ kernResult); ++ continue; ++ } ++ ++ /* If a match was found, leave the loop */ ++ if (*mediaIterator != 0) { ++ trace_file_FindEjectableOpticalMedia(matching_array[index]); ++ mediaType = g_strdup(matching_array[index]); ++ break; ++ } ++ } ++ return mediaType; ++} ++ ++kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, ++ CFIndex maxPathSize, int flags) ++{ ++ io_object_t nextMedia; ++ kern_return_t kernResult = KERN_FAILURE; ++ *bsdPath = '\0'; ++ nextMedia = IOIteratorNext( mediaIterator ); ++ if ( nextMedia ) ++ { ++ CFTypeRef bsdPathAsCFString; ++ bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 ); ++ if ( bsdPathAsCFString ) { ++ size_t devPathLength; ++ strcpy( bsdPath, _PATH_DEV ); ++ if (flags & BDRV_O_NOCACHE) { ++ strcat(bsdPath, "r"); ++ } ++ devPathLength = strlen( bsdPath ); ++ if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) { ++ kernResult = KERN_SUCCESS; ++ } ++ CFRelease( bsdPathAsCFString ); ++ } ++ IOObjectRelease( nextMedia ); ++ } ++ ++ return kernResult; ++} ++ ++/* Sets up a real cdrom for use in QEMU */ ++static bool setup_cdrom(char *bsd_path, Error **errp) ++{ ++ int index, num_of_test_partitions = 2, fd; ++ char test_partition[MAXPATHLEN]; ++ bool partition_found = false; ++ ++ /* look for a working partition */ ++ for (index = 0; index < num_of_test_partitions; index++) { ++ snprintf(test_partition, sizeof(test_partition), "%ss%d", bsd_path, ++ index); ++ fd = qemu_open(test_partition, O_RDONLY | O_BINARY | O_LARGEFILE, NULL); ++ if (fd >= 0) { ++ partition_found = true; ++ qemu_close(fd); ++ break; ++ } ++ } ++ ++ /* if a working partition on the device was not found */ ++ if (partition_found == false) { ++ error_setg(errp, "Failed to find a working partition on disc"); ++ } else { ++ trace_file_setup_cdrom(test_partition); ++ pstrcpy(bsd_path, MAXPATHLEN, test_partition); ++ } ++ return partition_found; ++} ++ ++/* Prints directions on mounting and unmounting a device */ ++static void print_unmounting_directions(const char *file_name) ++{ ++ error_report("If device %s is mounted on the desktop, unmount" ++ " it first before using it in QEMU", file_name); ++ error_report("Command to unmount device: diskutil unmountDisk %s", ++ file_name); ++ error_report("Command to mount device: diskutil mountDisk %s", file_name); ++} ++ ++#endif /* defined(__APPLE__) && defined(__MACH__) */ ++ ++static int hdev_probe_device(const char *filename) ++{ ++ struct stat st; ++ ++ /* allow a dedicated CD-ROM driver to match with a higher priority */ ++ if (strstart(filename, "/dev/cdrom", NULL)) ++ return 50; ++ ++ if (stat(filename, &st) >= 0 && ++ (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) { ++ return 100; ++ } ++ ++ return 0; ++} ++ ++static void hdev_parse_filename(const char *filename, QDict *options, ++ Error **errp) ++{ ++ bdrv_parse_filename_strip_prefix(filename, "host_device:", options); ++} ++ ++static bool hdev_is_sg(BlockDriverState *bs) ++{ ++ ++#if defined(__linux__) ++ ++ BDRVRawState *s = bs->opaque; ++ struct stat st; ++ struct sg_scsi_id scsiid; ++ int sg_version; ++ int ret; ++ ++ if (stat(bs->filename, &st) < 0 || !S_ISCHR(st.st_mode)) { ++ return false; ++ } ++ ++ ret = ioctl(s->fd, SG_GET_VERSION_NUM, &sg_version); ++ if (ret < 0) { ++ return false; ++ } ++ ++ ret = ioctl(s->fd, SG_GET_SCSI_ID, &scsiid); ++ if (ret >= 0) { ++ trace_file_hdev_is_sg(scsiid.scsi_type, sg_version); ++ return true; ++ } ++ ++#endif ++ ++ return false; ++} ++ ++static int hdev_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++#if defined(__APPLE__) && defined(__MACH__) ++ /* ++ * Caution: while qdict_get_str() is fine, getting non-string types ++ * would require more care. When @options come from -blockdev or ++ * blockdev_add, its members are typed according to the QAPI ++ * schema, but when they come from -drive, they're all QString. ++ */ ++ const char *filename = qdict_get_str(options, "filename"); ++ char bsd_path[MAXPATHLEN] = ""; ++ bool error_occurred = false; ++ ++ /* If using a real cdrom */ ++ if (strcmp(filename, "/dev/cdrom") == 0) { ++ char *mediaType = NULL; ++ kern_return_t ret_val; ++ io_iterator_t mediaIterator = 0; ++ ++ mediaType = FindEjectableOpticalMedia(&mediaIterator); ++ if (mediaType == NULL) { ++ error_setg(errp, "Please make sure your CD/DVD is in the optical" ++ " drive"); ++ error_occurred = true; ++ goto hdev_open_Mac_error; ++ } ++ ++ ret_val = GetBSDPath(mediaIterator, bsd_path, sizeof(bsd_path), flags); ++ if (ret_val != KERN_SUCCESS) { ++ error_setg(errp, "Could not get BSD path for optical drive"); ++ error_occurred = true; ++ goto hdev_open_Mac_error; ++ } ++ ++ /* If a real optical drive was not found */ ++ if (bsd_path[0] == '\0') { ++ error_setg(errp, "Failed to obtain bsd path for optical drive"); ++ error_occurred = true; ++ goto hdev_open_Mac_error; ++ } ++ ++ /* If using a cdrom disc and finding a partition on the disc failed */ ++ if (strncmp(mediaType, kIOCDMediaClass, 9) == 0 && ++ setup_cdrom(bsd_path, errp) == false) { ++ print_unmounting_directions(bsd_path); ++ error_occurred = true; ++ goto hdev_open_Mac_error; ++ } ++ ++ qdict_put_str(options, "filename", bsd_path); ++ ++hdev_open_Mac_error: ++ g_free(mediaType); ++ if (mediaIterator) { ++ IOObjectRelease(mediaIterator); ++ } ++ if (error_occurred) { ++ return -ENOENT; ++ } ++ } ++#endif /* defined(__APPLE__) && defined(__MACH__) */ ++ ++ s->type = FTYPE_FILE; ++ ++ ret = raw_open_common(bs, options, flags, 0, true, errp); ++ if (ret < 0) { ++#if defined(__APPLE__) && defined(__MACH__) ++ if (*bsd_path) { ++ filename = bsd_path; ++ } ++ /* if a physical device experienced an error while being opened */ ++ if (strncmp(filename, "/dev/", 5) == 0) { ++ print_unmounting_directions(filename); ++ } ++#endif /* defined(__APPLE__) && defined(__MACH__) */ ++ return ret; ++ } ++ ++ /* Since this does ioctl the device must be already opened */ ++ bs->sg = hdev_is_sg(bs); ++ ++ return ret; ++} ++ ++#if defined(__linux__) ++static int coroutine_fn ++hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) ++{ ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb; ++ int ret; ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (req == SG_IO && s->pr_mgr) { ++ struct sg_io_hdr *io_hdr = buf; ++ if (io_hdr->cmdp[0] == PERSISTENT_RESERVE_OUT || ++ io_hdr->cmdp[0] == PERSISTENT_RESERVE_IN) { ++ return pr_manager_execute(s->pr_mgr, qemu_get_current_aio_context(), ++ s->fd, io_hdr); ++ } ++ } ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_type = QEMU_AIO_IOCTL, ++ .aio_fildes = s->fd, ++ .aio_offset = 0, ++ .ioctl = { ++ .buf = buf, ++ .cmd = req, ++ }, ++ }; ++ ++ return raw_thread_pool_submit(handle_aiocb_ioctl, &acb); ++} ++#endif /* linux */ ++ ++static coroutine_fn int ++hdev_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ raw_account_discard(s, bytes, ret); ++ return ret; ++ } ++ return raw_do_pdiscard(bs, offset, bytes, true); ++} ++ ++static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, BdrvRequestFlags flags) ++{ ++ int rc; ++ ++ rc = fd_open(bs); ++ if (rc < 0) { ++ return rc; ++ } ++ ++ return raw_do_pwrite_zeroes(bs, offset, bytes, flags, true); ++} ++ ++static BlockDriver bdrv_host_device = { ++ .format_name = "host_device", ++ .protocol_name = "host_device", ++ .instance_size = sizeof(BDRVRawState), ++ .bdrv_needs_filename = true, ++ .bdrv_probe_device = hdev_probe_device, ++ .bdrv_parse_filename = hdev_parse_filename, ++ .bdrv_open = hdev_open, ++ .bdrv_close = raw_close, ++ .bdrv_reopen_prepare = raw_reopen_prepare, ++ .bdrv_reopen_commit = raw_reopen_commit, ++ .bdrv_reopen_abort = raw_reopen_abort, ++ .bdrv_co_create_opts = bdrv_co_create_opts_simple, ++ .create_opts = &bdrv_create_opts_simple, ++ .mutable_opts = mutable_opts, ++ .bdrv_co_invalidate_cache = raw_co_invalidate_cache, ++ .bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes, ++ ++ .bdrv_co_preadv = raw_co_preadv, ++ .bdrv_co_pwritev = raw_co_pwritev, ++ .bdrv_co_flush_to_disk = raw_co_flush_to_disk, ++ .bdrv_co_pdiscard = hdev_co_pdiscard, ++ .bdrv_co_copy_range_from = raw_co_copy_range_from, ++ .bdrv_co_copy_range_to = raw_co_copy_range_to, ++ .bdrv_refresh_limits = raw_refresh_limits, ++ ++ .bdrv_co_truncate = raw_co_truncate, ++ .bdrv_co_getlength = raw_co_getlength, ++ .bdrv_co_get_info = raw_co_get_info, ++ .bdrv_get_specific_info = raw_get_specific_info, ++ .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, ++ .bdrv_get_specific_stats = hdev_get_specific_stats, ++ .bdrv_check_perm = raw_check_perm, ++ .bdrv_set_perm = raw_set_perm, ++ .bdrv_abort_perm_update = raw_abort_perm_update, ++ .bdrv_probe_blocksizes = hdev_probe_blocksizes, ++ .bdrv_probe_geometry = hdev_probe_geometry, ++ ++ /* generic scsi device */ ++#ifdef __linux__ ++ .bdrv_co_ioctl = hdev_co_ioctl, ++#endif ++ ++ /* zoned device */ ++#if defined(CONFIG_BLKZONED) ++ /* zone management operations */ ++ .bdrv_co_zone_report = raw_co_zone_report, ++ .bdrv_co_zone_mgmt = raw_co_zone_mgmt, ++ .bdrv_co_zone_append = raw_co_zone_append, ++#endif ++}; ++ ++#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) ++static void cdrom_parse_filename(const char *filename, QDict *options, ++ Error **errp) ++{ ++ bdrv_parse_filename_strip_prefix(filename, "host_cdrom:", options); ++} ++ ++static void cdrom_refresh_limits(BlockDriverState *bs, Error **errp) ++{ ++ bs->bl.has_variable_length = true; ++ raw_refresh_limits(bs, errp); ++} ++#endif ++ ++#ifdef __linux__ ++static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ s->type = FTYPE_CD; ++ ++ /* open will not fail even if no CD is inserted, so add O_NONBLOCK */ ++ return raw_open_common(bs, options, flags, O_NONBLOCK, true, errp); ++} ++ ++static int cdrom_probe_device(const char *filename) ++{ ++ int fd, ret; ++ int prio = 0; ++ struct stat st; ++ ++ fd = qemu_open(filename, O_RDONLY | O_NONBLOCK, NULL); ++ if (fd < 0) { ++ goto out; ++ } ++ ret = fstat(fd, &st); ++ if (ret == -1 || !S_ISBLK(st.st_mode)) { ++ goto outc; ++ } ++ ++ /* Attempt to detect via a CDROM specific ioctl */ ++ ret = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); ++ if (ret >= 0) ++ prio = 100; ++ ++outc: ++ qemu_close(fd); ++out: ++ return prio; ++} ++ ++static bool coroutine_fn cdrom_co_is_inserted(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++ ret = ioctl(s->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); ++ return ret == CDS_DISC_OK; ++} ++ ++static void coroutine_fn cdrom_co_eject(BlockDriverState *bs, bool eject_flag) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (eject_flag) { ++ if (ioctl(s->fd, CDROMEJECT, NULL) < 0) ++ perror("CDROMEJECT"); ++ } else { ++ if (ioctl(s->fd, CDROMCLOSETRAY, NULL) < 0) ++ perror("CDROMEJECT"); ++ } ++} ++ ++static void coroutine_fn cdrom_co_lock_medium(BlockDriverState *bs, bool locked) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (ioctl(s->fd, CDROM_LOCKDOOR, locked) < 0) { ++ /* ++ * Note: an error can happen if the distribution automatically ++ * mounts the CD-ROM ++ */ ++ /* perror("CDROM_LOCKDOOR"); */ ++ } ++} ++ ++static BlockDriver bdrv_host_cdrom = { ++ .format_name = "host_cdrom", ++ .protocol_name = "host_cdrom", ++ .instance_size = sizeof(BDRVRawState), ++ .bdrv_needs_filename = true, ++ .bdrv_probe_device = cdrom_probe_device, ++ .bdrv_parse_filename = cdrom_parse_filename, ++ .bdrv_open = cdrom_open, ++ .bdrv_close = raw_close, ++ .bdrv_reopen_prepare = raw_reopen_prepare, ++ .bdrv_reopen_commit = raw_reopen_commit, ++ .bdrv_reopen_abort = raw_reopen_abort, ++ .bdrv_co_create_opts = bdrv_co_create_opts_simple, ++ .create_opts = &bdrv_create_opts_simple, ++ .mutable_opts = mutable_opts, ++ .bdrv_co_invalidate_cache = raw_co_invalidate_cache, ++ ++ .bdrv_co_preadv = raw_co_preadv, ++ .bdrv_co_pwritev = raw_co_pwritev, ++ .bdrv_co_flush_to_disk = raw_co_flush_to_disk, ++ .bdrv_refresh_limits = cdrom_refresh_limits, ++ ++ .bdrv_co_truncate = raw_co_truncate, ++ .bdrv_co_getlength = raw_co_getlength, ++ .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, ++ ++ /* removable device support */ ++ .bdrv_co_is_inserted = cdrom_co_is_inserted, ++ .bdrv_co_eject = cdrom_co_eject, ++ .bdrv_co_lock_medium = cdrom_co_lock_medium, ++ ++ /* generic scsi device */ ++ .bdrv_co_ioctl = hdev_co_ioctl, ++}; ++#endif /* __linux__ */ ++ ++#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) ++static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++ s->type = FTYPE_CD; ++ ++ ret = raw_open_common(bs, options, flags, 0, true, errp); ++ if (ret) { ++ return ret; ++ } ++ ++ /* make sure the door isn't locked at this time */ ++ ioctl(s->fd, CDIOCALLOW); ++ return 0; ++} ++ ++static int cdrom_probe_device(const char *filename) ++{ ++ if (strstart(filename, "/dev/cd", NULL) || ++ strstart(filename, "/dev/acd", NULL)) ++ return 100; ++ return 0; ++} ++ ++static int cdrom_reopen(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ int fd; ++ ++ /* ++ * Force reread of possibly changed/newly loaded disc, ++ * FreeBSD seems to not notice sometimes... ++ */ ++ if (s->fd >= 0) ++ qemu_close(s->fd); ++ fd = qemu_open(bs->filename, s->open_flags, NULL); ++ if (fd < 0) { ++ s->fd = -1; ++ return -EIO; ++ } ++ s->fd = fd; ++ ++ /* make sure the door isn't locked at this time */ ++ ioctl(s->fd, CDIOCALLOW); ++ return 0; ++} ++ ++static bool coroutine_fn cdrom_co_is_inserted(BlockDriverState *bs) ++{ ++ return raw_getlength(bs) > 0; ++} ++ ++static void coroutine_fn cdrom_co_eject(BlockDriverState *bs, bool eject_flag) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (s->fd < 0) ++ return; ++ ++ (void) ioctl(s->fd, CDIOCALLOW); ++ ++ if (eject_flag) { ++ if (ioctl(s->fd, CDIOCEJECT) < 0) ++ perror("CDIOCEJECT"); ++ } else { ++ if (ioctl(s->fd, CDIOCCLOSE) < 0) ++ perror("CDIOCCLOSE"); ++ } ++ ++ cdrom_reopen(bs); ++} ++ ++static void coroutine_fn cdrom_co_lock_medium(BlockDriverState *bs, bool locked) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (s->fd < 0) ++ return; ++ if (ioctl(s->fd, (locked ? CDIOCPREVENT : CDIOCALLOW)) < 0) { ++ /* ++ * Note: an error can happen if the distribution automatically ++ * mounts the CD-ROM ++ */ ++ /* perror("CDROM_LOCKDOOR"); */ ++ } ++} ++ ++static BlockDriver bdrv_host_cdrom = { ++ .format_name = "host_cdrom", ++ .protocol_name = "host_cdrom", ++ .instance_size = sizeof(BDRVRawState), ++ .bdrv_needs_filename = true, ++ .bdrv_probe_device = cdrom_probe_device, ++ .bdrv_parse_filename = cdrom_parse_filename, ++ .bdrv_open = cdrom_open, ++ .bdrv_close = raw_close, ++ .bdrv_reopen_prepare = raw_reopen_prepare, ++ .bdrv_reopen_commit = raw_reopen_commit, ++ .bdrv_reopen_abort = raw_reopen_abort, ++ .bdrv_co_create_opts = bdrv_co_create_opts_simple, ++ .create_opts = &bdrv_create_opts_simple, ++ .mutable_opts = mutable_opts, ++ ++ .bdrv_co_preadv = raw_co_preadv, ++ .bdrv_co_pwritev = raw_co_pwritev, ++ .bdrv_co_flush_to_disk = raw_co_flush_to_disk, ++ .bdrv_refresh_limits = cdrom_refresh_limits, ++ ++ .bdrv_co_truncate = raw_co_truncate, ++ .bdrv_co_getlength = raw_co_getlength, ++ .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, ++ ++ /* removable device support */ ++ .bdrv_co_is_inserted = cdrom_co_is_inserted, ++ .bdrv_co_eject = cdrom_co_eject, ++ .bdrv_co_lock_medium = cdrom_co_lock_medium, ++}; ++#endif /* __FreeBSD__ */ ++ ++#endif /* HAVE_HOST_BLOCK_DEVICE */ ++ ++static void bdrv_file_init(void) ++{ ++ /* ++ * Register all the drivers. Note that order is important, the driver ++ * registered last will get probed first. ++ */ ++ bdrv_register(&bdrv_file); ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ bdrv_register(&bdrv_host_device); ++#ifdef __linux__ ++ bdrv_register(&bdrv_host_cdrom); ++#endif ++#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) ++ bdrv_register(&bdrv_host_cdrom); ++#endif ++#endif /* HAVE_HOST_BLOCK_DEVICE */ ++} ++ ++block_init(bdrv_file_init); +diff --git a/qcow2/lib/block/graph-lock.c b/qcow2/lib/block/graph-lock.c +new file mode 100644 +index 00000000..c81162b1 +--- /dev/null ++++ b/qcow2/lib/block/graph-lock.c +@@ -0,0 +1,281 @@ ++/* ++ * Graph lock: rwlock to protect block layer graph manipulations (add/remove ++ * edges and nodes) ++ * ++ * Copyright (c) 2022 Red Hat ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/main-loop.h" ++#include "block/graph-lock.h" ++#include "block/block.h" ++#include "block/block_int.h" ++ ++/* Dummy lock object to use for Thread Safety Analysis (TSA) */ ++BdrvGraphLock graph_lock; ++ ++/* Protects the list of aiocontext and orphaned_reader_count */ ++static QemuMutex aio_context_list_lock; ++ ++/* Written and read with atomic operations. */ ++static int has_writer; ++ ++/* ++ * A reader coroutine could move from an AioContext to another. ++ * If this happens, there is no problem from the point of view of ++ * counters. The problem is that the total count becomes ++ * unbalanced if one of the two AioContexts gets deleted. ++ * The count of readers must remain correct, so the AioContext's ++ * balance is transferred to this glboal variable. ++ * Protected by aio_context_list_lock. ++ */ ++static uint32_t orphaned_reader_count; ++ ++/* Queue of readers waiting for the writer to finish */ ++static CoQueue reader_queue; ++ ++struct BdrvGraphRWlock { ++ /* How many readers are currently reading the graph. */ ++ uint32_t reader_count; ++ ++ /* ++ * List of BdrvGraphRWlock kept in graph-lock.c ++ * Protected by aio_context_list_lock ++ */ ++ QTAILQ_ENTRY(BdrvGraphRWlock) next_aio; ++}; ++ ++/* ++ * List of BdrvGraphRWlock. This list ensures that each BdrvGraphRWlock ++ * can safely modify only its own counter, avoid reading/writing ++ * others and thus improving performances by avoiding cacheline bounces. ++ */ ++static QTAILQ_HEAD(, BdrvGraphRWlock) aio_context_list = ++ QTAILQ_HEAD_INITIALIZER(aio_context_list); ++ ++static void __attribute__((__constructor__)) bdrv_init_graph_lock(void) ++{ ++ qemu_mutex_init(&aio_context_list_lock); ++ qemu_co_queue_init(&reader_queue); ++} ++ ++void register_aiocontext(AioContext *ctx) ++{ ++ ctx->bdrv_graph = g_new0(BdrvGraphRWlock, 1); ++ QEMU_LOCK_GUARD(&aio_context_list_lock); ++ assert(ctx->bdrv_graph->reader_count == 0); ++ QTAILQ_INSERT_TAIL(&aio_context_list, ctx->bdrv_graph, next_aio); ++} ++ ++void unregister_aiocontext(AioContext *ctx) ++{ ++ QEMU_LOCK_GUARD(&aio_context_list_lock); ++ orphaned_reader_count += ctx->bdrv_graph->reader_count; ++ QTAILQ_REMOVE(&aio_context_list, ctx->bdrv_graph, next_aio); ++ g_free(ctx->bdrv_graph); ++} ++ ++static uint32_t reader_count(void) ++{ ++ BdrvGraphRWlock *brdv_graph; ++ uint32_t rd; ++ ++ QEMU_LOCK_GUARD(&aio_context_list_lock); ++ ++ /* rd can temporarily be negative, but the total will *always* be >= 0 */ ++ rd = orphaned_reader_count; ++ QTAILQ_FOREACH(brdv_graph, &aio_context_list, next_aio) { ++ rd += qatomic_read(&brdv_graph->reader_count); ++ } ++ ++ /* shouldn't overflow unless there are 2^31 readers */ ++ assert((int32_t)rd >= 0); ++ return rd; ++} ++ ++void no_coroutine_fn bdrv_graph_wrlock(void) ++{ ++ GLOBAL_STATE_CODE(); ++ assert(!qatomic_read(&has_writer)); ++ assert(!qemu_in_coroutine()); ++ ++ /* Make sure that constantly arriving new I/O doesn't cause starvation */ ++ bdrv_drain_all_begin_nopoll(); ++ ++ /* ++ * reader_count == 0: this means writer will read has_reader as 1 ++ * reader_count >= 1: we don't know if writer read has_writer == 0 or 1, ++ * but we need to wait. ++ * Wait by allowing other coroutine (and possible readers) to continue. ++ */ ++ do { ++ /* ++ * has_writer must be 0 while polling, otherwise we get a deadlock if ++ * any callback involved during AIO_WAIT_WHILE() tries to acquire the ++ * reader lock. ++ */ ++ qatomic_set(&has_writer, 0); ++ AIO_WAIT_WHILE_UNLOCKED(NULL, reader_count() >= 1); ++ qatomic_set(&has_writer, 1); ++ ++ /* ++ * We want to only check reader_count() after has_writer = 1 is visible ++ * to other threads. That way no more readers can sneak in after we've ++ * determined reader_count() == 0. ++ */ ++ smp_mb(); ++ } while (reader_count() >= 1); ++ ++ bdrv_drain_all_end(); ++} ++ ++void no_coroutine_fn bdrv_graph_wrunlock(void) ++{ ++ GLOBAL_STATE_CODE(); ++ assert(qatomic_read(&has_writer)); ++ ++ WITH_QEMU_LOCK_GUARD(&aio_context_list_lock) { ++ /* ++ * No need for memory barriers, this works in pair with ++ * the slow path of rdlock() and both take the lock. ++ */ ++ qatomic_store_release(&has_writer, 0); ++ ++ /* Wake up all coroutines that are waiting to read the graph */ ++ qemu_co_enter_all(&reader_queue, &aio_context_list_lock); ++ } ++ ++ /* ++ * Run any BHs that were scheduled during the wrlock section and that ++ * callers might expect to have finished (in particular, this is important ++ * for bdrv_schedule_unref()). ++ * ++ * Do this only after restarting coroutines so that nested event loops in ++ * BHs don't deadlock if their condition relies on the coroutine making ++ * progress. ++ */ ++ aio_bh_poll(qemu_get_aio_context()); ++} ++ ++void coroutine_fn bdrv_graph_co_rdlock(void) ++{ ++ BdrvGraphRWlock *bdrv_graph; ++ bdrv_graph = qemu_get_current_aio_context()->bdrv_graph; ++ ++ for (;;) { ++ qatomic_set(&bdrv_graph->reader_count, ++ bdrv_graph->reader_count + 1); ++ /* make sure writer sees reader_count before we check has_writer */ ++ smp_mb(); ++ ++ /* ++ * has_writer == 0: this means writer will read reader_count as >= 1 ++ * has_writer == 1: we don't know if writer read reader_count == 0 ++ * or > 0, but we need to wait anyways because ++ * it will write. ++ */ ++ if (!qatomic_read(&has_writer)) { ++ break; ++ } ++ ++ /* ++ * Synchronize access with reader_count() in bdrv_graph_wrlock(). ++ * Case 1: ++ * If this critical section gets executed first, reader_count will ++ * decrease and the reader will go to sleep. ++ * Then the writer will read reader_count that does not take into ++ * account this reader, and if there's no other reader it will ++ * enter the write section. ++ * Case 2: ++ * If reader_count() critical section gets executed first, ++ * then writer will read reader_count >= 1. ++ * It will wait in AIO_WAIT_WHILE(), but once it releases the lock ++ * we will enter this critical section and call aio_wait_kick(). ++ */ ++ WITH_QEMU_LOCK_GUARD(&aio_context_list_lock) { ++ /* ++ * Additional check when we use the above lock to synchronize ++ * with bdrv_graph_wrunlock(). ++ * Case 1: ++ * If this gets executed first, has_writer is still 1, so we reduce ++ * reader_count and go to sleep. ++ * Then the writer will set has_writer to 0 and wake up all readers, ++ * us included. ++ * Case 2: ++ * If bdrv_graph_wrunlock() critical section gets executed first, ++ * then it will set has_writer to 0 and wake up all other readers. ++ * Then we execute this critical section, and therefore must check ++ * again for has_writer, otherwise we sleep without any writer ++ * actually running. ++ */ ++ if (!qatomic_read(&has_writer)) { ++ return; ++ } ++ ++ /* slow path where reader sleeps */ ++ bdrv_graph->reader_count--; ++ aio_wait_kick(); ++ qemu_co_queue_wait(&reader_queue, &aio_context_list_lock); ++ } ++ } ++} ++ ++void coroutine_fn bdrv_graph_co_rdunlock(void) ++{ ++ BdrvGraphRWlock *bdrv_graph; ++ bdrv_graph = qemu_get_current_aio_context()->bdrv_graph; ++ ++ qatomic_store_release(&bdrv_graph->reader_count, ++ bdrv_graph->reader_count - 1); ++ /* make sure writer sees reader_count before we check has_writer */ ++ smp_mb(); ++ ++ /* ++ * has_writer == 0: this means reader will read reader_count decreased ++ * has_writer == 1: we don't know if writer read reader_count old or ++ * new. Therefore, kick again so on next iteration ++ * writer will for sure read the updated value. ++ */ ++ if (qatomic_read(&has_writer)) { ++ aio_wait_kick(); ++ } ++} ++ ++void bdrv_graph_rdlock_main_loop(void) ++{ ++ GLOBAL_STATE_CODE(); ++ assert(!qemu_in_coroutine()); ++} ++ ++void bdrv_graph_rdunlock_main_loop(void) ++{ ++ GLOBAL_STATE_CODE(); ++ assert(!qemu_in_coroutine()); ++} ++ ++void assert_bdrv_graph_readable(void) ++{ ++ /* reader_count() is slow due to aio_context_list_lock lock contention */ ++#ifdef CONFIG_DEBUG_GRAPH_LOCK ++ assert(qemu_in_main_thread() || reader_count()); ++#endif ++} ++ ++void assert_bdrv_graph_writable(void) ++{ ++ assert(qemu_in_main_thread()); ++ assert(qatomic_read(&has_writer)); ++} +diff --git a/qcow2/lib/block/io.c b/qcow2/lib/block/io.c +new file mode 100644 +index 00000000..301514c8 +--- /dev/null ++++ b/qcow2/lib/block/io.c +@@ -0,0 +1,3755 @@ ++/* ++ * Block layer I/O functions ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "trace.h" ++#include "sysemu/block-backend.h" ++#include "block/aio-wait.h" ++#include "block/blockjob.h" ++#include "block/blockjob_int.h" ++#include "block/block_int.h" ++#include "block/coroutines.h" ++#include "block/dirty-bitmap.h" ++#include "block/write-threshold.h" ++#include "qemu/cutils.h" ++#include "qemu/memalign.h" ++#include "qapi/error.h" ++#include "qemu/error-report.h" ++#include "qemu/main-loop.h" ++#include "sysemu/replay.h" ++ ++/* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ ++#define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) ++ ++static void coroutine_fn GRAPH_RDLOCK ++bdrv_parent_cb_resize(BlockDriverState *bs); ++ ++static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, BdrvRequestFlags flags); ++ ++static void GRAPH_RDLOCK ++bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore) ++{ ++ BdrvChild *c, *next; ++ IO_OR_GS_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { ++ if (c == ignore) { ++ continue; ++ } ++ bdrv_parent_drained_begin_single(c); ++ } ++} ++ ++void bdrv_parent_drained_end_single(BdrvChild *c) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ assert(c->quiesced_parent); ++ c->quiesced_parent = false; ++ ++ if (c->klass->drained_end) { ++ c->klass->drained_end(c); ++ } ++} ++ ++static void GRAPH_RDLOCK ++bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) ++{ ++ BdrvChild *c; ++ IO_OR_GS_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ if (c == ignore) { ++ continue; ++ } ++ bdrv_parent_drained_end_single(c); ++ } ++} ++ ++bool bdrv_parent_drained_poll_single(BdrvChild *c) ++{ ++ IO_OR_GS_CODE(); ++ ++ if (c->klass->drained_poll) { ++ return c->klass->drained_poll(c); ++ } ++ return false; ++} ++ ++static bool GRAPH_RDLOCK ++bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, ++ bool ignore_bds_parents) ++{ ++ BdrvChild *c, *next; ++ bool busy = false; ++ IO_OR_GS_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { ++ if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) { ++ continue; ++ } ++ busy |= bdrv_parent_drained_poll_single(c); ++ } ++ ++ return busy; ++} ++ ++void bdrv_parent_drained_begin_single(BdrvChild *c) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ assert(!c->quiesced_parent); ++ c->quiesced_parent = true; ++ ++ if (c->klass->drained_begin) { ++ /* called with rdlock taken, but it doesn't really need it. */ ++ c->klass->drained_begin(c); ++ } ++} ++ ++static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) ++{ ++ dst->pdiscard_alignment = MAX(dst->pdiscard_alignment, ++ src->pdiscard_alignment); ++ dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer); ++ dst->max_transfer = MIN_NON_ZERO(dst->max_transfer, src->max_transfer); ++ dst->max_hw_transfer = MIN_NON_ZERO(dst->max_hw_transfer, ++ src->max_hw_transfer); ++ dst->opt_mem_alignment = MAX(dst->opt_mem_alignment, ++ src->opt_mem_alignment); ++ dst->min_mem_alignment = MAX(dst->min_mem_alignment, ++ src->min_mem_alignment); ++ dst->max_iov = MIN_NON_ZERO(dst->max_iov, src->max_iov); ++ dst->max_hw_iov = MIN_NON_ZERO(dst->max_hw_iov, src->max_hw_iov); ++} ++ ++typedef struct BdrvRefreshLimitsState { ++ BlockDriverState *bs; ++ BlockLimits old_bl; ++} BdrvRefreshLimitsState; ++ ++static void bdrv_refresh_limits_abort(void *opaque) ++{ ++ BdrvRefreshLimitsState *s = opaque; ++ ++ s->bs->bl = s->old_bl; ++} ++ ++static TransactionActionDrv bdrv_refresh_limits_drv = { ++ .abort = bdrv_refresh_limits_abort, ++ .clean = g_free, ++}; ++ ++/* @tran is allowed to be NULL, in this case no rollback is possible. */ ++void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp) ++{ ++ ERRP_GUARD(); ++ BlockDriver *drv = bs->drv; ++ BdrvChild *c; ++ bool have_limits; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (tran) { ++ BdrvRefreshLimitsState *s = g_new(BdrvRefreshLimitsState, 1); ++ *s = (BdrvRefreshLimitsState) { ++ .bs = bs, ++ .old_bl = bs->bl, ++ }; ++ tran_add(tran, &bdrv_refresh_limits_drv, s); ++ } ++ ++ memset(&bs->bl, 0, sizeof(bs->bl)); ++ ++ if (!drv) { ++ return; ++ } ++ ++ /* Default alignment based on whether driver has byte interface */ ++ bs->bl.request_alignment = (drv->bdrv_co_preadv || ++ drv->bdrv_aio_preadv || ++ drv->bdrv_co_preadv_part) ? 1 : 512; ++ ++ /* Take some limits from the children as a default */ ++ have_limits = false; ++ QLIST_FOREACH(c, &bs->children, next) { ++ if (c->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED | BDRV_CHILD_COW)) ++ { ++ bdrv_merge_limits(&bs->bl, &c->bs->bl); ++ have_limits = true; ++ } ++ ++ if (c->role & BDRV_CHILD_FILTERED) { ++ bs->bl.has_variable_length |= c->bs->bl.has_variable_length; ++ } ++ } ++ ++ if (!have_limits) { ++ bs->bl.min_mem_alignment = 512; ++ bs->bl.opt_mem_alignment = qemu_real_host_page_size(); ++ ++ /* Safe default since most protocols use readv()/writev()/etc */ ++ bs->bl.max_iov = IOV_MAX; ++ } ++ ++ /* Then let the driver override it */ ++ if (drv->bdrv_refresh_limits) { ++ drv->bdrv_refresh_limits(bs, errp); ++ if (*errp) { ++ return; ++ } ++ } ++ ++ if (bs->bl.request_alignment > BDRV_MAX_ALIGNMENT) { ++ error_setg(errp, "Driver requires too large request alignment"); ++ } ++} ++ ++/** ++ * The copy-on-read flag is actually a reference count so multiple users may ++ * use the feature without worrying about clobbering its previous state. ++ * Copy-on-read stays enabled until all users have called to disable it. ++ */ ++void bdrv_enable_copy_on_read(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ qatomic_inc(&bs->copy_on_read); ++} ++ ++void bdrv_disable_copy_on_read(BlockDriverState *bs) ++{ ++ int old = qatomic_fetch_dec(&bs->copy_on_read); ++ IO_CODE(); ++ assert(old >= 1); ++} ++ ++typedef struct { ++ Coroutine *co; ++ BlockDriverState *bs; ++ bool done; ++ bool begin; ++ bool poll; ++ BdrvChild *parent; ++} BdrvCoDrainData; ++ ++/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ ++bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent, ++ bool ignore_bds_parents) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ if (bdrv_parent_drained_poll(bs, ignore_parent, ignore_bds_parents)) { ++ return true; ++ } ++ ++ if (qatomic_read(&bs->in_flight)) { ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool bdrv_drain_poll_top_level(BlockDriverState *bs, ++ BdrvChild *ignore_parent) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ return bdrv_drain_poll(bs, ignore_parent, false); ++} ++ ++static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent, ++ bool poll); ++static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent); ++ ++static void bdrv_co_drain_bh_cb(void *opaque) ++{ ++ BdrvCoDrainData *data = opaque; ++ Coroutine *co = data->co; ++ BlockDriverState *bs = data->bs; ++ ++ if (bs) { ++ bdrv_dec_in_flight(bs); ++ if (data->begin) { ++ bdrv_do_drained_begin(bs, data->parent, data->poll); ++ } else { ++ assert(!data->poll); ++ bdrv_do_drained_end(bs, data->parent); ++ } ++ } else { ++ assert(data->begin); ++ bdrv_drain_all_begin(); ++ } ++ ++ data->done = true; ++ aio_co_wake(co); ++} ++ ++static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, ++ bool begin, ++ BdrvChild *parent, ++ bool poll) ++{ ++ BdrvCoDrainData data; ++ Coroutine *self = qemu_coroutine_self(); ++ ++ /* Calling bdrv_drain() from a BH ensures the current coroutine yields and ++ * other coroutines run if they were queued by aio_co_enter(). */ ++ ++ assert(qemu_in_coroutine()); ++ data = (BdrvCoDrainData) { ++ .co = self, ++ .bs = bs, ++ .done = false, ++ .begin = begin, ++ .parent = parent, ++ .poll = poll, ++ }; ++ ++ if (bs) { ++ bdrv_inc_in_flight(bs); ++ } ++ ++ replay_bh_schedule_oneshot_event(qemu_get_aio_context(), ++ bdrv_co_drain_bh_cb, &data); ++ ++ qemu_coroutine_yield(); ++ /* If we are resumed from some other event (such as an aio completion or a ++ * timer callback), it is a bug in the caller that should be fixed. */ ++ assert(data.done); ++} ++ ++static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent, ++ bool poll) ++{ ++ IO_OR_GS_CODE(); ++ ++ if (qemu_in_coroutine()) { ++ bdrv_co_yield_to_drain(bs, true, parent, poll); ++ return; ++ } ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* Stop things in parent-to-child order */ ++ if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) { ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ bdrv_parent_drained_begin(bs, parent); ++ if (bs->drv && bs->drv->bdrv_drain_begin) { ++ bs->drv->bdrv_drain_begin(bs); ++ } ++ } ++ ++ /* ++ * Wait for drained requests to finish. ++ * ++ * Calling BDRV_POLL_WHILE() only once for the top-level node is okay: The ++ * call is needed so things in this AioContext can make progress even ++ * though we don't return to the main AioContext loop - this automatically ++ * includes other nodes in the same AioContext and therefore all child ++ * nodes. ++ */ ++ if (poll) { ++ BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent)); ++ } ++} ++ ++void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent) ++{ ++ bdrv_do_drained_begin(bs, parent, false); ++} ++ ++void coroutine_mixed_fn ++bdrv_drained_begin(BlockDriverState *bs) ++{ ++ IO_OR_GS_CODE(); ++ bdrv_do_drained_begin(bs, NULL, true); ++} ++ ++/** ++ * This function does not poll, nor must any of its recursively called ++ * functions. ++ */ ++static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent) ++{ ++ int old_quiesce_counter; ++ ++ IO_OR_GS_CODE(); ++ ++ if (qemu_in_coroutine()) { ++ bdrv_co_yield_to_drain(bs, false, parent, false); ++ return; ++ } ++ ++ /* At this point, we should be always running in the main loop. */ ++ GLOBAL_STATE_CODE(); ++ assert(bs->quiesce_counter > 0); ++ GLOBAL_STATE_CODE(); ++ ++ /* Re-enable things in child-to-parent order */ ++ old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter); ++ if (old_quiesce_counter == 1) { ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ if (bs->drv && bs->drv->bdrv_drain_end) { ++ bs->drv->bdrv_drain_end(bs); ++ } ++ bdrv_parent_drained_end(bs, parent); ++ } ++} ++ ++void bdrv_drained_end(BlockDriverState *bs) ++{ ++ IO_OR_GS_CODE(); ++ bdrv_do_drained_end(bs, NULL); ++} ++ ++void bdrv_drain(BlockDriverState *bs) ++{ ++ IO_OR_GS_CODE(); ++ bdrv_drained_begin(bs); ++ bdrv_drained_end(bs); ++} ++ ++static void bdrv_drain_assert_idle(BlockDriverState *bs) ++{ ++ BdrvChild *child, *next; ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ assert(qatomic_read(&bs->in_flight) == 0); ++ QLIST_FOREACH_SAFE(child, &bs->children, next, next) { ++ bdrv_drain_assert_idle(child->bs); ++ } ++} ++ ++unsigned int bdrv_drain_all_count = 0; ++ ++static bool bdrv_drain_all_poll(void) ++{ ++ BlockDriverState *bs = NULL; ++ bool result = false; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ /* ++ * bdrv_drain_poll() can't make changes to the graph and we hold the BQL, ++ * so iterating bdrv_next_all_states() is safe. ++ */ ++ while ((bs = bdrv_next_all_states(bs))) { ++ result |= bdrv_drain_poll(bs, NULL, true); ++ } ++ ++ return result; ++} ++ ++/* ++ * Wait for pending requests to complete across all BlockDriverStates ++ * ++ * This function does not flush data to disk, use bdrv_flush_all() for that ++ * after calling this function. ++ * ++ * This pauses all block jobs and disables external clients. It must ++ * be paired with bdrv_drain_all_end(). ++ * ++ * NOTE: no new block jobs or BlockDriverStates can be created between ++ * the bdrv_drain_all_begin() and bdrv_drain_all_end() calls. ++ */ ++void bdrv_drain_all_begin_nopoll(void) ++{ ++ BlockDriverState *bs = NULL; ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * bdrv queue is managed by record/replay, ++ * waiting for finishing the I/O requests may ++ * be infinite ++ */ ++ if (replay_events_enabled()) { ++ return; ++ } ++ ++ /* AIO_WAIT_WHILE() with a NULL context can only be called from the main ++ * loop AioContext, so make sure we're in the main context. */ ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ assert(bdrv_drain_all_count < INT_MAX); ++ bdrv_drain_all_count++; ++ ++ /* Quiesce all nodes, without polling in-flight requests yet. The graph ++ * cannot change during this loop. */ ++ while ((bs = bdrv_next_all_states(bs))) { ++ bdrv_do_drained_begin(bs, NULL, false); ++ } ++} ++ ++void coroutine_mixed_fn bdrv_drain_all_begin(void) ++{ ++ BlockDriverState *bs = NULL; ++ ++ if (qemu_in_coroutine()) { ++ bdrv_co_yield_to_drain(NULL, true, NULL, true); ++ return; ++ } ++ ++ /* ++ * bdrv queue is managed by record/replay, ++ * waiting for finishing the I/O requests may ++ * be infinite ++ */ ++ if (replay_events_enabled()) { ++ return; ++ } ++ ++ bdrv_drain_all_begin_nopoll(); ++ ++ /* Now poll the in-flight requests */ ++ AIO_WAIT_WHILE_UNLOCKED(NULL, bdrv_drain_all_poll()); ++ ++ while ((bs = bdrv_next_all_states(bs))) { ++ bdrv_drain_assert_idle(bs); ++ } ++} ++ ++void bdrv_drain_all_end_quiesce(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ g_assert(bs->quiesce_counter > 0); ++ g_assert(!bs->refcnt); ++ ++ while (bs->quiesce_counter) { ++ bdrv_do_drained_end(bs, NULL); ++ } ++} ++ ++void bdrv_drain_all_end(void) ++{ ++ BlockDriverState *bs = NULL; ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * bdrv queue is managed by record/replay, ++ * waiting for finishing the I/O requests may ++ * be endless ++ */ ++ if (replay_events_enabled()) { ++ return; ++ } ++ ++ while ((bs = bdrv_next_all_states(bs))) { ++ bdrv_do_drained_end(bs, NULL); ++ } ++ ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ assert(bdrv_drain_all_count > 0); ++ bdrv_drain_all_count--; ++} ++ ++void bdrv_drain_all(void) ++{ ++ GLOBAL_STATE_CODE(); ++ bdrv_drain_all_begin(); ++ bdrv_drain_all_end(); ++} ++ ++/** ++ * Remove an active request from the tracked requests list ++ * ++ * This function should be called when a tracked request is completing. ++ */ ++static void coroutine_fn tracked_request_end(BdrvTrackedRequest *req) ++{ ++ if (req->serialising) { ++ qatomic_dec(&req->bs->serialising_in_flight); ++ } ++ ++ qemu_mutex_lock(&req->bs->reqs_lock); ++ QLIST_REMOVE(req, list); ++ qemu_mutex_unlock(&req->bs->reqs_lock); ++ ++ /* ++ * At this point qemu_co_queue_wait(&req->wait_queue, ...) won't be called ++ * anymore because the request has been removed from the list, so it's safe ++ * to restart the queue outside reqs_lock to minimize the critical section. ++ */ ++ qemu_co_queue_restart_all(&req->wait_queue); ++} ++ ++/** ++ * Add an active request to the tracked requests list ++ */ ++static void coroutine_fn tracked_request_begin(BdrvTrackedRequest *req, ++ BlockDriverState *bs, ++ int64_t offset, ++ int64_t bytes, ++ enum BdrvTrackedRequestType type) ++{ ++ bdrv_check_request(offset, bytes, &error_abort); ++ ++ *req = (BdrvTrackedRequest){ ++ .bs = bs, ++ .offset = offset, ++ .bytes = bytes, ++ .type = type, ++ .co = qemu_coroutine_self(), ++ .serialising = false, ++ .overlap_offset = offset, ++ .overlap_bytes = bytes, ++ }; ++ ++ qemu_co_queue_init(&req->wait_queue); ++ ++ qemu_mutex_lock(&bs->reqs_lock); ++ QLIST_INSERT_HEAD(&bs->tracked_requests, req, list); ++ qemu_mutex_unlock(&bs->reqs_lock); ++} ++ ++static bool tracked_request_overlaps(BdrvTrackedRequest *req, ++ int64_t offset, int64_t bytes) ++{ ++ bdrv_check_request(offset, bytes, &error_abort); ++ ++ /* aaaa bbbb */ ++ if (offset >= req->overlap_offset + req->overlap_bytes) { ++ return false; ++ } ++ /* bbbb aaaa */ ++ if (req->overlap_offset >= offset + bytes) { ++ return false; ++ } ++ return true; ++} ++ ++/* Called with self->bs->reqs_lock held */ ++static coroutine_fn BdrvTrackedRequest * ++bdrv_find_conflicting_request(BdrvTrackedRequest *self) ++{ ++ BdrvTrackedRequest *req; ++ ++ QLIST_FOREACH(req, &self->bs->tracked_requests, list) { ++ if (req == self || (!req->serialising && !self->serialising)) { ++ continue; ++ } ++ if (tracked_request_overlaps(req, self->overlap_offset, ++ self->overlap_bytes)) ++ { ++ /* ++ * Hitting this means there was a reentrant request, for ++ * example, a block driver issuing nested requests. This must ++ * never happen since it means deadlock. ++ */ ++ assert(qemu_coroutine_self() != req->co); ++ ++ /* ++ * If the request is already (indirectly) waiting for us, or ++ * will wait for us as soon as it wakes up, then just go on ++ * (instead of producing a deadlock in the former case). ++ */ ++ if (!req->waiting_for) { ++ return req; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++/* Called with self->bs->reqs_lock held */ ++static void coroutine_fn ++bdrv_wait_serialising_requests_locked(BdrvTrackedRequest *self) ++{ ++ BdrvTrackedRequest *req; ++ ++ while ((req = bdrv_find_conflicting_request(self))) { ++ self->waiting_for = req; ++ qemu_co_queue_wait(&req->wait_queue, &self->bs->reqs_lock); ++ self->waiting_for = NULL; ++ } ++} ++ ++/* Called with req->bs->reqs_lock held */ ++static void tracked_request_set_serialising(BdrvTrackedRequest *req, ++ uint64_t align) ++{ ++ int64_t overlap_offset = req->offset & ~(align - 1); ++ int64_t overlap_bytes = ++ ROUND_UP(req->offset + req->bytes, align) - overlap_offset; ++ ++ bdrv_check_request(req->offset, req->bytes, &error_abort); ++ ++ if (!req->serialising) { ++ qatomic_inc(&req->bs->serialising_in_flight); ++ req->serialising = true; ++ } ++ ++ req->overlap_offset = MIN(req->overlap_offset, overlap_offset); ++ req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes); ++} ++ ++/** ++ * Return the tracked request on @bs for the current coroutine, or ++ * NULL if there is none. ++ */ ++BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs) ++{ ++ BdrvTrackedRequest *req; ++ Coroutine *self = qemu_coroutine_self(); ++ IO_CODE(); ++ ++ QLIST_FOREACH(req, &bs->tracked_requests, list) { ++ if (req->co == self) { ++ return req; ++ } ++ } ++ ++ return NULL; ++} ++ ++/** ++ * Round a region to subcluster (if supported) or cluster boundaries ++ */ ++void coroutine_fn GRAPH_RDLOCK ++bdrv_round_to_subclusters(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ int64_t *align_offset, int64_t *align_bytes) ++{ ++ BlockDriverInfo bdi; ++ IO_CODE(); ++ if (bdrv_co_get_info(bs, &bdi) < 0 || bdi.subcluster_size == 0) { ++ *align_offset = offset; ++ *align_bytes = bytes; ++ } else { ++ int64_t c = bdi.subcluster_size; ++ *align_offset = QEMU_ALIGN_DOWN(offset, c); ++ *align_bytes = QEMU_ALIGN_UP(offset - *align_offset + bytes, c); ++ } ++} ++ ++static int coroutine_fn GRAPH_RDLOCK bdrv_get_cluster_size(BlockDriverState *bs) ++{ ++ BlockDriverInfo bdi; ++ int ret; ++ ++ ret = bdrv_co_get_info(bs, &bdi); ++ if (ret < 0 || bdi.cluster_size == 0) { ++ return bs->bl.request_alignment; ++ } else { ++ return bdi.cluster_size; ++ } ++} ++ ++void bdrv_inc_in_flight(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ qatomic_inc(&bs->in_flight); ++} ++ ++void bdrv_wakeup(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ aio_wait_kick(); ++} ++ ++void bdrv_dec_in_flight(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ qatomic_dec(&bs->in_flight); ++ bdrv_wakeup(bs); ++} ++ ++static void coroutine_fn ++bdrv_wait_serialising_requests(BdrvTrackedRequest *self) ++{ ++ BlockDriverState *bs = self->bs; ++ ++ if (!qatomic_read(&bs->serialising_in_flight)) { ++ return; ++ } ++ ++ qemu_mutex_lock(&bs->reqs_lock); ++ bdrv_wait_serialising_requests_locked(self); ++ qemu_mutex_unlock(&bs->reqs_lock); ++} ++ ++void coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, ++ uint64_t align) ++{ ++ IO_CODE(); ++ ++ qemu_mutex_lock(&req->bs->reqs_lock); ++ ++ tracked_request_set_serialising(req, align); ++ bdrv_wait_serialising_requests_locked(req); ++ ++ qemu_mutex_unlock(&req->bs->reqs_lock); ++} ++ ++int bdrv_check_qiov_request(int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ Error **errp) ++{ ++ /* ++ * Check generic offset/bytes correctness ++ */ ++ ++ if (offset < 0) { ++ error_setg(errp, "offset is negative: %" PRIi64, offset); ++ return -EIO; ++ } ++ ++ if (bytes < 0) { ++ error_setg(errp, "bytes is negative: %" PRIi64, bytes); ++ return -EIO; ++ } ++ ++ if (bytes > BDRV_MAX_LENGTH) { ++ error_setg(errp, "bytes(%" PRIi64 ") exceeds maximum(%" PRIi64 ")", ++ bytes, BDRV_MAX_LENGTH); ++ return -EIO; ++ } ++ ++ if (offset > BDRV_MAX_LENGTH) { ++ error_setg(errp, "offset(%" PRIi64 ") exceeds maximum(%" PRIi64 ")", ++ offset, BDRV_MAX_LENGTH); ++ return -EIO; ++ } ++ ++ if (offset > BDRV_MAX_LENGTH - bytes) { ++ error_setg(errp, "sum of offset(%" PRIi64 ") and bytes(%" PRIi64 ") " ++ "exceeds maximum(%" PRIi64 ")", offset, bytes, ++ BDRV_MAX_LENGTH); ++ return -EIO; ++ } ++ ++ if (!qiov) { ++ return 0; ++ } ++ ++ /* ++ * Check qiov and qiov_offset ++ */ ++ ++ if (qiov_offset > qiov->size) { ++ error_setg(errp, "qiov_offset(%zu) overflow io vector size(%zu)", ++ qiov_offset, qiov->size); ++ return -EIO; ++ } ++ ++ if (bytes > qiov->size - qiov_offset) { ++ error_setg(errp, "bytes(%" PRIi64 ") + qiov_offset(%zu) overflow io " ++ "vector size(%zu)", bytes, qiov_offset, qiov->size); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++int bdrv_check_request(int64_t offset, int64_t bytes, Error **errp) ++{ ++ return bdrv_check_qiov_request(offset, bytes, NULL, 0, errp); ++} ++ ++static int bdrv_check_request32(int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset) ++{ ++ int ret = bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (bytes > BDRV_REQUEST_MAX_BYTES) { ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Completely zero out a block device with the help of bdrv_pwrite_zeroes. ++ * The operation is sped up by checking the block status and only writing ++ * zeroes to the device if they currently do not return zeroes. Optional ++ * flags are passed through to bdrv_pwrite_zeroes (e.g. BDRV_REQ_MAY_UNMAP, ++ * BDRV_REQ_FUA). ++ * ++ * Returns < 0 on error, 0 on success. For error codes see bdrv_pwrite(). ++ */ ++int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags) ++{ ++ int ret; ++ int64_t target_size, bytes, offset = 0; ++ BlockDriverState *bs = child->bs; ++ IO_CODE(); ++ ++ target_size = bdrv_getlength(bs); ++ if (target_size < 0) { ++ return target_size; ++ } ++ ++ for (;;) { ++ bytes = MIN(target_size - offset, BDRV_REQUEST_MAX_BYTES); ++ if (bytes <= 0) { ++ return 0; ++ } ++ ret = bdrv_block_status(bs, offset, bytes, &bytes, NULL, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ if (ret & BDRV_BLOCK_ZERO) { ++ offset += bytes; ++ continue; ++ } ++ ret = bdrv_pwrite_zeroes(child, offset, bytes, flags); ++ if (ret < 0) { ++ return ret; ++ } ++ offset += bytes; ++ } ++} ++ ++/* ++ * Writes to the file and ensures that no writes are reordered across this ++ * request (acts as a barrier) ++ * ++ * Returns 0 on success, -errno in error cases. ++ */ ++int coroutine_fn bdrv_co_pwrite_sync(BdrvChild *child, int64_t offset, ++ int64_t bytes, const void *buf, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ ret = bdrv_co_pwrite(child, offset, bytes, buf, flags); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ ret = bdrv_co_flush(child->bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++typedef struct CoroutineIOCompletion { ++ Coroutine *coroutine; ++ int ret; ++} CoroutineIOCompletion; ++ ++static void bdrv_co_io_em_complete(void *opaque, int ret) ++{ ++ CoroutineIOCompletion *co = opaque; ++ ++ co->ret = ret; ++ aio_co_wake(co->coroutine); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_driver_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, int flags) ++{ ++ BlockDriver *drv = bs->drv; ++ int64_t sector_num; ++ unsigned int nb_sectors; ++ QEMUIOVector local_qiov; ++ int ret; ++ assert_bdrv_graph_readable(); ++ ++ bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); ++ assert(!(flags & ~bs->supported_read_flags)); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if (drv->bdrv_co_preadv_part) { ++ return drv->bdrv_co_preadv_part(bs, offset, bytes, qiov, qiov_offset, ++ flags); ++ } ++ ++ if (qiov_offset > 0 || bytes != qiov->size) { ++ qemu_iovec_init_slice(&local_qiov, qiov, qiov_offset, bytes); ++ qiov = &local_qiov; ++ } ++ ++ if (drv->bdrv_co_preadv) { ++ ret = drv->bdrv_co_preadv(bs, offset, bytes, qiov, flags); ++ goto out; ++ } ++ ++ if (drv->bdrv_aio_preadv) { ++ BlockAIOCB *acb; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ ++ acb = drv->bdrv_aio_preadv(bs, offset, bytes, qiov, flags, ++ bdrv_co_io_em_complete, &co); ++ if (acb == NULL) { ++ ret = -EIO; ++ goto out; ++ } else { ++ qemu_coroutine_yield(); ++ ret = co.ret; ++ goto out; ++ } ++ } ++ ++ sector_num = offset >> BDRV_SECTOR_BITS; ++ nb_sectors = bytes >> BDRV_SECTOR_BITS; ++ ++ assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)); ++ assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE)); ++ assert(bytes <= BDRV_REQUEST_MAX_BYTES); ++ assert(drv->bdrv_co_readv); ++ ++ ret = drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); ++ ++out: ++ if (qiov == &local_qiov) { ++ qemu_iovec_destroy(&local_qiov); ++ } ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_driver_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ BlockDriver *drv = bs->drv; ++ bool emulate_fua = false; ++ int64_t sector_num; ++ unsigned int nb_sectors; ++ QEMUIOVector local_qiov; ++ int ret; ++ assert_bdrv_graph_readable(); ++ ++ bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if ((flags & BDRV_REQ_FUA) && ++ (~bs->supported_write_flags & BDRV_REQ_FUA)) { ++ flags &= ~BDRV_REQ_FUA; ++ emulate_fua = true; ++ } ++ ++ flags &= bs->supported_write_flags; ++ ++ if (drv->bdrv_co_pwritev_part) { ++ ret = drv->bdrv_co_pwritev_part(bs, offset, bytes, qiov, qiov_offset, ++ flags); ++ goto emulate_flags; ++ } ++ ++ if (qiov_offset > 0 || bytes != qiov->size) { ++ qemu_iovec_init_slice(&local_qiov, qiov, qiov_offset, bytes); ++ qiov = &local_qiov; ++ } ++ ++ if (drv->bdrv_co_pwritev) { ++ ret = drv->bdrv_co_pwritev(bs, offset, bytes, qiov, flags); ++ goto emulate_flags; ++ } ++ ++ if (drv->bdrv_aio_pwritev) { ++ BlockAIOCB *acb; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ ++ acb = drv->bdrv_aio_pwritev(bs, offset, bytes, qiov, flags, ++ bdrv_co_io_em_complete, &co); ++ if (acb == NULL) { ++ ret = -EIO; ++ } else { ++ qemu_coroutine_yield(); ++ ret = co.ret; ++ } ++ goto emulate_flags; ++ } ++ ++ sector_num = offset >> BDRV_SECTOR_BITS; ++ nb_sectors = bytes >> BDRV_SECTOR_BITS; ++ ++ assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)); ++ assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE)); ++ assert(bytes <= BDRV_REQUEST_MAX_BYTES); ++ ++ assert(drv->bdrv_co_writev); ++ ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov, flags); ++ ++emulate_flags: ++ if (ret == 0 && emulate_fua) { ++ ret = bdrv_co_flush(bs); ++ } ++ ++ if (qiov == &local_qiov) { ++ qemu_iovec_destroy(&local_qiov); ++ } ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_driver_pwritev_compressed(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ size_t qiov_offset) ++{ ++ BlockDriver *drv = bs->drv; ++ QEMUIOVector local_qiov; ++ int ret; ++ assert_bdrv_graph_readable(); ++ ++ bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if (!block_driver_can_compress(drv)) { ++ return -ENOTSUP; ++ } ++ ++ if (drv->bdrv_co_pwritev_compressed_part) { ++ return drv->bdrv_co_pwritev_compressed_part(bs, offset, bytes, ++ qiov, qiov_offset); ++ } ++ ++ if (qiov_offset == 0) { ++ return drv->bdrv_co_pwritev_compressed(bs, offset, bytes, qiov); ++ } ++ ++ qemu_iovec_init_slice(&local_qiov, qiov, qiov_offset, bytes); ++ ret = drv->bdrv_co_pwritev_compressed(bs, offset, bytes, &local_qiov); ++ qemu_iovec_destroy(&local_qiov); ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, int flags) ++{ ++ BlockDriverState *bs = child->bs; ++ ++ /* Perform I/O through a temporary buffer so that users who scribble over ++ * their read buffer while the operation is in progress do not end up ++ * modifying the image file. This is critical for zero-copy guest I/O ++ * where anything might happen inside guest memory. ++ */ ++ void *bounce_buffer = NULL; ++ ++ BlockDriver *drv = bs->drv; ++ int64_t align_offset; ++ int64_t align_bytes; ++ int64_t skip_bytes; ++ int ret; ++ int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, ++ BDRV_REQUEST_MAX_BYTES); ++ int64_t progress = 0; ++ bool skip_write; ++ ++ bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ /* ++ * Do not write anything when the BDS is inactive. That is not ++ * allowed, and it would not help. ++ */ ++ skip_write = (bs->open_flags & BDRV_O_INACTIVE); ++ ++ /* FIXME We cannot require callers to have write permissions when all they ++ * are doing is a read request. If we did things right, write permissions ++ * would be obtained anyway, but internally by the copy-on-read code. As ++ * long as it is implemented here rather than in a separate filter driver, ++ * the copy-on-read code doesn't have its own BdrvChild, however, for which ++ * it could request permissions. Therefore we have to bypass the permission ++ * system for the moment. */ ++ // assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); ++ ++ /* Cover entire cluster so no additional backing file I/O is required when ++ * allocating cluster in the image file. Note that this value may exceed ++ * BDRV_REQUEST_MAX_BYTES (even when the original read did not), which ++ * is one reason we loop rather than doing it all at once. ++ */ ++ bdrv_round_to_subclusters(bs, offset, bytes, &align_offset, &align_bytes); ++ skip_bytes = offset - align_offset; ++ ++ trace_bdrv_co_do_copy_on_readv(bs, offset, bytes, ++ align_offset, align_bytes); ++ ++ while (align_bytes) { ++ int64_t pnum; ++ ++ if (skip_write) { ++ ret = 1; /* "already allocated", so nothing will be copied */ ++ pnum = MIN(align_bytes, max_transfer); ++ } else { ++ ret = bdrv_co_is_allocated(bs, align_offset, ++ MIN(align_bytes, max_transfer), &pnum); ++ if (ret < 0) { ++ /* ++ * Safe to treat errors in querying allocation as if ++ * unallocated; we'll probably fail again soon on the ++ * read, but at least that will set a decent errno. ++ */ ++ pnum = MIN(align_bytes, max_transfer); ++ } ++ ++ /* Stop at EOF if the image ends in the middle of the cluster */ ++ if (ret == 0 && pnum == 0) { ++ assert(progress >= bytes); ++ break; ++ } ++ ++ assert(skip_bytes < pnum); ++ } ++ ++ if (ret <= 0) { ++ QEMUIOVector local_qiov; ++ ++ /* Must copy-on-read; use the bounce buffer */ ++ pnum = MIN(pnum, MAX_BOUNCE_BUFFER); ++ if (!bounce_buffer) { ++ int64_t max_we_need = MAX(pnum, align_bytes - pnum); ++ int64_t max_allowed = MIN(max_transfer, MAX_BOUNCE_BUFFER); ++ int64_t bounce_buffer_len = MIN(max_we_need, max_allowed); ++ ++ bounce_buffer = qemu_try_blockalign(bs, bounce_buffer_len); ++ if (!bounce_buffer) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ } ++ qemu_iovec_init_buf(&local_qiov, bounce_buffer, pnum); ++ ++ ret = bdrv_driver_preadv(bs, align_offset, pnum, ++ &local_qiov, 0, 0); ++ if (ret < 0) { ++ goto err; ++ } ++ ++ bdrv_co_debug_event(bs, BLKDBG_COR_WRITE); ++ if (drv->bdrv_co_pwrite_zeroes && ++ buffer_is_zero(bounce_buffer, pnum)) { ++ /* FIXME: Should we (perhaps conditionally) be setting ++ * BDRV_REQ_MAY_UNMAP, if it will allow for a sparser copy ++ * that still correctly reads as zero? */ ++ ret = bdrv_co_do_pwrite_zeroes(bs, align_offset, pnum, ++ BDRV_REQ_WRITE_UNCHANGED); ++ } else { ++ /* This does not change the data on the disk, it is not ++ * necessary to flush even in cache=writethrough mode. ++ */ ++ ret = bdrv_driver_pwritev(bs, align_offset, pnum, ++ &local_qiov, 0, ++ BDRV_REQ_WRITE_UNCHANGED); ++ } ++ ++ if (ret < 0) { ++ /* It might be okay to ignore write errors for guest ++ * requests. If this is a deliberate copy-on-read ++ * then we don't want to ignore the error. Simply ++ * report it in all cases. ++ */ ++ goto err; ++ } ++ ++ if (!(flags & BDRV_REQ_PREFETCH)) { ++ qemu_iovec_from_buf(qiov, qiov_offset + progress, ++ bounce_buffer + skip_bytes, ++ MIN(pnum - skip_bytes, bytes - progress)); ++ } ++ } else if (!(flags & BDRV_REQ_PREFETCH)) { ++ /* Read directly into the destination */ ++ ret = bdrv_driver_preadv(bs, offset + progress, ++ MIN(pnum - skip_bytes, bytes - progress), ++ qiov, qiov_offset + progress, 0); ++ if (ret < 0) { ++ goto err; ++ } ++ } ++ ++ align_offset += pnum; ++ align_bytes -= pnum; ++ progress += pnum - skip_bytes; ++ skip_bytes = 0; ++ } ++ ret = 0; ++ ++err: ++ qemu_vfree(bounce_buffer); ++ return ret; ++} ++ ++/* ++ * Forwards an already correctly aligned request to the BlockDriver. This ++ * handles copy on read, zeroing after EOF, and fragmentation of large ++ * reads; any other features must be implemented by the caller. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_aligned_preadv(BdrvChild *child, BdrvTrackedRequest *req, ++ int64_t offset, int64_t bytes, int64_t align, ++ QEMUIOVector *qiov, size_t qiov_offset, int flags) ++{ ++ BlockDriverState *bs = child->bs; ++ int64_t total_bytes, max_bytes; ++ int ret = 0; ++ int64_t bytes_remaining = bytes; ++ int max_transfer; ++ ++ bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); ++ assert(is_power_of_2(align)); ++ assert((offset & (align - 1)) == 0); ++ assert((bytes & (align - 1)) == 0); ++ assert((bs->open_flags & BDRV_O_NO_IO) == 0); ++ max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX), ++ align); ++ ++ /* ++ * TODO: We would need a per-BDS .supported_read_flags and ++ * potential fallback support, if we ever implement any read flags ++ * to pass through to drivers. For now, there aren't any ++ * passthrough flags except the BDRV_REQ_REGISTERED_BUF optimization hint. ++ */ ++ assert(!(flags & ~(BDRV_REQ_COPY_ON_READ | BDRV_REQ_PREFETCH | ++ BDRV_REQ_REGISTERED_BUF))); ++ ++ /* Handle Copy on Read and associated serialisation */ ++ if (flags & BDRV_REQ_COPY_ON_READ) { ++ /* If we touch the same cluster it counts as an overlap. This ++ * guarantees that allocating writes will be serialized and not race ++ * with each other for the same cluster. For example, in copy-on-read ++ * it ensures that the CoR read and write operations are atomic and ++ * guest writes cannot interleave between them. */ ++ bdrv_make_request_serialising(req, bdrv_get_cluster_size(bs)); ++ } else { ++ bdrv_wait_serialising_requests(req); ++ } ++ ++ if (flags & BDRV_REQ_COPY_ON_READ) { ++ int64_t pnum; ++ ++ /* The flag BDRV_REQ_COPY_ON_READ has reached its addressee */ ++ flags &= ~BDRV_REQ_COPY_ON_READ; ++ ++ ret = bdrv_co_is_allocated(bs, offset, bytes, &pnum); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ if (!ret || pnum != bytes) { ++ ret = bdrv_co_do_copy_on_readv(child, offset, bytes, ++ qiov, qiov_offset, flags); ++ goto out; ++ } else if (flags & BDRV_REQ_PREFETCH) { ++ goto out; ++ } ++ } ++ ++ /* Forward the request to the BlockDriver, possibly fragmenting it */ ++ total_bytes = bdrv_co_getlength(bs); ++ if (total_bytes < 0) { ++ ret = total_bytes; ++ goto out; ++ } ++ ++ assert(!(flags & ~(bs->supported_read_flags | BDRV_REQ_REGISTERED_BUF))); ++ ++ max_bytes = ROUND_UP(MAX(0, total_bytes - offset), align); ++ if (bytes <= max_bytes && bytes <= max_transfer) { ++ ret = bdrv_driver_preadv(bs, offset, bytes, qiov, qiov_offset, flags); ++ goto out; ++ } ++ ++ while (bytes_remaining) { ++ int64_t num; ++ ++ if (max_bytes) { ++ num = MIN(bytes_remaining, MIN(max_bytes, max_transfer)); ++ assert(num); ++ ++ ret = bdrv_driver_preadv(bs, offset + bytes - bytes_remaining, ++ num, qiov, ++ qiov_offset + bytes - bytes_remaining, ++ flags); ++ max_bytes -= num; ++ } else { ++ num = bytes_remaining; ++ ret = qemu_iovec_memset(qiov, qiov_offset + bytes - bytes_remaining, ++ 0, bytes_remaining); ++ } ++ if (ret < 0) { ++ goto out; ++ } ++ bytes_remaining -= num; ++ } ++ ++out: ++ return ret < 0 ? ret : 0; ++} ++ ++/* ++ * Request padding ++ * ++ * |<---- align ----->| |<----- align ---->| ++ * |<- head ->|<------------- bytes ------------->|<-- tail -->| ++ * | | | | | | ++ * -*----------$-------*-------- ... --------*-----$------------*--- ++ * | | | | | | ++ * | offset | | end | ++ * ALIGN_DOWN(offset) ALIGN_UP(offset) ALIGN_DOWN(end) ALIGN_UP(end) ++ * [buf ... ) [tail_buf ) ++ * ++ * @buf is an aligned allocation needed to store @head and @tail paddings. @head ++ * is placed at the beginning of @buf and @tail at the @end. ++ * ++ * @tail_buf is a pointer to sub-buffer, corresponding to align-sized chunk ++ * around tail, if tail exists. ++ * ++ * @merge_reads is true for small requests, ++ * if @buf_len == @head + bytes + @tail. In this case it is possible that both ++ * head and tail exist but @buf_len == align and @tail_buf == @buf. ++ * ++ * @write is true for write requests, false for read requests. ++ * ++ * If padding makes the vector too long (exceeding IOV_MAX), then we need to ++ * merge existing vector elements into a single one. @collapse_bounce_buf acts ++ * as the bounce buffer in such cases. @pre_collapse_qiov has the pre-collapse ++ * I/O vector elements so for read requests, the data can be copied back after ++ * the read is done. ++ */ ++typedef struct BdrvRequestPadding { ++ uint8_t *buf; ++ size_t buf_len; ++ uint8_t *tail_buf; ++ size_t head; ++ size_t tail; ++ bool merge_reads; ++ bool write; ++ QEMUIOVector local_qiov; ++ ++ uint8_t *collapse_bounce_buf; ++ size_t collapse_len; ++ QEMUIOVector pre_collapse_qiov; ++} BdrvRequestPadding; ++ ++static bool bdrv_init_padding(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, ++ bool write, ++ BdrvRequestPadding *pad) ++{ ++ int64_t align = bs->bl.request_alignment; ++ int64_t sum; ++ ++ bdrv_check_request(offset, bytes, &error_abort); ++ assert(align <= INT_MAX); /* documented in block/block_int.h */ ++ assert(align <= SIZE_MAX / 2); /* so we can allocate the buffer */ ++ ++ memset(pad, 0, sizeof(*pad)); ++ ++ pad->head = offset & (align - 1); ++ pad->tail = ((offset + bytes) & (align - 1)); ++ if (pad->tail) { ++ pad->tail = align - pad->tail; ++ } ++ ++ if (!pad->head && !pad->tail) { ++ return false; ++ } ++ ++ assert(bytes); /* Nothing good in aligning zero-length requests */ ++ ++ sum = pad->head + bytes + pad->tail; ++ pad->buf_len = (sum > align && pad->head && pad->tail) ? 2 * align : align; ++ pad->buf = qemu_blockalign(bs, pad->buf_len); ++ pad->merge_reads = sum == pad->buf_len; ++ if (pad->tail) { ++ pad->tail_buf = pad->buf + pad->buf_len - align; ++ } ++ ++ pad->write = write; ++ ++ return true; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_padding_rmw_read(BdrvChild *child, BdrvTrackedRequest *req, ++ BdrvRequestPadding *pad, bool zero_middle) ++{ ++ QEMUIOVector local_qiov; ++ BlockDriverState *bs = child->bs; ++ uint64_t align = bs->bl.request_alignment; ++ int ret; ++ ++ assert(req->serialising && pad->buf); ++ ++ if (pad->head || pad->merge_reads) { ++ int64_t bytes = pad->merge_reads ? pad->buf_len : align; ++ ++ qemu_iovec_init_buf(&local_qiov, pad->buf, bytes); ++ ++ if (pad->head) { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); ++ } ++ if (pad->merge_reads && pad->tail) { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ++ } ++ ret = bdrv_aligned_preadv(child, req, req->overlap_offset, bytes, ++ align, &local_qiov, 0, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ if (pad->head) { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); ++ } ++ if (pad->merge_reads && pad->tail) { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); ++ } ++ ++ if (pad->merge_reads) { ++ goto zero_mem; ++ } ++ } ++ ++ if (pad->tail) { ++ qemu_iovec_init_buf(&local_qiov, pad->tail_buf, align); ++ ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ++ ret = bdrv_aligned_preadv( ++ child, req, ++ req->overlap_offset + req->overlap_bytes - align, ++ align, align, &local_qiov, 0, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); ++ } ++ ++zero_mem: ++ if (zero_middle) { ++ memset(pad->buf + pad->head, 0, pad->buf_len - pad->head - pad->tail); ++ } ++ ++ return 0; ++} ++ ++/** ++ * Free *pad's associated buffers, and perform any necessary finalization steps. ++ */ ++static void bdrv_padding_finalize(BdrvRequestPadding *pad) ++{ ++ if (pad->collapse_bounce_buf) { ++ if (!pad->write) { ++ /* ++ * If padding required elements in the vector to be collapsed into a ++ * bounce buffer, copy the bounce buffer content back ++ */ ++ qemu_iovec_from_buf(&pad->pre_collapse_qiov, 0, ++ pad->collapse_bounce_buf, pad->collapse_len); ++ } ++ qemu_vfree(pad->collapse_bounce_buf); ++ qemu_iovec_destroy(&pad->pre_collapse_qiov); ++ } ++ if (pad->buf) { ++ qemu_vfree(pad->buf); ++ qemu_iovec_destroy(&pad->local_qiov); ++ } ++ memset(pad, 0, sizeof(*pad)); ++} ++ ++/* ++ * Create pad->local_qiov by wrapping @iov in the padding head and tail, while ++ * ensuring that the resulting vector will not exceed IOV_MAX elements. ++ * ++ * To ensure this, when necessary, the first two or three elements of @iov are ++ * merged into pad->collapse_bounce_buf and replaced by a reference to that ++ * bounce buffer in pad->local_qiov. ++ * ++ * After performing a read request, the data from the bounce buffer must be ++ * copied back into pad->pre_collapse_qiov (e.g. by bdrv_padding_finalize()). ++ */ ++static int bdrv_create_padded_qiov(BlockDriverState *bs, ++ BdrvRequestPadding *pad, ++ struct iovec *iov, int niov, ++ size_t iov_offset, size_t bytes) ++{ ++ int padded_niov, surplus_count, collapse_count; ++ ++ /* Assert this invariant */ ++ assert(niov <= IOV_MAX); ++ ++ /* ++ * Cannot pad if resulting length would exceed SIZE_MAX. Returning an error ++ * to the guest is not ideal, but there is little else we can do. At least ++ * this will practically never happen on 64-bit systems. ++ */ ++ if (SIZE_MAX - pad->head < bytes || ++ SIZE_MAX - pad->head - bytes < pad->tail) ++ { ++ return -EINVAL; ++ } ++ ++ /* Length of the resulting IOV if we just concatenated everything */ ++ padded_niov = !!pad->head + niov + !!pad->tail; ++ ++ qemu_iovec_init(&pad->local_qiov, MIN(padded_niov, IOV_MAX)); ++ ++ if (pad->head) { ++ qemu_iovec_add(&pad->local_qiov, pad->buf, pad->head); ++ } ++ ++ /* ++ * If padded_niov > IOV_MAX, we cannot just concatenate everything. ++ * Instead, merge the first two or three elements of @iov to reduce the ++ * number of vector elements as necessary. ++ */ ++ if (padded_niov > IOV_MAX) { ++ /* ++ * Only head and tail can have lead to the number of entries exceeding ++ * IOV_MAX, so we can exceed it by the head and tail at most. We need ++ * to reduce the number of elements by `surplus_count`, so we merge that ++ * many elements plus one into one element. ++ */ ++ surplus_count = padded_niov - IOV_MAX; ++ assert(surplus_count <= !!pad->head + !!pad->tail); ++ collapse_count = surplus_count + 1; ++ ++ /* ++ * Move the elements to collapse into `pad->pre_collapse_qiov`, then ++ * advance `iov` (and associated variables) by those elements. ++ */ ++ qemu_iovec_init(&pad->pre_collapse_qiov, collapse_count); ++ qemu_iovec_concat_iov(&pad->pre_collapse_qiov, iov, ++ collapse_count, iov_offset, SIZE_MAX); ++ iov += collapse_count; ++ iov_offset = 0; ++ niov -= collapse_count; ++ bytes -= pad->pre_collapse_qiov.size; ++ ++ /* ++ * Construct the bounce buffer to match the length of the to-collapse ++ * vector elements, and for write requests, initialize it with the data ++ * from those elements. Then add it to `pad->local_qiov`. ++ */ ++ pad->collapse_len = pad->pre_collapse_qiov.size; ++ pad->collapse_bounce_buf = qemu_blockalign(bs, pad->collapse_len); ++ if (pad->write) { ++ qemu_iovec_to_buf(&pad->pre_collapse_qiov, 0, ++ pad->collapse_bounce_buf, pad->collapse_len); ++ } ++ qemu_iovec_add(&pad->local_qiov, ++ pad->collapse_bounce_buf, pad->collapse_len); ++ } ++ ++ qemu_iovec_concat_iov(&pad->local_qiov, iov, niov, iov_offset, bytes); ++ ++ if (pad->tail) { ++ qemu_iovec_add(&pad->local_qiov, ++ pad->buf + pad->buf_len - pad->tail, pad->tail); ++ } ++ ++ assert(pad->local_qiov.niov == MIN(padded_niov, IOV_MAX)); ++ return 0; ++} ++ ++/* ++ * bdrv_pad_request ++ * ++ * Exchange request parameters with padded request if needed. Don't include RMW ++ * read of padding, bdrv_padding_rmw_read() should be called separately if ++ * needed. ++ * ++ * @write is true for write requests, false for read requests. ++ * ++ * Request parameters (@qiov, &qiov_offset, &offset, &bytes) are in-out: ++ * - on function start they represent original request ++ * - on failure or when padding is not needed they are unchanged ++ * - on success when padding is needed they represent padded request ++ */ ++static int bdrv_pad_request(BlockDriverState *bs, ++ QEMUIOVector **qiov, size_t *qiov_offset, ++ int64_t *offset, int64_t *bytes, ++ bool write, ++ BdrvRequestPadding *pad, bool *padded, ++ BdrvRequestFlags *flags) ++{ ++ int ret; ++ struct iovec *sliced_iov; ++ int sliced_niov; ++ size_t sliced_head, sliced_tail; ++ ++ /* Should have been checked by the caller already */ ++ ret = bdrv_check_request32(*offset, *bytes, *qiov, *qiov_offset); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (!bdrv_init_padding(bs, *offset, *bytes, write, pad)) { ++ if (padded) { ++ *padded = false; ++ } ++ return 0; ++ } ++ ++ /* ++ * For prefetching in stream_populate(), no qiov is passed along, because ++ * only copy-on-read matters. ++ */ ++ if (*qiov) { ++ sliced_iov = qemu_iovec_slice(*qiov, *qiov_offset, *bytes, ++ &sliced_head, &sliced_tail, ++ &sliced_niov); ++ ++ /* Guaranteed by bdrv_check_request32() */ ++ assert(*bytes <= SIZE_MAX); ++ ret = bdrv_create_padded_qiov(bs, pad, sliced_iov, sliced_niov, ++ sliced_head, *bytes); ++ if (ret < 0) { ++ bdrv_padding_finalize(pad); ++ return ret; ++ } ++ *qiov = &pad->local_qiov; ++ *qiov_offset = 0; ++ } ++ ++ *bytes += pad->head + pad->tail; ++ *offset -= pad->head; ++ if (padded) { ++ *padded = true; ++ } ++ if (flags) { ++ /* Can't use optimization hint with bounce buffer */ ++ *flags &= ~BDRV_REQ_REGISTERED_BUF; ++ } ++ ++ return 0; ++} ++ ++int coroutine_fn bdrv_co_preadv(BdrvChild *child, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ IO_CODE(); ++ return bdrv_co_preadv_part(child, offset, bytes, qiov, 0, flags); ++} ++ ++int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, ++ int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ BlockDriverState *bs = child->bs; ++ BdrvTrackedRequest req; ++ BdrvRequestPadding pad; ++ int ret; ++ IO_CODE(); ++ ++ trace_bdrv_co_preadv_part(bs, offset, bytes, flags); ++ ++ if (!bdrv_co_is_inserted(bs)) { ++ return -ENOMEDIUM; ++ } ++ ++ ret = bdrv_check_request32(offset, bytes, qiov, qiov_offset); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (bytes == 0 && !QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)) { ++ /* ++ * Aligning zero request is nonsense. Even if driver has special meaning ++ * of zero-length (like qcow2_co_pwritev_compressed_part), we can't pass ++ * it to driver due to request_alignment. ++ * ++ * Still, no reason to return an error if someone do unaligned ++ * zero-length read occasionally. ++ */ ++ return 0; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ++ /* Don't do copy-on-read if we read data before write operation */ ++ if (qatomic_read(&bs->copy_on_read)) { ++ flags |= BDRV_REQ_COPY_ON_READ; ++ } ++ ++ ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, false, ++ &pad, NULL, &flags); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_READ); ++ ret = bdrv_aligned_preadv(child, &req, offset, bytes, ++ bs->bl.request_alignment, ++ qiov, qiov_offset, flags); ++ tracked_request_end(&req); ++ bdrv_padding_finalize(&pad); ++ ++fail: ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags) ++{ ++ BlockDriver *drv = bs->drv; ++ QEMUIOVector qiov; ++ void *buf = NULL; ++ int ret = 0; ++ bool need_flush = false; ++ int head = 0; ++ int tail = 0; ++ ++ int64_t max_write_zeroes = MIN_NON_ZERO(bs->bl.max_pwrite_zeroes, ++ INT64_MAX); ++ int alignment = MAX(bs->bl.pwrite_zeroes_alignment, ++ bs->bl.request_alignment); ++ int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, MAX_BOUNCE_BUFFER); ++ ++ assert_bdrv_graph_readable(); ++ bdrv_check_request(offset, bytes, &error_abort); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if ((flags & ~bs->supported_zero_flags) & BDRV_REQ_NO_FALLBACK) { ++ return -ENOTSUP; ++ } ++ ++ /* By definition there is no user buffer so this flag doesn't make sense */ ++ if (flags & BDRV_REQ_REGISTERED_BUF) { ++ return -EINVAL; ++ } ++ ++ /* If opened with discard=off we should never unmap. */ ++ if (!(bs->open_flags & BDRV_O_UNMAP)) { ++ flags &= ~BDRV_REQ_MAY_UNMAP; ++ } ++ ++ /* Invalidate the cached block-status data range if this write overlaps */ ++ bdrv_bsc_invalidate_range(bs, offset, bytes); ++ ++ assert(alignment % bs->bl.request_alignment == 0); ++ head = offset % alignment; ++ tail = (offset + bytes) % alignment; ++ max_write_zeroes = QEMU_ALIGN_DOWN(max_write_zeroes, alignment); ++ assert(max_write_zeroes >= bs->bl.request_alignment); ++ ++ while (bytes > 0 && !ret) { ++ int64_t num = bytes; ++ ++ /* Align request. Block drivers can expect the "bulk" of the request ++ * to be aligned, and that unaligned requests do not cross cluster ++ * boundaries. ++ */ ++ if (head) { ++ /* Make a small request up to the first aligned sector. For ++ * convenience, limit this request to max_transfer even if ++ * we don't need to fall back to writes. */ ++ num = MIN(MIN(bytes, max_transfer), alignment - head); ++ head = (head + num) % alignment; ++ assert(num < max_write_zeroes); ++ } else if (tail && num > alignment) { ++ /* Shorten the request to the last aligned sector. */ ++ num -= tail; ++ } ++ ++ /* limit request size */ ++ if (num > max_write_zeroes) { ++ num = max_write_zeroes; ++ } ++ ++ ret = -ENOTSUP; ++ /* First try the efficient write zeroes operation */ ++ if (drv->bdrv_co_pwrite_zeroes) { ++ ret = drv->bdrv_co_pwrite_zeroes(bs, offset, num, ++ flags & bs->supported_zero_flags); ++ if (ret != -ENOTSUP && (flags & BDRV_REQ_FUA) && ++ !(bs->supported_zero_flags & BDRV_REQ_FUA)) { ++ need_flush = true; ++ } ++ } else { ++ assert(!bs->supported_zero_flags); ++ } ++ ++ if (ret == -ENOTSUP && !(flags & BDRV_REQ_NO_FALLBACK)) { ++ /* Fall back to bounce buffer if write zeroes is unsupported */ ++ BdrvRequestFlags write_flags = flags & ~BDRV_REQ_ZERO_WRITE; ++ ++ if ((flags & BDRV_REQ_FUA) && ++ !(bs->supported_write_flags & BDRV_REQ_FUA)) { ++ /* No need for bdrv_driver_pwrite() to do a fallback ++ * flush on each chunk; use just one at the end */ ++ write_flags &= ~BDRV_REQ_FUA; ++ need_flush = true; ++ } ++ num = MIN(num, max_transfer); ++ if (buf == NULL) { ++ buf = qemu_try_blockalign0(bs, num); ++ if (buf == NULL) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ } ++ qemu_iovec_init_buf(&qiov, buf, num); ++ ++ ret = bdrv_driver_pwritev(bs, offset, num, &qiov, 0, write_flags); ++ ++ /* Keep bounce buffer around if it is big enough for all ++ * all future requests. ++ */ ++ if (num < max_transfer) { ++ qemu_vfree(buf); ++ buf = NULL; ++ } ++ } ++ ++ offset += num; ++ bytes -= num; ++ } ++ ++fail: ++ if (ret == 0 && need_flush) { ++ ret = bdrv_co_flush(bs); ++ } ++ qemu_vfree(buf); ++ return ret; ++} ++ ++static inline int coroutine_fn GRAPH_RDLOCK ++bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes, ++ BdrvTrackedRequest *req, int flags) ++{ ++ BlockDriverState *bs = child->bs; ++ ++ bdrv_check_request(offset, bytes, &error_abort); ++ ++ if (bdrv_is_read_only(bs)) { ++ return -EPERM; ++ } ++ ++ assert(!(bs->open_flags & BDRV_O_INACTIVE)); ++ assert((bs->open_flags & BDRV_O_NO_IO) == 0); ++ assert(!(flags & ~BDRV_REQ_MASK)); ++ assert(!((flags & BDRV_REQ_NO_WAIT) && !(flags & BDRV_REQ_SERIALISING))); ++ ++ if (flags & BDRV_REQ_SERIALISING) { ++ QEMU_LOCK_GUARD(&bs->reqs_lock); ++ ++ tracked_request_set_serialising(req, bdrv_get_cluster_size(bs)); ++ ++ if ((flags & BDRV_REQ_NO_WAIT) && bdrv_find_conflicting_request(req)) { ++ return -EBUSY; ++ } ++ ++ bdrv_wait_serialising_requests_locked(req); ++ } else { ++ bdrv_wait_serialising_requests(req); ++ } ++ ++ assert(req->overlap_offset <= offset); ++ assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); ++ assert(offset + bytes <= bs->total_sectors * BDRV_SECTOR_SIZE || ++ child->perm & BLK_PERM_RESIZE); ++ ++ switch (req->type) { ++ case BDRV_TRACKED_WRITE: ++ case BDRV_TRACKED_DISCARD: ++ if (flags & BDRV_REQ_WRITE_UNCHANGED) { ++ assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); ++ } else { ++ assert(child->perm & BLK_PERM_WRITE); ++ } ++ bdrv_write_threshold_check_write(bs, offset, bytes); ++ return 0; ++ case BDRV_TRACKED_TRUNCATE: ++ assert(child->perm & BLK_PERM_RESIZE); ++ return 0; ++ default: ++ abort(); ++ } ++} ++ ++static inline void coroutine_fn GRAPH_RDLOCK ++bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, int64_t bytes, ++ BdrvTrackedRequest *req, int ret) ++{ ++ int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); ++ BlockDriverState *bs = child->bs; ++ ++ bdrv_check_request(offset, bytes, &error_abort); ++ ++ qatomic_inc(&bs->write_gen); ++ ++ /* ++ * Discard cannot extend the image, but in error handling cases, such as ++ * when reverting a qcow2 cluster allocation, the discarded range can pass ++ * the end of image file, so we cannot assert about BDRV_TRACKED_DISCARD ++ * here. Instead, just skip it, since semantically a discard request ++ * beyond EOF cannot expand the image anyway. ++ */ ++ if (ret == 0 && ++ (req->type == BDRV_TRACKED_TRUNCATE || ++ end_sector > bs->total_sectors) && ++ req->type != BDRV_TRACKED_DISCARD) { ++ bs->total_sectors = end_sector; ++ bdrv_parent_cb_resize(bs); ++ bdrv_dirty_bitmap_truncate(bs, end_sector << BDRV_SECTOR_BITS); ++ } ++ if (req->bytes) { ++ switch (req->type) { ++ case BDRV_TRACKED_WRITE: ++ stat64_max(&bs->wr_highest_offset, offset + bytes); ++ /* fall through, to set dirty bits */ ++ case BDRV_TRACKED_DISCARD: ++ bdrv_set_dirty(bs, offset, bytes); ++ break; ++ default: ++ break; ++ } ++ } ++} ++ ++/* ++ * Forwards an already correctly aligned write request to the BlockDriver, ++ * after possibly fragmenting it. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_aligned_pwritev(BdrvChild *child, BdrvTrackedRequest *req, ++ int64_t offset, int64_t bytes, int64_t align, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ BlockDriverState *bs = child->bs; ++ BlockDriver *drv = bs->drv; ++ int ret; ++ ++ int64_t bytes_remaining = bytes; ++ int max_transfer; ++ ++ bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if (bdrv_has_readonly_bitmaps(bs)) { ++ return -EPERM; ++ } ++ ++ assert(is_power_of_2(align)); ++ assert((offset & (align - 1)) == 0); ++ assert((bytes & (align - 1)) == 0); ++ max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX), ++ align); ++ ++ ret = bdrv_co_write_req_prepare(child, offset, bytes, req, flags); ++ ++ if (!ret && bs->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF && ++ !(flags & BDRV_REQ_ZERO_WRITE) && drv->bdrv_co_pwrite_zeroes && ++ qemu_iovec_is_zero(qiov, qiov_offset, bytes)) { ++ flags |= BDRV_REQ_ZERO_WRITE; ++ if (bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP) { ++ flags |= BDRV_REQ_MAY_UNMAP; ++ } ++ ++ /* Can't use optimization hint with bufferless zero write */ ++ flags &= ~BDRV_REQ_REGISTERED_BUF; ++ } ++ ++ if (ret < 0) { ++ /* Do nothing, write notifier decided to fail this request */ ++ } else if (flags & BDRV_REQ_ZERO_WRITE) { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_ZERO); ++ ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags); ++ } else if (flags & BDRV_REQ_WRITE_COMPRESSED) { ++ ret = bdrv_driver_pwritev_compressed(bs, offset, bytes, ++ qiov, qiov_offset); ++ } else if (bytes <= max_transfer) { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV); ++ ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, qiov_offset, flags); ++ } else { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV); ++ while (bytes_remaining) { ++ int num = MIN(bytes_remaining, max_transfer); ++ int local_flags = flags; ++ ++ assert(num); ++ if (num < bytes_remaining && (flags & BDRV_REQ_FUA) && ++ !(bs->supported_write_flags & BDRV_REQ_FUA)) { ++ /* If FUA is going to be emulated by flush, we only ++ * need to flush on the last iteration */ ++ local_flags &= ~BDRV_REQ_FUA; ++ } ++ ++ ret = bdrv_driver_pwritev(bs, offset + bytes - bytes_remaining, ++ num, qiov, ++ qiov_offset + bytes - bytes_remaining, ++ local_flags); ++ if (ret < 0) { ++ break; ++ } ++ bytes_remaining -= num; ++ } ++ } ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_DONE); ++ ++ if (ret >= 0) { ++ ret = 0; ++ } ++ bdrv_co_write_req_finish(child, offset, bytes, req, ret); ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_co_do_zero_pwritev(BdrvChild *child, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags, BdrvTrackedRequest *req) ++{ ++ BlockDriverState *bs = child->bs; ++ QEMUIOVector local_qiov; ++ uint64_t align = bs->bl.request_alignment; ++ int ret = 0; ++ bool padding; ++ BdrvRequestPadding pad; ++ ++ /* This flag doesn't make sense for padding or zero writes */ ++ flags &= ~BDRV_REQ_REGISTERED_BUF; ++ ++ padding = bdrv_init_padding(bs, offset, bytes, true, &pad); ++ if (padding) { ++ assert(!(flags & BDRV_REQ_NO_WAIT)); ++ bdrv_make_request_serialising(req, align); ++ ++ bdrv_padding_rmw_read(child, req, &pad, true); ++ ++ if (pad.head || pad.merge_reads) { ++ int64_t aligned_offset = offset & ~(align - 1); ++ int64_t write_bytes = pad.merge_reads ? pad.buf_len : align; ++ ++ qemu_iovec_init_buf(&local_qiov, pad.buf, write_bytes); ++ ret = bdrv_aligned_pwritev(child, req, aligned_offset, write_bytes, ++ align, &local_qiov, 0, ++ flags & ~BDRV_REQ_ZERO_WRITE); ++ if (ret < 0 || pad.merge_reads) { ++ /* Error or all work is done */ ++ goto out; ++ } ++ offset += write_bytes - pad.head; ++ bytes -= write_bytes - pad.head; ++ } ++ } ++ ++ assert(!bytes || (offset & (align - 1)) == 0); ++ if (bytes >= align) { ++ /* Write the aligned part in the middle. */ ++ int64_t aligned_bytes = bytes & ~(align - 1); ++ ret = bdrv_aligned_pwritev(child, req, offset, aligned_bytes, align, ++ NULL, 0, flags); ++ if (ret < 0) { ++ goto out; ++ } ++ bytes -= aligned_bytes; ++ offset += aligned_bytes; ++ } ++ ++ assert(!bytes || (offset & (align - 1)) == 0); ++ if (bytes) { ++ assert(align == pad.tail + bytes); ++ ++ qemu_iovec_init_buf(&local_qiov, pad.tail_buf, align); ++ ret = bdrv_aligned_pwritev(child, req, offset, align, align, ++ &local_qiov, 0, ++ flags & ~BDRV_REQ_ZERO_WRITE); ++ } ++ ++out: ++ bdrv_padding_finalize(&pad); ++ ++ return ret; ++} ++ ++/* ++ * Handle a write request in coroutine context ++ */ ++int coroutine_fn bdrv_co_pwritev(BdrvChild *child, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ IO_CODE(); ++ return bdrv_co_pwritev_part(child, offset, bytes, qiov, 0, flags); ++} ++ ++int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ BlockDriverState *bs = child->bs; ++ BdrvTrackedRequest req; ++ uint64_t align = bs->bl.request_alignment; ++ BdrvRequestPadding pad; ++ int ret; ++ bool padded = false; ++ IO_CODE(); ++ ++ trace_bdrv_co_pwritev_part(child->bs, offset, bytes, flags); ++ ++ if (!bdrv_co_is_inserted(bs)) { ++ return -ENOMEDIUM; ++ } ++ ++ if (flags & BDRV_REQ_ZERO_WRITE) { ++ ret = bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, NULL); ++ } else { ++ ret = bdrv_check_request32(offset, bytes, qiov, qiov_offset); ++ } ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* If the request is misaligned then we can't make it efficient */ ++ if ((flags & BDRV_REQ_NO_FALLBACK) && ++ !QEMU_IS_ALIGNED(offset | bytes, align)) ++ { ++ return -ENOTSUP; ++ } ++ ++ if (bytes == 0 && !QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)) { ++ /* ++ * Aligning zero request is nonsense. Even if driver has special meaning ++ * of zero-length (like qcow2_co_pwritev_compressed_part), we can't pass ++ * it to driver due to request_alignment. ++ * ++ * Still, no reason to return an error if someone do unaligned ++ * zero-length write occasionally. ++ */ ++ return 0; ++ } ++ ++ if (!(flags & BDRV_REQ_ZERO_WRITE)) { ++ /* ++ * Pad request for following read-modify-write cycle. ++ * bdrv_co_do_zero_pwritev() does aligning by itself, so, we do ++ * alignment only if there is no ZERO flag. ++ */ ++ ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, true, ++ &pad, &padded, &flags); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ bdrv_inc_in_flight(bs); ++ tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_WRITE); ++ ++ if (flags & BDRV_REQ_ZERO_WRITE) { ++ assert(!padded); ++ ret = bdrv_co_do_zero_pwritev(child, offset, bytes, flags, &req); ++ goto out; ++ } ++ ++ if (padded) { ++ /* ++ * Request was unaligned to request_alignment and therefore ++ * padded. We are going to do read-modify-write, and must ++ * serialize the request to prevent interactions of the ++ * widened region with other transactions. ++ */ ++ assert(!(flags & BDRV_REQ_NO_WAIT)); ++ bdrv_make_request_serialising(&req, align); ++ bdrv_padding_rmw_read(child, &req, &pad, false); ++ } ++ ++ ret = bdrv_aligned_pwritev(child, &req, offset, bytes, align, ++ qiov, qiov_offset, flags); ++ ++ bdrv_padding_finalize(&pad); ++ ++out: ++ tracked_request_end(&req); ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags) ++{ ++ IO_CODE(); ++ trace_bdrv_co_pwrite_zeroes(child->bs, offset, bytes, flags); ++ assert_bdrv_graph_readable(); ++ ++ return bdrv_co_pwritev(child, offset, bytes, NULL, ++ BDRV_REQ_ZERO_WRITE | flags); ++} ++ ++/* ++ * Flush ALL BDSes regardless of if they are reachable via a BlkBackend or not. ++ */ ++int bdrv_flush_all(void) ++{ ++ BdrvNextIterator it; ++ BlockDriverState *bs = NULL; ++ int result = 0; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ /* ++ * bdrv queue is managed by record/replay, ++ * creating new flush request for stopping ++ * the VM may break the determinism ++ */ ++ if (replay_events_enabled()) { ++ return result; ++ } ++ ++ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { ++ int ret = bdrv_flush(bs); ++ if (ret < 0 && !result) { ++ result = ret; ++ } ++ } ++ ++ return result; ++} ++ ++/* ++ * Returns the allocation status of the specified sectors. ++ * Drivers not implementing the functionality are assumed to not support ++ * backing files, hence all their sectors are reported as allocated. ++ * ++ * If 'want_zero' is true, the caller is querying for mapping ++ * purposes, with a focus on valid BDRV_BLOCK_OFFSET_VALID, _DATA, and ++ * _ZERO where possible; otherwise, the result favors larger 'pnum', ++ * with a focus on accurate BDRV_BLOCK_ALLOCATED. ++ * ++ * If 'offset' is beyond the end of the disk image the return value is ++ * BDRV_BLOCK_EOF and 'pnum' is set to 0. ++ * ++ * 'bytes' is the max value 'pnum' should be set to. If bytes goes ++ * beyond the end of the disk image it will be clamped; if 'pnum' is set to ++ * the end of the image, then the returned value will include BDRV_BLOCK_EOF. ++ * ++ * 'pnum' is set to the number of bytes (including and immediately ++ * following the specified offset) that are easily known to be in the ++ * same allocated/unallocated state. Note that a second call starting ++ * at the original offset plus returned pnum may have the same status. ++ * The returned value is non-zero on success except at end-of-file. ++ * ++ * Returns negative errno on failure. Otherwise, if the ++ * BDRV_BLOCK_OFFSET_VALID bit is set, 'map' and 'file' (if non-NULL) are ++ * set to the host mapping and BDS corresponding to the guest offset. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero, ++ int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, BlockDriverState **file) ++{ ++ int64_t total_size; ++ int64_t n; /* bytes */ ++ int ret; ++ int64_t local_map = 0; ++ BlockDriverState *local_file = NULL; ++ int64_t aligned_offset, aligned_bytes; ++ uint32_t align; ++ bool has_filtered_child; ++ ++ assert(pnum); ++ assert_bdrv_graph_readable(); ++ *pnum = 0; ++ total_size = bdrv_co_getlength(bs); ++ if (total_size < 0) { ++ ret = total_size; ++ goto early_out; ++ } ++ ++ if (offset >= total_size) { ++ ret = BDRV_BLOCK_EOF; ++ goto early_out; ++ } ++ if (!bytes) { ++ ret = 0; ++ goto early_out; ++ } ++ ++ n = total_size - offset; ++ if (n < bytes) { ++ bytes = n; ++ } ++ ++ /* Must be non-NULL or bdrv_co_getlength() would have failed */ ++ assert(bs->drv); ++ has_filtered_child = bdrv_filter_child(bs); ++ if (!bs->drv->bdrv_co_block_status && !has_filtered_child) { ++ *pnum = bytes; ++ ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED; ++ if (offset + bytes == total_size) { ++ ret |= BDRV_BLOCK_EOF; ++ } ++ if (bs->drv->protocol_name) { ++ ret |= BDRV_BLOCK_OFFSET_VALID; ++ local_map = offset; ++ local_file = bs; ++ } ++ goto early_out; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ++ /* Round out to request_alignment boundaries */ ++ align = bs->bl.request_alignment; ++ aligned_offset = QEMU_ALIGN_DOWN(offset, align); ++ aligned_bytes = ROUND_UP(offset + bytes, align) - aligned_offset; ++ ++ if (bs->drv->bdrv_co_block_status) { ++ /* ++ * Use the block-status cache only for protocol nodes: Format ++ * drivers are generally quick to inquire the status, but protocol ++ * drivers often need to get information from outside of qemu, so ++ * we do not have control over the actual implementation. There ++ * have been cases where inquiring the status took an unreasonably ++ * long time, and we can do nothing in qemu to fix it. ++ * This is especially problematic for images with large data areas, ++ * because finding the few holes in them and giving them special ++ * treatment does not gain much performance. Therefore, we try to ++ * cache the last-identified data region. ++ * ++ * Second, limiting ourselves to protocol nodes allows us to assume ++ * the block status for data regions to be DATA | OFFSET_VALID, and ++ * that the host offset is the same as the guest offset. ++ * ++ * Note that it is possible that external writers zero parts of ++ * the cached regions without the cache being invalidated, and so ++ * we may report zeroes as data. This is not catastrophic, ++ * however, because reporting zeroes as data is fine. ++ */ ++ if (QLIST_EMPTY(&bs->children) && ++ bdrv_bsc_is_data(bs, aligned_offset, pnum)) ++ { ++ ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; ++ local_file = bs; ++ local_map = aligned_offset; ++ } else { ++ ret = bs->drv->bdrv_co_block_status(bs, want_zero, aligned_offset, ++ aligned_bytes, pnum, &local_map, ++ &local_file); ++ ++ /* ++ * Note that checking QLIST_EMPTY(&bs->children) is also done when ++ * the cache is queried above. Technically, we do not need to check ++ * it here; the worst that can happen is that we fill the cache for ++ * non-protocol nodes, and then it is never used. However, filling ++ * the cache requires an RCU update, so double check here to avoid ++ * such an update if possible. ++ * ++ * Check want_zero, because we only want to update the cache when we ++ * have accurate information about what is zero and what is data. ++ */ ++ if (want_zero && ++ ret == (BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID) && ++ QLIST_EMPTY(&bs->children)) ++ { ++ /* ++ * When a protocol driver reports BLOCK_OFFSET_VALID, the ++ * returned local_map value must be the same as the offset we ++ * have passed (aligned_offset), and local_bs must be the node ++ * itself. ++ * Assert this, because we follow this rule when reading from ++ * the cache (see the `local_file = bs` and ++ * `local_map = aligned_offset` assignments above), and the ++ * result the cache delivers must be the same as the driver ++ * would deliver. ++ */ ++ assert(local_file == bs); ++ assert(local_map == aligned_offset); ++ bdrv_bsc_fill(bs, aligned_offset, *pnum); ++ } ++ } ++ } else { ++ /* Default code for filters */ ++ ++ local_file = bdrv_filter_bs(bs); ++ assert(local_file); ++ ++ *pnum = aligned_bytes; ++ local_map = aligned_offset; ++ ret = BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID; ++ } ++ if (ret < 0) { ++ *pnum = 0; ++ goto out; ++ } ++ ++ /* ++ * The driver's result must be a non-zero multiple of request_alignment. ++ * Clamp pnum and adjust map to original request. ++ */ ++ assert(*pnum && QEMU_IS_ALIGNED(*pnum, align) && ++ align > offset - aligned_offset); ++ if (ret & BDRV_BLOCK_RECURSE) { ++ assert(ret & BDRV_BLOCK_DATA); ++ assert(ret & BDRV_BLOCK_OFFSET_VALID); ++ assert(!(ret & BDRV_BLOCK_ZERO)); ++ } ++ ++ *pnum -= offset - aligned_offset; ++ if (*pnum > bytes) { ++ *pnum = bytes; ++ } ++ if (ret & BDRV_BLOCK_OFFSET_VALID) { ++ local_map += offset - aligned_offset; ++ } ++ ++ if (ret & BDRV_BLOCK_RAW) { ++ assert(ret & BDRV_BLOCK_OFFSET_VALID && local_file); ++ ret = bdrv_co_do_block_status(local_file, want_zero, local_map, ++ *pnum, pnum, &local_map, &local_file); ++ goto out; ++ } ++ ++ if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) { ++ ret |= BDRV_BLOCK_ALLOCATED; ++ } else if (bs->drv->supports_backing) { ++ BlockDriverState *cow_bs = bdrv_cow_bs(bs); ++ ++ if (!cow_bs) { ++ ret |= BDRV_BLOCK_ZERO; ++ } else if (want_zero) { ++ int64_t size2 = bdrv_co_getlength(cow_bs); ++ ++ if (size2 >= 0 && offset >= size2) { ++ ret |= BDRV_BLOCK_ZERO; ++ } ++ } ++ } ++ ++ if (want_zero && ret & BDRV_BLOCK_RECURSE && ++ local_file && local_file != bs && ++ (ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) && ++ (ret & BDRV_BLOCK_OFFSET_VALID)) { ++ int64_t file_pnum; ++ int ret2; ++ ++ ret2 = bdrv_co_do_block_status(local_file, want_zero, local_map, ++ *pnum, &file_pnum, NULL, NULL); ++ if (ret2 >= 0) { ++ /* Ignore errors. This is just providing extra information, it ++ * is useful but not necessary. ++ */ ++ if (ret2 & BDRV_BLOCK_EOF && ++ (!file_pnum || ret2 & BDRV_BLOCK_ZERO)) { ++ /* ++ * It is valid for the format block driver to read ++ * beyond the end of the underlying file's current ++ * size; such areas read as zero. ++ */ ++ ret |= BDRV_BLOCK_ZERO; ++ } else { ++ /* Limit request to the range reported by the protocol driver */ ++ *pnum = file_pnum; ++ ret |= (ret2 & BDRV_BLOCK_ZERO); ++ } ++ } ++ ++ /* ++ * Now that the recursive search was done, clear the flag. Otherwise, ++ * with more complicated block graphs like snapshot-access -> ++ * copy-before-write -> qcow2, where the return value will be propagated ++ * further up to a parent bdrv_co_do_block_status() call, both the ++ * BDRV_BLOCK_RECURSE and BDRV_BLOCK_ZERO flags would be set, which is ++ * not allowed. ++ */ ++ ret &= ~BDRV_BLOCK_RECURSE; ++ } ++ ++out: ++ bdrv_dec_in_flight(bs); ++ if (ret >= 0 && offset + *pnum == total_size) { ++ ret |= BDRV_BLOCK_EOF; ++ } ++early_out: ++ if (file) { ++ *file = local_file; ++ } ++ if (map) { ++ *map = local_map; ++ } ++ return ret; ++} ++ ++int coroutine_fn ++bdrv_co_common_block_status_above(BlockDriverState *bs, ++ BlockDriverState *base, ++ bool include_base, ++ bool want_zero, ++ int64_t offset, ++ int64_t bytes, ++ int64_t *pnum, ++ int64_t *map, ++ BlockDriverState **file, ++ int *depth) ++{ ++ int ret; ++ BlockDriverState *p; ++ int64_t eof = 0; ++ int dummy; ++ IO_CODE(); ++ ++ assert(!include_base || base); /* Can't include NULL base */ ++ assert_bdrv_graph_readable(); ++ ++ if (!depth) { ++ depth = &dummy; ++ } ++ *depth = 0; ++ ++ if (!include_base && bs == base) { ++ *pnum = bytes; ++ return 0; ++ } ++ ++ ret = bdrv_co_do_block_status(bs, want_zero, offset, bytes, pnum, ++ map, file); ++ ++*depth; ++ if (ret < 0 || *pnum == 0 || ret & BDRV_BLOCK_ALLOCATED || bs == base) { ++ return ret; ++ } ++ ++ if (ret & BDRV_BLOCK_EOF) { ++ eof = offset + *pnum; ++ } ++ ++ assert(*pnum <= bytes); ++ bytes = *pnum; ++ ++ for (p = bdrv_filter_or_cow_bs(bs); include_base || p != base; ++ p = bdrv_filter_or_cow_bs(p)) ++ { ++ ret = bdrv_co_do_block_status(p, want_zero, offset, bytes, pnum, ++ map, file); ++ ++*depth; ++ if (ret < 0) { ++ return ret; ++ } ++ if (*pnum == 0) { ++ /* ++ * The top layer deferred to this layer, and because this layer is ++ * short, any zeroes that we synthesize beyond EOF behave as if they ++ * were allocated at this layer. ++ * ++ * We don't include BDRV_BLOCK_EOF into ret, as upper layer may be ++ * larger. We'll add BDRV_BLOCK_EOF if needed at function end, see ++ * below. ++ */ ++ assert(ret & BDRV_BLOCK_EOF); ++ *pnum = bytes; ++ if (file) { ++ *file = p; ++ } ++ ret = BDRV_BLOCK_ZERO | BDRV_BLOCK_ALLOCATED; ++ break; ++ } ++ if (ret & BDRV_BLOCK_ALLOCATED) { ++ /* ++ * We've found the node and the status, we must break. ++ * ++ * Drop BDRV_BLOCK_EOF, as it's not for upper layer, which may be ++ * larger. We'll add BDRV_BLOCK_EOF if needed at function end, see ++ * below. ++ */ ++ ret &= ~BDRV_BLOCK_EOF; ++ break; ++ } ++ ++ if (p == base) { ++ assert(include_base); ++ break; ++ } ++ ++ /* ++ * OK, [offset, offset + *pnum) region is unallocated on this layer, ++ * let's continue the diving. ++ */ ++ assert(*pnum <= bytes); ++ bytes = *pnum; ++ } ++ ++ if (offset + *pnum == eof) { ++ ret |= BDRV_BLOCK_EOF; ++ } ++ ++ return ret; ++} ++ ++int coroutine_fn bdrv_co_block_status_above(BlockDriverState *bs, ++ BlockDriverState *base, ++ int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, ++ BlockDriverState **file) ++{ ++ IO_CODE(); ++ return bdrv_co_common_block_status_above(bs, base, false, true, offset, ++ bytes, pnum, map, file, NULL); ++} ++ ++int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, int64_t *pnum, ++ int64_t *map, BlockDriverState **file) ++{ ++ IO_CODE(); ++ return bdrv_co_block_status_above(bs, bdrv_filter_or_cow_bs(bs), ++ offset, bytes, pnum, map, file); ++} ++ ++/* ++ * Check @bs (and its backing chain) to see if the range defined ++ * by @offset and @bytes is known to read as zeroes. ++ * Return 1 if that is the case, 0 otherwise and -errno on error. ++ * This test is meant to be fast rather than accurate so returning 0 ++ * does not guarantee non-zero data. ++ */ ++int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, ++ int64_t bytes) ++{ ++ int ret; ++ int64_t pnum = bytes; ++ IO_CODE(); ++ ++ if (!bytes) { ++ return 1; ++ } ++ ++ ret = bdrv_co_common_block_status_above(bs, NULL, false, false, offset, ++ bytes, &pnum, NULL, NULL, NULL); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return (pnum == bytes) && (ret & BDRV_BLOCK_ZERO); ++} ++ ++int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, int64_t *pnum) ++{ ++ int ret; ++ int64_t dummy; ++ IO_CODE(); ++ ++ ret = bdrv_co_common_block_status_above(bs, bs, true, false, offset, ++ bytes, pnum ? pnum : &dummy, NULL, ++ NULL, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ return !!(ret & BDRV_BLOCK_ALLOCATED); ++} ++ ++/* ++ * Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP] ++ * ++ * Return a positive depth if (a prefix of) the given range is allocated ++ * in any image between BASE and TOP (BASE is only included if include_base ++ * is set). Depth 1 is TOP, 2 is the first backing layer, and so forth. ++ * BASE can be NULL to check if the given offset is allocated in any ++ * image of the chain. Return 0 otherwise, or negative errno on ++ * failure. ++ * ++ * 'pnum' is set to the number of bytes (including and immediately ++ * following the specified offset) that are known to be in the same ++ * allocated/unallocated state. Note that a subsequent call starting ++ * at 'offset + *pnum' may return the same allocation status (in other ++ * words, the result is not necessarily the maximum possible range); ++ * but 'pnum' will only be 0 when end of file is reached. ++ */ ++int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *bs, ++ BlockDriverState *base, ++ bool include_base, int64_t offset, ++ int64_t bytes, int64_t *pnum) ++{ ++ int depth; ++ int ret; ++ IO_CODE(); ++ ++ ret = bdrv_co_common_block_status_above(bs, base, include_base, false, ++ offset, bytes, pnum, NULL, NULL, ++ &depth); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (ret & BDRV_BLOCK_ALLOCATED) { ++ return depth; ++ } ++ return 0; ++} ++ ++int coroutine_fn ++bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *child_bs = bdrv_primary_bs(bs); ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ++ if (drv->bdrv_co_load_vmstate) { ++ ret = drv->bdrv_co_load_vmstate(bs, qiov, pos); ++ } else if (child_bs) { ++ ret = bdrv_co_readv_vmstate(child_bs, qiov, pos); ++ } else { ++ ret = -ENOTSUP; ++ } ++ ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++int coroutine_fn ++bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *child_bs = bdrv_primary_bs(bs); ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ++ if (drv->bdrv_co_save_vmstate) { ++ ret = drv->bdrv_co_save_vmstate(bs, qiov, pos); ++ } else if (child_bs) { ++ ret = bdrv_co_writev_vmstate(child_bs, qiov, pos); ++ } else { ++ ret = -ENOTSUP; ++ } ++ ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, ++ int64_t pos, int size) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size); ++ int ret = bdrv_writev_vmstate(bs, &qiov, pos); ++ IO_CODE(); ++ ++ return ret < 0 ? ret : size; ++} ++ ++int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, ++ int64_t pos, int size) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size); ++ int ret = bdrv_readv_vmstate(bs, &qiov, pos); ++ IO_CODE(); ++ ++ return ret < 0 ? ret : size; ++} ++ ++/**************************************************************/ ++/* async I/Os */ ++ ++/** ++ * Synchronously cancels an acb. Must be called with the BQL held and the acb ++ * must be processed with the BQL held too (IOThreads are not allowed). ++ * ++ * Use bdrv_aio_cancel_async() instead when possible. ++ */ ++void bdrv_aio_cancel(BlockAIOCB *acb) ++{ ++ GLOBAL_STATE_CODE(); ++ qemu_aio_ref(acb); ++ bdrv_aio_cancel_async(acb); ++ AIO_WAIT_WHILE_UNLOCKED(NULL, acb->refcnt > 1); ++ qemu_aio_unref(acb); ++} ++ ++/* Async version of aio cancel. The caller is not blocked if the acb implements ++ * cancel_async, otherwise we do nothing and let the request normally complete. ++ * In either case the completion callback must be called. */ ++void bdrv_aio_cancel_async(BlockAIOCB *acb) ++{ ++ IO_CODE(); ++ if (acb->aiocb_info->cancel_async) { ++ acb->aiocb_info->cancel_async(acb); ++ } ++} ++ ++/**************************************************************/ ++/* Coroutine block device emulation */ ++ ++int coroutine_fn bdrv_co_flush(BlockDriverState *bs) ++{ ++ BdrvChild *primary_child = bdrv_primary_child(bs); ++ BdrvChild *child; ++ int current_gen; ++ int ret = 0; ++ IO_CODE(); ++ ++ assert_bdrv_graph_readable(); ++ bdrv_inc_in_flight(bs); ++ ++ if (!bdrv_co_is_inserted(bs) || bdrv_is_read_only(bs) || ++ bdrv_is_sg(bs)) { ++ goto early_exit; ++ } ++ ++ qemu_mutex_lock(&bs->reqs_lock); ++ current_gen = qatomic_read(&bs->write_gen); ++ ++ /* Wait until any previous flushes are completed */ ++ while (bs->active_flush_req) { ++ qemu_co_queue_wait(&bs->flush_queue, &bs->reqs_lock); ++ } ++ ++ /* Flushes reach this point in nondecreasing current_gen order. */ ++ bs->active_flush_req = true; ++ qemu_mutex_unlock(&bs->reqs_lock); ++ ++ /* Write back all layers by calling one driver function */ ++ if (bs->drv->bdrv_co_flush) { ++ ret = bs->drv->bdrv_co_flush(bs); ++ goto out; ++ } ++ ++ /* Write back cached data to the OS even with cache=unsafe */ ++ BLKDBG_CO_EVENT(primary_child, BLKDBG_FLUSH_TO_OS); ++ if (bs->drv->bdrv_co_flush_to_os) { ++ ret = bs->drv->bdrv_co_flush_to_os(bs); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ ++ /* But don't actually force it to the disk with cache=unsafe */ ++ if (bs->open_flags & BDRV_O_NO_FLUSH) { ++ goto flush_children; ++ } ++ ++ /* Check if we really need to flush anything */ ++ if (bs->flushed_gen == current_gen) { ++ goto flush_children; ++ } ++ ++ BLKDBG_CO_EVENT(primary_child, BLKDBG_FLUSH_TO_DISK); ++ if (!bs->drv) { ++ /* bs->drv->bdrv_co_flush() might have ejected the BDS ++ * (even in case of apparent success) */ ++ ret = -ENOMEDIUM; ++ goto out; ++ } ++ if (bs->drv->bdrv_co_flush_to_disk) { ++ ret = bs->drv->bdrv_co_flush_to_disk(bs); ++ } else if (bs->drv->bdrv_aio_flush) { ++ BlockAIOCB *acb; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ ++ acb = bs->drv->bdrv_aio_flush(bs, bdrv_co_io_em_complete, &co); ++ if (acb == NULL) { ++ ret = -EIO; ++ } else { ++ qemu_coroutine_yield(); ++ ret = co.ret; ++ } ++ } else { ++ /* ++ * Some block drivers always operate in either writethrough or unsafe ++ * mode and don't support bdrv_flush therefore. Usually qemu doesn't ++ * know how the server works (because the behaviour is hardcoded or ++ * depends on server-side configuration), so we can't ensure that ++ * everything is safe on disk. Returning an error doesn't work because ++ * that would break guests even if the server operates in writethrough ++ * mode. ++ * ++ * Let's hope the user knows what he's doing. ++ */ ++ ret = 0; ++ } ++ ++ if (ret < 0) { ++ goto out; ++ } ++ ++ /* Now flush the underlying protocol. It will also have BDRV_O_NO_FLUSH ++ * in the case of cache=unsafe, so there are no useless flushes. ++ */ ++flush_children: ++ ret = 0; ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (child->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) { ++ int this_child_ret = bdrv_co_flush(child->bs); ++ if (!ret) { ++ ret = this_child_ret; ++ } ++ } ++ } ++ ++out: ++ /* Notify any pending flushes that we have completed */ ++ if (ret == 0) { ++ bs->flushed_gen = current_gen; ++ } ++ ++ qemu_mutex_lock(&bs->reqs_lock); ++ bs->active_flush_req = false; ++ /* Return value is ignored - it's ok if wait queue is empty */ ++ qemu_co_queue_next(&bs->flush_queue); ++ qemu_mutex_unlock(&bs->reqs_lock); ++ ++early_exit: ++ bdrv_dec_in_flight(bs); ++ return ret; ++} ++ ++int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, ++ int64_t bytes) ++{ ++ BdrvTrackedRequest req; ++ int ret; ++ int64_t max_pdiscard; ++ int head, tail, align; ++ BlockDriverState *bs = child->bs; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!bs || !bs->drv || !bdrv_co_is_inserted(bs)) { ++ return -ENOMEDIUM; ++ } ++ ++ if (bdrv_has_readonly_bitmaps(bs)) { ++ return -EPERM; ++ } ++ ++ ret = bdrv_check_request(offset, bytes, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Do nothing if disabled. */ ++ if (!(bs->open_flags & BDRV_O_UNMAP)) { ++ return 0; ++ } ++ ++ if (!bs->drv->bdrv_co_pdiscard && !bs->drv->bdrv_aio_pdiscard) { ++ return 0; ++ } ++ ++ /* Invalidate the cached block-status data range if this discard overlaps */ ++ bdrv_bsc_invalidate_range(bs, offset, bytes); ++ ++ /* Discard is advisory, but some devices track and coalesce ++ * unaligned requests, so we must pass everything down rather than ++ * round here. Still, most devices will just silently ignore ++ * unaligned requests (by returning -ENOTSUP), so we must fragment ++ * the request accordingly. */ ++ align = MAX(bs->bl.pdiscard_alignment, bs->bl.request_alignment); ++ assert(align % bs->bl.request_alignment == 0); ++ head = offset % align; ++ tail = (offset + bytes) % align; ++ ++ bdrv_inc_in_flight(bs); ++ tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_DISCARD); ++ ++ ret = bdrv_co_write_req_prepare(child, offset, bytes, &req, 0); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ max_pdiscard = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_pdiscard, INT64_MAX), ++ align); ++ assert(max_pdiscard >= bs->bl.request_alignment); ++ ++ while (bytes > 0) { ++ int64_t num = bytes; ++ ++ if (head) { ++ /* Make small requests to get to alignment boundaries. */ ++ num = MIN(bytes, align - head); ++ if (!QEMU_IS_ALIGNED(num, bs->bl.request_alignment)) { ++ num %= bs->bl.request_alignment; ++ } ++ head = (head + num) % align; ++ assert(num < max_pdiscard); ++ } else if (tail) { ++ if (num > align) { ++ /* Shorten the request to the last aligned cluster. */ ++ num -= tail; ++ } else if (!QEMU_IS_ALIGNED(tail, bs->bl.request_alignment) && ++ tail > bs->bl.request_alignment) { ++ tail %= bs->bl.request_alignment; ++ num -= tail; ++ } ++ } ++ /* limit request size */ ++ if (num > max_pdiscard) { ++ num = max_pdiscard; ++ } ++ ++ if (!bs->drv) { ++ ret = -ENOMEDIUM; ++ goto out; ++ } ++ if (bs->drv->bdrv_co_pdiscard) { ++ ret = bs->drv->bdrv_co_pdiscard(bs, offset, num); ++ } else { ++ BlockAIOCB *acb; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ ++ acb = bs->drv->bdrv_aio_pdiscard(bs, offset, num, ++ bdrv_co_io_em_complete, &co); ++ if (acb == NULL) { ++ ret = -EIO; ++ goto out; ++ } else { ++ qemu_coroutine_yield(); ++ ret = co.ret; ++ } ++ } ++ if (ret && ret != -ENOTSUP) { ++ goto out; ++ } ++ ++ offset += num; ++ bytes -= num; ++ } ++ ret = 0; ++out: ++ bdrv_co_write_req_finish(child, req.offset, req.bytes, &req, ret); ++ tracked_request_end(&req); ++ bdrv_dec_in_flight(bs); ++ return ret; ++} ++ ++int coroutine_fn bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf) ++{ ++ BlockDriver *drv = bs->drv; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ BlockAIOCB *acb; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ bdrv_inc_in_flight(bs); ++ if (!drv || (!drv->bdrv_aio_ioctl && !drv->bdrv_co_ioctl)) { ++ co.ret = -ENOTSUP; ++ goto out; ++ } ++ ++ if (drv->bdrv_co_ioctl) { ++ co.ret = drv->bdrv_co_ioctl(bs, req, buf); ++ } else { ++ acb = drv->bdrv_aio_ioctl(bs, req, buf, bdrv_co_io_em_complete, &co); ++ if (!acb) { ++ co.ret = -ENOTSUP; ++ goto out; ++ } ++ qemu_coroutine_yield(); ++ } ++out: ++ bdrv_dec_in_flight(bs); ++ return co.ret; ++} ++ ++int coroutine_fn bdrv_co_zone_report(BlockDriverState *bs, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones) ++{ ++ BlockDriver *drv = bs->drv; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ IO_CODE(); ++ ++ bdrv_inc_in_flight(bs); ++ if (!drv || !drv->bdrv_co_zone_report || bs->bl.zoned == BLK_Z_NONE) { ++ co.ret = -ENOTSUP; ++ goto out; ++ } ++ co.ret = drv->bdrv_co_zone_report(bs, offset, nr_zones, zones); ++out: ++ bdrv_dec_in_flight(bs); ++ return co.ret; ++} ++ ++int coroutine_fn bdrv_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, ++ int64_t offset, int64_t len) ++{ ++ BlockDriver *drv = bs->drv; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ IO_CODE(); ++ ++ bdrv_inc_in_flight(bs); ++ if (!drv || !drv->bdrv_co_zone_mgmt || bs->bl.zoned == BLK_Z_NONE) { ++ co.ret = -ENOTSUP; ++ goto out; ++ } ++ co.ret = drv->bdrv_co_zone_mgmt(bs, op, offset, len); ++out: ++ bdrv_dec_in_flight(bs); ++ return co.ret; ++} ++ ++int coroutine_fn bdrv_co_zone_append(BlockDriverState *bs, int64_t *offset, ++ QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ BlockDriver *drv = bs->drv; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ IO_CODE(); ++ ++ ret = bdrv_check_qiov_request(*offset, qiov->size, qiov, 0, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ if (!drv || !drv->bdrv_co_zone_append || bs->bl.zoned == BLK_Z_NONE) { ++ co.ret = -ENOTSUP; ++ goto out; ++ } ++ co.ret = drv->bdrv_co_zone_append(bs, offset, qiov, flags); ++out: ++ bdrv_dec_in_flight(bs); ++ return co.ret; ++} ++ ++void *qemu_blockalign(BlockDriverState *bs, size_t size) ++{ ++ IO_CODE(); ++ return qemu_memalign(bdrv_opt_mem_align(bs), size); ++} ++ ++void *qemu_blockalign0(BlockDriverState *bs, size_t size) ++{ ++ IO_CODE(); ++ return memset(qemu_blockalign(bs, size), 0, size); ++} ++ ++void *qemu_try_blockalign(BlockDriverState *bs, size_t size) ++{ ++ size_t align = bdrv_opt_mem_align(bs); ++ IO_CODE(); ++ ++ /* Ensure that NULL is never returned on success */ ++ assert(align > 0); ++ if (size == 0) { ++ size = align; ++ } ++ ++ return qemu_try_memalign(align, size); ++} ++ ++void *qemu_try_blockalign0(BlockDriverState *bs, size_t size) ++{ ++ void *mem = qemu_try_blockalign(bs, size); ++ IO_CODE(); ++ ++ if (mem) { ++ memset(mem, 0, size); ++ } ++ ++ return mem; ++} ++ ++/* Helper that undoes bdrv_register_buf() when it fails partway through */ ++static void GRAPH_RDLOCK ++bdrv_register_buf_rollback(BlockDriverState *bs, void *host, size_t size, ++ BdrvChild *final_child) ++{ ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (child == final_child) { ++ break; ++ } ++ ++ bdrv_unregister_buf(child->bs, host, size); ++ } ++ ++ if (bs->drv && bs->drv->bdrv_unregister_buf) { ++ bs->drv->bdrv_unregister_buf(bs, host, size); ++ } ++} ++ ++bool bdrv_register_buf(BlockDriverState *bs, void *host, size_t size, ++ Error **errp) ++{ ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bs->drv && bs->drv->bdrv_register_buf) { ++ if (!bs->drv->bdrv_register_buf(bs, host, size, errp)) { ++ return false; ++ } ++ } ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (!bdrv_register_buf(child->bs, host, size, errp)) { ++ bdrv_register_buf_rollback(bs, host, size, child); ++ return false; ++ } ++ } ++ return true; ++} ++ ++void bdrv_unregister_buf(BlockDriverState *bs, void *host, size_t size) ++{ ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bs->drv && bs->drv->bdrv_unregister_buf) { ++ bs->drv->bdrv_unregister_buf(bs, host, size); ++ } ++ QLIST_FOREACH(child, &bs->children, next) { ++ bdrv_unregister_buf(child->bs, host, size); ++ } ++} ++ ++static int coroutine_fn GRAPH_RDLOCK bdrv_co_copy_range_internal( ++ BdrvChild *src, int64_t src_offset, BdrvChild *dst, ++ int64_t dst_offset, int64_t bytes, ++ BdrvRequestFlags read_flags, BdrvRequestFlags write_flags, ++ bool recurse_src) ++{ ++ BdrvTrackedRequest req; ++ int ret; ++ assert_bdrv_graph_readable(); ++ ++ /* TODO We can support BDRV_REQ_NO_FALLBACK here */ ++ assert(!(read_flags & BDRV_REQ_NO_FALLBACK)); ++ assert(!(write_flags & BDRV_REQ_NO_FALLBACK)); ++ assert(!(read_flags & BDRV_REQ_NO_WAIT)); ++ assert(!(write_flags & BDRV_REQ_NO_WAIT)); ++ ++ if (!dst || !dst->bs || !bdrv_co_is_inserted(dst->bs)) { ++ return -ENOMEDIUM; ++ } ++ ret = bdrv_check_request32(dst_offset, bytes, NULL, 0); ++ if (ret) { ++ return ret; ++ } ++ if (write_flags & BDRV_REQ_ZERO_WRITE) { ++ return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, write_flags); ++ } ++ ++ if (!src || !src->bs || !bdrv_co_is_inserted(src->bs)) { ++ return -ENOMEDIUM; ++ } ++ ret = bdrv_check_request32(src_offset, bytes, NULL, 0); ++ if (ret) { ++ return ret; ++ } ++ ++ if (!src->bs->drv->bdrv_co_copy_range_from ++ || !dst->bs->drv->bdrv_co_copy_range_to ++ || src->bs->encrypted || dst->bs->encrypted) { ++ return -ENOTSUP; ++ } ++ ++ if (recurse_src) { ++ bdrv_inc_in_flight(src->bs); ++ tracked_request_begin(&req, src->bs, src_offset, bytes, ++ BDRV_TRACKED_READ); ++ ++ /* BDRV_REQ_SERIALISING is only for write operation */ ++ assert(!(read_flags & BDRV_REQ_SERIALISING)); ++ bdrv_wait_serialising_requests(&req); ++ ++ ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, ++ src, src_offset, ++ dst, dst_offset, ++ bytes, ++ read_flags, write_flags); ++ ++ tracked_request_end(&req); ++ bdrv_dec_in_flight(src->bs); ++ } else { ++ bdrv_inc_in_flight(dst->bs); ++ tracked_request_begin(&req, dst->bs, dst_offset, bytes, ++ BDRV_TRACKED_WRITE); ++ ret = bdrv_co_write_req_prepare(dst, dst_offset, bytes, &req, ++ write_flags); ++ if (!ret) { ++ ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, ++ src, src_offset, ++ dst, dst_offset, ++ bytes, ++ read_flags, write_flags); ++ } ++ bdrv_co_write_req_finish(dst, dst_offset, bytes, &req, ret); ++ tracked_request_end(&req); ++ bdrv_dec_in_flight(dst->bs); ++ } ++ ++ return ret; ++} ++ ++/* Copy range from @src to @dst. ++ * ++ * See the comment of bdrv_co_copy_range for the parameter and return value ++ * semantics. */ ++int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ trace_bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, bytes, ++ read_flags, write_flags); ++ return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, ++ bytes, read_flags, write_flags, true); ++} ++ ++/* Copy range from @src to @dst. ++ * ++ * See the comment of bdrv_co_copy_range for the parameter and return value ++ * semantics. */ ++int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ trace_bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, ++ read_flags, write_flags); ++ return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, ++ bytes, read_flags, write_flags, false); ++} ++ ++int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ return bdrv_co_copy_range_from(src, src_offset, ++ dst, dst_offset, ++ bytes, read_flags, write_flags); ++} ++ ++static void coroutine_fn GRAPH_RDLOCK ++bdrv_parent_cb_resize(BlockDriverState *bs) ++{ ++ BdrvChild *c; ++ ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ if (c->klass->resize) { ++ c->klass->resize(c); ++ } ++ } ++} ++ ++/** ++ * Truncate file to 'offset' bytes (needed only for file protocols) ++ * ++ * If 'exact' is true, the file must be resized to exactly the given ++ * 'offset'. Otherwise, it is sufficient for the node to be at least ++ * 'offset' bytes in length. ++ */ ++int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, ++ Error **errp) ++{ ++ BlockDriverState *bs = child->bs; ++ BdrvChild *filtered, *backing; ++ BlockDriver *drv = bs->drv; ++ BdrvTrackedRequest req; ++ int64_t old_size, new_bytes; ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ ++ if (!drv) { ++ error_setg(errp, "No medium inserted"); ++ return -ENOMEDIUM; ++ } ++ if (offset < 0) { ++ error_setg(errp, "Image size cannot be negative"); ++ return -EINVAL; ++ } ++ ++ ret = bdrv_check_request(offset, 0, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ old_size = bdrv_co_getlength(bs); ++ if (old_size < 0) { ++ error_setg_errno(errp, -old_size, "Failed to get old image size"); ++ return old_size; ++ } ++ ++ if (bdrv_is_read_only(bs)) { ++ error_setg(errp, "Image is read-only"); ++ return -EACCES; ++ } ++ ++ if (offset > old_size) { ++ new_bytes = offset - old_size; ++ } else { ++ new_bytes = 0; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ tracked_request_begin(&req, bs, offset - new_bytes, new_bytes, ++ BDRV_TRACKED_TRUNCATE); ++ ++ /* If we are growing the image and potentially using preallocation for the ++ * new area, we need to make sure that no write requests are made to it ++ * concurrently or they might be overwritten by preallocation. */ ++ if (new_bytes) { ++ bdrv_make_request_serialising(&req, 1); ++ } ++ ret = bdrv_co_write_req_prepare(child, offset - new_bytes, new_bytes, &req, ++ 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to prepare request for truncation"); ++ goto out; ++ } ++ ++ filtered = bdrv_filter_child(bs); ++ backing = bdrv_cow_child(bs); ++ ++ /* ++ * If the image has a backing file that is large enough that it would ++ * provide data for the new area, we cannot leave it unallocated because ++ * then the backing file content would become visible. Instead, zero-fill ++ * the new area. ++ * ++ * Note that if the image has a backing file, but was opened without the ++ * backing file, taking care of keeping things consistent with that backing ++ * file is the user's responsibility. ++ */ ++ if (new_bytes && backing) { ++ int64_t backing_len; ++ ++ backing_len = bdrv_co_getlength(backing->bs); ++ if (backing_len < 0) { ++ ret = backing_len; ++ error_setg_errno(errp, -ret, "Could not get backing file size"); ++ goto out; ++ } ++ ++ if (backing_len > old_size) { ++ flags |= BDRV_REQ_ZERO_WRITE; ++ } ++ } ++ ++ if (drv->bdrv_co_truncate) { ++ if (flags & ~bs->supported_truncate_flags) { ++ error_setg(errp, "Block driver does not support requested flags"); ++ ret = -ENOTSUP; ++ goto out; ++ } ++ ret = drv->bdrv_co_truncate(bs, offset, exact, prealloc, flags, errp); ++ } else if (filtered) { ++ ret = bdrv_co_truncate(filtered, offset, exact, prealloc, flags, errp); ++ } else { ++ error_setg(errp, "Image format driver does not support resize"); ++ ret = -ENOTSUP; ++ goto out; ++ } ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = bdrv_co_refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not refresh total sector count"); ++ } else { ++ offset = bs->total_sectors * BDRV_SECTOR_SIZE; ++ } ++ /* ++ * It's possible that truncation succeeded but bdrv_refresh_total_sectors ++ * failed, but the latter doesn't affect how we should finish the request. ++ * Pass 0 as the last parameter so that dirty bitmaps etc. are handled. ++ */ ++ bdrv_co_write_req_finish(child, offset - new_bytes, new_bytes, &req, 0); ++ ++out: ++ tracked_request_end(&req); ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++void bdrv_cancel_in_flight(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!bs || !bs->drv) { ++ return; ++ } ++ ++ if (bs->drv->bdrv_cancel_in_flight) { ++ bs->drv->bdrv_cancel_in_flight(bs); ++ } ++} ++ ++int coroutine_fn ++bdrv_co_preadv_snapshot(BdrvChild *child, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset) ++{ ++ BlockDriverState *bs = child->bs; ++ BlockDriver *drv = bs->drv; ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if (!drv->bdrv_co_preadv_snapshot) { ++ return -ENOTSUP; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ret = drv->bdrv_co_preadv_snapshot(bs, offset, bytes, qiov, qiov_offset); ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++int coroutine_fn ++bdrv_co_snapshot_block_status(BlockDriverState *bs, ++ bool want_zero, int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, ++ BlockDriverState **file) ++{ ++ BlockDriver *drv = bs->drv; ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if (!drv->bdrv_co_snapshot_block_status) { ++ return -ENOTSUP; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ret = drv->bdrv_co_snapshot_block_status(bs, want_zero, offset, bytes, ++ pnum, map, file); ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++int coroutine_fn ++bdrv_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ BlockDriver *drv = bs->drv; ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if (!drv->bdrv_co_pdiscard_snapshot) { ++ return -ENOTSUP; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ret = drv->bdrv_co_pdiscard_snapshot(bs, offset, bytes); ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} +diff --git a/qcow2/lib/block/linux-aio.c b/qcow2/lib/block/linux-aio.c +new file mode 100644 +index 00000000..e3b5ec9a +--- /dev/null ++++ b/qcow2/lib/block/linux-aio.c +@@ -0,0 +1,507 @@ ++/* ++ * Linux native AIO support. ++ * ++ * Copyright (C) 2009 IBM, Corp. ++ * Copyright (C) 2009 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++#include "qemu/osdep.h" ++#include "block/aio.h" ++#include "qemu/queue.h" ++#include "block/block.h" ++#include "block/raw-aio.h" ++#include "qemu/event_notifier.h" ++#include "qemu/coroutine.h" ++#include "qemu/defer-call.h" ++#include "qapi/error.h" ++#include "sysemu/block-backend.h" ++ ++/* Only used for assertions. */ ++#include "qemu/coroutine_int.h" ++ ++#include ++ ++/* ++ * Queue size (per-device). ++ * ++ * XXX: eventually we need to communicate this to the guest and/or make it ++ * tunable by the guest. If we get more outstanding requests at a time ++ * than this we will get EAGAIN from io_submit which is communicated to ++ * the guest as an I/O error. ++ */ ++#define MAX_EVENTS 1024 ++ ++/* Maximum number of requests in a batch. (default value) */ ++#define DEFAULT_MAX_BATCH 32 ++ ++struct qemu_laiocb { ++ Coroutine *co; ++ LinuxAioState *ctx; ++ struct iocb iocb; ++ ssize_t ret; ++ size_t nbytes; ++ QEMUIOVector *qiov; ++ bool is_read; ++ QSIMPLEQ_ENTRY(qemu_laiocb) next; ++}; ++ ++typedef struct { ++ unsigned int in_queue; ++ unsigned int in_flight; ++ bool blocked; ++ QSIMPLEQ_HEAD(, qemu_laiocb) pending; ++} LaioQueue; ++ ++struct LinuxAioState { ++ AioContext *aio_context; ++ ++ io_context_t ctx; ++ EventNotifier e; ++ ++ /* No locking required, only accessed from AioContext home thread */ ++ LaioQueue io_q; ++ QEMUBH *completion_bh; ++ int event_idx; ++ int event_max; ++}; ++ ++static void ioq_submit(LinuxAioState *s); ++ ++static inline ssize_t io_event_ret(struct io_event *ev) ++{ ++ return (ssize_t)(((uint64_t)ev->res2 << 32) | ev->res); ++} ++ ++/* ++ * Completes an AIO request. ++ */ ++static void qemu_laio_process_completion(struct qemu_laiocb *laiocb) ++{ ++ int ret; ++ ++ ret = laiocb->ret; ++ if (ret != -ECANCELED) { ++ if (ret == laiocb->nbytes) { ++ ret = 0; ++ } else if (ret >= 0) { ++ /* Short reads mean EOF, pad with zeros. */ ++ if (laiocb->is_read) { ++ qemu_iovec_memset(laiocb->qiov, ret, 0, ++ laiocb->qiov->size - ret); ++ } else { ++ ret = -ENOSPC; ++ } ++ } ++ } ++ ++ laiocb->ret = ret; ++ ++ /* ++ * If the coroutine is already entered it must be in ioq_submit() and ++ * will notice laio->ret has been filled in when it eventually runs ++ * later. Coroutines cannot be entered recursively so avoid doing ++ * that! ++ */ ++ assert(laiocb->co->ctx == laiocb->ctx->aio_context); ++ if (!qemu_coroutine_entered(laiocb->co)) { ++ aio_co_wake(laiocb->co); ++ } ++} ++ ++/** ++ * aio_ring buffer which is shared between userspace and kernel. ++ * ++ * This copied from linux/fs/aio.c, common header does not exist ++ * but AIO exists for ages so we assume ABI is stable. ++ */ ++struct aio_ring { ++ unsigned id; /* kernel internal index number */ ++ unsigned nr; /* number of io_events */ ++ unsigned head; /* Written to by userland or by kernel. */ ++ unsigned tail; ++ ++ unsigned magic; ++ unsigned compat_features; ++ unsigned incompat_features; ++ unsigned header_length; /* size of aio_ring */ ++ ++ struct io_event io_events[]; ++}; ++ ++/** ++ * io_getevents_peek: ++ * @ctx: AIO context ++ * @events: pointer on events array, output value ++ ++ * Returns the number of completed events and sets a pointer ++ * on events array. This function does not update the internal ++ * ring buffer, only reads head and tail. When @events has been ++ * processed io_getevents_commit() must be called. ++ */ ++static inline unsigned int io_getevents_peek(io_context_t ctx, ++ struct io_event **events) ++{ ++ struct aio_ring *ring = (struct aio_ring *)ctx; ++ unsigned int head = ring->head, tail = ring->tail; ++ unsigned int nr; ++ ++ nr = tail >= head ? tail - head : ring->nr - head; ++ *events = ring->io_events + head; ++ /* To avoid speculative loads of s->events[i] before observing tail. ++ Paired with smp_wmb() inside linux/fs/aio.c: aio_complete(). */ ++ smp_rmb(); ++ ++ return nr; ++} ++ ++/** ++ * io_getevents_commit: ++ * @ctx: AIO context ++ * @nr: the number of events on which head should be advanced ++ * ++ * Advances head of a ring buffer. ++ */ ++static inline void io_getevents_commit(io_context_t ctx, unsigned int nr) ++{ ++ struct aio_ring *ring = (struct aio_ring *)ctx; ++ ++ if (nr) { ++ ring->head = (ring->head + nr) % ring->nr; ++ } ++} ++ ++/** ++ * io_getevents_advance_and_peek: ++ * @ctx: AIO context ++ * @events: pointer on events array, output value ++ * @nr: the number of events on which head should be advanced ++ * ++ * Advances head of a ring buffer and returns number of elements left. ++ */ ++static inline unsigned int ++io_getevents_advance_and_peek(io_context_t ctx, ++ struct io_event **events, ++ unsigned int nr) ++{ ++ io_getevents_commit(ctx, nr); ++ return io_getevents_peek(ctx, events); ++} ++ ++/** ++ * qemu_laio_process_completions: ++ * @s: AIO state ++ * ++ * Fetches completed I/O requests and invokes their callbacks. ++ * ++ * The function is somewhat tricky because it supports nested event loops, for ++ * example when a request callback invokes aio_poll(). In order to do this, ++ * indices are kept in LinuxAioState. Function schedules BH completion so it ++ * can be called again in a nested event loop. When there are no events left ++ * to complete the BH is being canceled. ++ */ ++static void qemu_laio_process_completions(LinuxAioState *s) ++{ ++ struct io_event *events; ++ ++ defer_call_begin(); ++ ++ /* Reschedule so nested event loops see currently pending completions */ ++ qemu_bh_schedule(s->completion_bh); ++ ++ while ((s->event_max = io_getevents_advance_and_peek(s->ctx, &events, ++ s->event_idx))) { ++ for (s->event_idx = 0; s->event_idx < s->event_max; ) { ++ struct iocb *iocb = events[s->event_idx].obj; ++ struct qemu_laiocb *laiocb = ++ container_of(iocb, struct qemu_laiocb, iocb); ++ ++ laiocb->ret = io_event_ret(&events[s->event_idx]); ++ ++ /* Change counters one-by-one because we can be nested. */ ++ s->io_q.in_flight--; ++ s->event_idx++; ++ qemu_laio_process_completion(laiocb); ++ } ++ } ++ ++ qemu_bh_cancel(s->completion_bh); ++ ++ /* If we are nested we have to notify the level above that we are done ++ * by setting event_max to zero, upper level will then jump out of it's ++ * own `for` loop. If we are the last all counters dropped to zero. */ ++ s->event_max = 0; ++ s->event_idx = 0; ++ ++ defer_call_end(); ++} ++ ++static void qemu_laio_process_completions_and_submit(LinuxAioState *s) ++{ ++ qemu_laio_process_completions(s); ++ ++ if (!QSIMPLEQ_EMPTY(&s->io_q.pending)) { ++ ioq_submit(s); ++ } ++} ++ ++static void qemu_laio_completion_bh(void *opaque) ++{ ++ LinuxAioState *s = opaque; ++ ++ qemu_laio_process_completions_and_submit(s); ++} ++ ++static void qemu_laio_completion_cb(EventNotifier *e) ++{ ++ LinuxAioState *s = container_of(e, LinuxAioState, e); ++ ++ if (event_notifier_test_and_clear(&s->e)) { ++ qemu_laio_process_completions_and_submit(s); ++ } ++} ++ ++static bool qemu_laio_poll_cb(void *opaque) ++{ ++ EventNotifier *e = opaque; ++ LinuxAioState *s = container_of(e, LinuxAioState, e); ++ struct io_event *events; ++ ++ return io_getevents_peek(s->ctx, &events); ++} ++ ++static void qemu_laio_poll_ready(EventNotifier *opaque) ++{ ++ EventNotifier *e = opaque; ++ LinuxAioState *s = container_of(e, LinuxAioState, e); ++ ++ qemu_laio_process_completions_and_submit(s); ++} ++ ++static void ioq_init(LaioQueue *io_q) ++{ ++ QSIMPLEQ_INIT(&io_q->pending); ++ io_q->in_queue = 0; ++ io_q->in_flight = 0; ++ io_q->blocked = false; ++} ++ ++static void ioq_submit(LinuxAioState *s) ++{ ++ int ret, len; ++ struct qemu_laiocb *aiocb; ++ struct iocb *iocbs[MAX_EVENTS]; ++ QSIMPLEQ_HEAD(, qemu_laiocb) completed; ++ ++ do { ++ if (s->io_q.in_flight >= MAX_EVENTS) { ++ break; ++ } ++ len = 0; ++ QSIMPLEQ_FOREACH(aiocb, &s->io_q.pending, next) { ++ iocbs[len++] = &aiocb->iocb; ++ if (s->io_q.in_flight + len >= MAX_EVENTS) { ++ break; ++ } ++ } ++ ++ ret = io_submit(s->ctx, len, iocbs); ++ if (ret == -EAGAIN) { ++ break; ++ } ++ if (ret < 0) { ++ /* Fail the first request, retry the rest */ ++ aiocb = QSIMPLEQ_FIRST(&s->io_q.pending); ++ QSIMPLEQ_REMOVE_HEAD(&s->io_q.pending, next); ++ s->io_q.in_queue--; ++ aiocb->ret = ret; ++ qemu_laio_process_completion(aiocb); ++ continue; ++ } ++ ++ s->io_q.in_flight += ret; ++ s->io_q.in_queue -= ret; ++ aiocb = container_of(iocbs[ret - 1], struct qemu_laiocb, iocb); ++ QSIMPLEQ_SPLIT_AFTER(&s->io_q.pending, aiocb, next, &completed); ++ } while (ret == len && !QSIMPLEQ_EMPTY(&s->io_q.pending)); ++ s->io_q.blocked = (s->io_q.in_queue > 0); ++ ++ if (s->io_q.in_flight) { ++ /* We can try to complete something just right away if there are ++ * still requests in-flight. */ ++ qemu_laio_process_completions(s); ++ /* ++ * Even we have completed everything (in_flight == 0), the queue can ++ * have still pended requests (in_queue > 0). We do not attempt to ++ * repeat submission to avoid IO hang. The reason is simple: s->e is ++ * still set and completion callback will be called shortly and all ++ * pended requests will be submitted from there. ++ */ ++ } ++} ++ ++static uint64_t laio_max_batch(LinuxAioState *s, uint64_t dev_max_batch) ++{ ++ uint64_t max_batch = s->aio_context->aio_max_batch ?: DEFAULT_MAX_BATCH; ++ ++ /* ++ * AIO context can be shared between multiple block devices, so ++ * `dev_max_batch` allows reducing the batch size for latency-sensitive ++ * devices. ++ */ ++ max_batch = MIN_NON_ZERO(dev_max_batch, max_batch); ++ ++ /* limit the batch with the number of available events */ ++ max_batch = MIN_NON_ZERO(MAX_EVENTS - s->io_q.in_flight, max_batch); ++ ++ return max_batch; ++} ++ ++static void laio_deferred_fn(void *opaque) ++{ ++ LinuxAioState *s = opaque; ++ ++ if (!s->io_q.blocked && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { ++ ioq_submit(s); ++ } ++} ++ ++static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, ++ int type, uint64_t dev_max_batch) ++{ ++ LinuxAioState *s = laiocb->ctx; ++ struct iocb *iocbs = &laiocb->iocb; ++ QEMUIOVector *qiov = laiocb->qiov; ++ ++ switch (type) { ++ case QEMU_AIO_WRITE: ++ io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); ++ break; ++ case QEMU_AIO_ZONE_APPEND: ++ io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); ++ break; ++ case QEMU_AIO_READ: ++ io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset); ++ break; ++ case QEMU_AIO_FLUSH: ++ io_prep_fdsync(iocbs, fd); ++ break; ++ /* Currently Linux kernel does not support other operations */ ++ default: ++ fprintf(stderr, "%s: invalid AIO request type 0x%x.\n", ++ __func__, type); ++ return -EIO; ++ } ++ io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e)); ++ ++ QSIMPLEQ_INSERT_TAIL(&s->io_q.pending, laiocb, next); ++ s->io_q.in_queue++; ++ if (!s->io_q.blocked) { ++ if (s->io_q.in_queue >= laio_max_batch(s, dev_max_batch)) { ++ ioq_submit(s); ++ } else { ++ defer_call(laio_deferred_fn, s); ++ } ++ } ++ ++ return 0; ++} ++ ++int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, ++ int type, uint64_t dev_max_batch) ++{ ++ int ret; ++ AioContext *ctx = qemu_get_current_aio_context(); ++ struct qemu_laiocb laiocb = { ++ .co = qemu_coroutine_self(), ++ .nbytes = qiov ? qiov->size : 0, ++ .ctx = aio_get_linux_aio(ctx), ++ .ret = -EINPROGRESS, ++ .is_read = (type == QEMU_AIO_READ), ++ .qiov = qiov, ++ }; ++ ++ ret = laio_do_submit(fd, &laiocb, offset, type, dev_max_batch); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (laiocb.ret == -EINPROGRESS) { ++ qemu_coroutine_yield(); ++ } ++ return laiocb.ret; ++} ++ ++void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context) ++{ ++ aio_set_event_notifier(old_context, &s->e, NULL, NULL, NULL); ++ qemu_bh_delete(s->completion_bh); ++ s->aio_context = NULL; ++} ++ ++void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context) ++{ ++ s->aio_context = new_context; ++ s->completion_bh = aio_bh_new(new_context, qemu_laio_completion_bh, s); ++ aio_set_event_notifier(new_context, &s->e, ++ qemu_laio_completion_cb, ++ qemu_laio_poll_cb, ++ qemu_laio_poll_ready); ++} ++ ++LinuxAioState *laio_init(Error **errp) ++{ ++ int rc; ++ LinuxAioState *s; ++ ++ s = g_malloc0(sizeof(*s)); ++ rc = event_notifier_init(&s->e, false); ++ if (rc < 0) { ++ error_setg_errno(errp, -rc, "failed to initialize event notifier"); ++ goto out_free_state; ++ } ++ ++ rc = io_setup(MAX_EVENTS, &s->ctx); ++ if (rc < 0) { ++ error_setg_errno(errp, -rc, "failed to create linux AIO context"); ++ goto out_close_efd; ++ } ++ ++ ioq_init(&s->io_q); ++ ++ return s; ++ ++out_close_efd: ++ event_notifier_cleanup(&s->e); ++out_free_state: ++ g_free(s); ++ return NULL; ++} ++ ++void laio_cleanup(LinuxAioState *s) ++{ ++ event_notifier_cleanup(&s->e); ++ ++ if (io_destroy(s->ctx) != 0) { ++ fprintf(stderr, "%s: destroy AIO context %p failed\n", ++ __func__, &s->ctx); ++ } ++ g_free(s); ++} ++ ++bool laio_has_fdsync(int fd) ++{ ++ struct iocb cb; ++ struct iocb *cbs[] = {&cb, NULL}; ++ ++ io_context_t ctx = 0; ++ io_setup(1, &ctx); ++ ++ /* check if host kernel supports IO_CMD_FDSYNC */ ++ io_prep_fdsync(&cb, fd); ++ int ret = io_submit(ctx, 1, cbs); ++ ++ io_destroy(ctx); ++ return (ret == -EINVAL) ? false : true; ++} +diff --git a/qcow2/lib/block/mirror.c b/qcow2/lib/block/mirror.c +new file mode 100644 +index 00000000..61f0a717 +--- /dev/null ++++ b/qcow2/lib/block/mirror.c +@@ -0,0 +1,2082 @@ ++/* ++ * Image mirroring ++ * ++ * Copyright Red Hat, Inc. 2012 ++ * ++ * Authors: ++ * Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++#include "qemu/coroutine.h" ++#include "qemu/range.h" ++#include "trace.h" ++#include "block/blockjob_int.h" ++#include "block/block_int.h" ++#include "block/dirty-bitmap.h" ++#include "sysemu/block-backend.h" ++#include "qapi/error.h" ++#include "qemu/ratelimit.h" ++#include "qemu/bitmap.h" ++#include "qemu/memalign.h" ++ ++#define MAX_IN_FLIGHT 16 ++#define MAX_IO_BYTES (1 << 20) /* 1 Mb */ ++#define DEFAULT_MIRROR_BUF_SIZE (MAX_IN_FLIGHT * MAX_IO_BYTES) ++ ++/* The mirroring buffer is a list of granularity-sized chunks. ++ * Free chunks are organized in a list. ++ */ ++typedef struct MirrorBuffer { ++ QSIMPLEQ_ENTRY(MirrorBuffer) next; ++} MirrorBuffer; ++ ++typedef struct MirrorOp MirrorOp; ++ ++typedef struct MirrorBlockJob { ++ BlockJob common; ++ BlockBackend *target; ++ BlockDriverState *mirror_top_bs; ++ BlockDriverState *base; ++ BlockDriverState *base_overlay; ++ ++ /* The name of the graph node to replace */ ++ char *replaces; ++ /* The BDS to replace */ ++ BlockDriverState *to_replace; ++ /* Used to block operations on the drive-mirror-replace target */ ++ Error *replace_blocker; ++ bool is_none_mode; ++ BlockMirrorBackingMode backing_mode; ++ /* Whether the target image requires explicit zero-initialization */ ++ bool zero_target; ++ /* ++ * To be accesssed with atomics. Written only under the BQL (required by the ++ * current implementation of mirror_change()). ++ */ ++ MirrorCopyMode copy_mode; ++ BlockdevOnError on_source_error, on_target_error; ++ /* ++ * To be accessed with atomics. ++ * ++ * Set when the target is synced (dirty bitmap is clean, nothing in flight) ++ * and the job is running in active mode. ++ */ ++ bool actively_synced; ++ bool should_complete; ++ int64_t granularity; ++ size_t buf_size; ++ int64_t bdev_length; ++ unsigned long *cow_bitmap; ++ BdrvDirtyBitmap *dirty_bitmap; ++ BdrvDirtyBitmapIter *dbi; ++ uint8_t *buf; ++ QSIMPLEQ_HEAD(, MirrorBuffer) buf_free; ++ int buf_free_count; ++ ++ uint64_t last_pause_ns; ++ unsigned long *in_flight_bitmap; ++ unsigned in_flight; ++ int64_t bytes_in_flight; ++ QTAILQ_HEAD(, MirrorOp) ops_in_flight; ++ int ret; ++ bool unmap; ++ int target_cluster_size; ++ int max_iov; ++ bool initial_zeroing_ongoing; ++ int in_active_write_counter; ++ int64_t active_write_bytes_in_flight; ++ bool prepared; ++ bool in_drain; ++ bool base_ro; ++} MirrorBlockJob; ++ ++typedef struct MirrorBDSOpaque { ++ MirrorBlockJob *job; ++ bool stop; ++ bool is_commit; ++} MirrorBDSOpaque; ++ ++struct MirrorOp { ++ MirrorBlockJob *s; ++ QEMUIOVector qiov; ++ int64_t offset; ++ uint64_t bytes; ++ ++ /* The pointee is set by mirror_co_read(), mirror_co_zero(), and ++ * mirror_co_discard() before yielding for the first time */ ++ int64_t *bytes_handled; ++ ++ bool is_pseudo_op; ++ bool is_active_write; ++ bool is_in_flight; ++ CoQueue waiting_requests; ++ Coroutine *co; ++ MirrorOp *waiting_for_op; ++ ++ QTAILQ_ENTRY(MirrorOp) next; ++}; ++ ++typedef enum MirrorMethod { ++ MIRROR_METHOD_COPY, ++ MIRROR_METHOD_ZERO, ++ MIRROR_METHOD_DISCARD, ++} MirrorMethod; ++ ++static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, ++ int error) ++{ ++ qatomic_set(&s->actively_synced, false); ++ if (read) { ++ return block_job_error_action(&s->common, s->on_source_error, ++ true, error); ++ } else { ++ return block_job_error_action(&s->common, s->on_target_error, ++ false, error); ++ } ++} ++ ++static void coroutine_fn mirror_wait_on_conflicts(MirrorOp *self, ++ MirrorBlockJob *s, ++ uint64_t offset, ++ uint64_t bytes) ++{ ++ uint64_t self_start_chunk = offset / s->granularity; ++ uint64_t self_end_chunk = DIV_ROUND_UP(offset + bytes, s->granularity); ++ uint64_t self_nb_chunks = self_end_chunk - self_start_chunk; ++ ++ while (find_next_bit(s->in_flight_bitmap, self_end_chunk, ++ self_start_chunk) < self_end_chunk && ++ s->ret >= 0) ++ { ++ MirrorOp *op; ++ ++ QTAILQ_FOREACH(op, &s->ops_in_flight, next) { ++ uint64_t op_start_chunk = op->offset / s->granularity; ++ uint64_t op_nb_chunks = DIV_ROUND_UP(op->offset + op->bytes, ++ s->granularity) - ++ op_start_chunk; ++ ++ if (op == self) { ++ continue; ++ } ++ ++ if (ranges_overlap(self_start_chunk, self_nb_chunks, ++ op_start_chunk, op_nb_chunks)) ++ { ++ if (self) { ++ /* ++ * If the operation is already (indirectly) waiting for us, ++ * or will wait for us as soon as it wakes up, then just go ++ * on (instead of producing a deadlock in the former case). ++ */ ++ if (op->waiting_for_op) { ++ continue; ++ } ++ ++ self->waiting_for_op = op; ++ } ++ ++ qemu_co_queue_wait(&op->waiting_requests, NULL); ++ ++ if (self) { ++ self->waiting_for_op = NULL; ++ } ++ ++ break; ++ } ++ } ++ } ++} ++ ++static void coroutine_fn mirror_iteration_done(MirrorOp *op, int ret) ++{ ++ MirrorBlockJob *s = op->s; ++ struct iovec *iov; ++ int64_t chunk_num; ++ int i, nb_chunks; ++ ++ trace_mirror_iteration_done(s, op->offset, op->bytes, ret); ++ ++ s->in_flight--; ++ s->bytes_in_flight -= op->bytes; ++ iov = op->qiov.iov; ++ for (i = 0; i < op->qiov.niov; i++) { ++ MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base; ++ QSIMPLEQ_INSERT_TAIL(&s->buf_free, buf, next); ++ s->buf_free_count++; ++ } ++ ++ chunk_num = op->offset / s->granularity; ++ nb_chunks = DIV_ROUND_UP(op->bytes, s->granularity); ++ ++ bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks); ++ QTAILQ_REMOVE(&s->ops_in_flight, op, next); ++ if (ret >= 0) { ++ if (s->cow_bitmap) { ++ bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); ++ } ++ if (!s->initial_zeroing_ongoing) { ++ job_progress_update(&s->common.job, op->bytes); ++ } ++ } ++ qemu_iovec_destroy(&op->qiov); ++ ++ qemu_co_queue_restart_all(&op->waiting_requests); ++ g_free(op); ++} ++ ++static void coroutine_fn mirror_write_complete(MirrorOp *op, int ret) ++{ ++ MirrorBlockJob *s = op->s; ++ ++ if (ret < 0) { ++ BlockErrorAction action; ++ ++ bdrv_set_dirty_bitmap(s->dirty_bitmap, op->offset, op->bytes); ++ action = mirror_error_action(s, false, -ret); ++ if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { ++ s->ret = ret; ++ } ++ } ++ ++ mirror_iteration_done(op, ret); ++} ++ ++static void coroutine_fn mirror_read_complete(MirrorOp *op, int ret) ++{ ++ MirrorBlockJob *s = op->s; ++ ++ if (ret < 0) { ++ BlockErrorAction action; ++ ++ bdrv_set_dirty_bitmap(s->dirty_bitmap, op->offset, op->bytes); ++ action = mirror_error_action(s, true, -ret); ++ if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { ++ s->ret = ret; ++ } ++ ++ mirror_iteration_done(op, ret); ++ return; ++ } ++ ++ ret = blk_co_pwritev(s->target, op->offset, op->qiov.size, &op->qiov, 0); ++ mirror_write_complete(op, ret); ++} ++ ++/* Clip bytes relative to offset to not exceed end-of-file */ ++static inline int64_t mirror_clip_bytes(MirrorBlockJob *s, ++ int64_t offset, ++ int64_t bytes) ++{ ++ return MIN(bytes, s->bdev_length - offset); ++} ++ ++/* Round offset and/or bytes to target cluster if COW is needed, and ++ * return the offset of the adjusted tail against original. */ ++static int coroutine_fn mirror_cow_align(MirrorBlockJob *s, int64_t *offset, ++ uint64_t *bytes) ++{ ++ bool need_cow; ++ int ret = 0; ++ int64_t align_offset = *offset; ++ int64_t align_bytes = *bytes; ++ int max_bytes = s->granularity * s->max_iov; ++ ++ need_cow = !test_bit(*offset / s->granularity, s->cow_bitmap); ++ need_cow |= !test_bit((*offset + *bytes - 1) / s->granularity, ++ s->cow_bitmap); ++ if (need_cow) { ++ bdrv_round_to_subclusters(blk_bs(s->target), *offset, *bytes, ++ &align_offset, &align_bytes); ++ } ++ ++ if (align_bytes > max_bytes) { ++ align_bytes = max_bytes; ++ if (need_cow) { ++ align_bytes = QEMU_ALIGN_DOWN(align_bytes, s->target_cluster_size); ++ } ++ } ++ /* Clipping may result in align_bytes unaligned to chunk boundary, but ++ * that doesn't matter because it's already the end of source image. */ ++ align_bytes = mirror_clip_bytes(s, align_offset, align_bytes); ++ ++ ret = align_offset + align_bytes - (*offset + *bytes); ++ *offset = align_offset; ++ *bytes = align_bytes; ++ assert(ret >= 0); ++ return ret; ++} ++ ++static inline void coroutine_fn ++mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) ++{ ++ MirrorOp *op; ++ ++ QTAILQ_FOREACH(op, &s->ops_in_flight, next) { ++ /* ++ * Do not wait on pseudo ops, because it may in turn wait on ++ * some other operation to start, which may in fact be the ++ * caller of this function. Since there is only one pseudo op ++ * at any given time, we will always find some real operation ++ * to wait on. ++ * Also, do not wait on active operations, because they do not ++ * use up in-flight slots. ++ */ ++ if (!op->is_pseudo_op && op->is_in_flight && !op->is_active_write) { ++ qemu_co_queue_wait(&op->waiting_requests, NULL); ++ return; ++ } ++ } ++ abort(); ++} ++ ++/* Perform a mirror copy operation. ++ * ++ * *op->bytes_handled is set to the number of bytes copied after and ++ * including offset, excluding any bytes copied prior to offset due ++ * to alignment. This will be op->bytes if no alignment is necessary, ++ * or (new_end - op->offset) if the tail is rounded up or down due to ++ * alignment or buffer limit. ++ */ ++static void coroutine_fn mirror_co_read(void *opaque) ++{ ++ MirrorOp *op = opaque; ++ MirrorBlockJob *s = op->s; ++ int nb_chunks; ++ uint64_t ret; ++ uint64_t max_bytes; ++ ++ max_bytes = s->granularity * s->max_iov; ++ ++ /* We can only handle as much as buf_size at a time. */ ++ op->bytes = MIN(s->buf_size, MIN(max_bytes, op->bytes)); ++ assert(op->bytes); ++ assert(op->bytes < BDRV_REQUEST_MAX_BYTES); ++ *op->bytes_handled = op->bytes; ++ ++ if (s->cow_bitmap) { ++ *op->bytes_handled += mirror_cow_align(s, &op->offset, &op->bytes); ++ } ++ /* Cannot exceed BDRV_REQUEST_MAX_BYTES + INT_MAX */ ++ assert(*op->bytes_handled <= UINT_MAX); ++ assert(op->bytes <= s->buf_size); ++ /* The offset is granularity-aligned because: ++ * 1) Caller passes in aligned values; ++ * 2) mirror_cow_align is used only when target cluster is larger. */ ++ assert(QEMU_IS_ALIGNED(op->offset, s->granularity)); ++ /* The range is sector-aligned, since bdrv_getlength() rounds up. */ ++ assert(QEMU_IS_ALIGNED(op->bytes, BDRV_SECTOR_SIZE)); ++ nb_chunks = DIV_ROUND_UP(op->bytes, s->granularity); ++ ++ while (s->buf_free_count < nb_chunks) { ++ trace_mirror_yield_in_flight(s, op->offset, s->in_flight); ++ mirror_wait_for_free_in_flight_slot(s); ++ } ++ ++ /* Now make a QEMUIOVector taking enough granularity-sized chunks ++ * from s->buf_free. ++ */ ++ qemu_iovec_init(&op->qiov, nb_chunks); ++ while (nb_chunks-- > 0) { ++ MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free); ++ size_t remaining = op->bytes - op->qiov.size; ++ ++ QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next); ++ s->buf_free_count--; ++ qemu_iovec_add(&op->qiov, buf, MIN(s->granularity, remaining)); ++ } ++ ++ /* Copy the dirty cluster. */ ++ s->in_flight++; ++ s->bytes_in_flight += op->bytes; ++ op->is_in_flight = true; ++ trace_mirror_one_iteration(s, op->offset, op->bytes); ++ ++ WITH_GRAPH_RDLOCK_GUARD() { ++ ret = bdrv_co_preadv(s->mirror_top_bs->backing, op->offset, op->bytes, ++ &op->qiov, 0); ++ } ++ mirror_read_complete(op, ret); ++} ++ ++static void coroutine_fn mirror_co_zero(void *opaque) ++{ ++ MirrorOp *op = opaque; ++ int ret; ++ ++ op->s->in_flight++; ++ op->s->bytes_in_flight += op->bytes; ++ *op->bytes_handled = op->bytes; ++ op->is_in_flight = true; ++ ++ ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes, ++ op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0); ++ mirror_write_complete(op, ret); ++} ++ ++static void coroutine_fn mirror_co_discard(void *opaque) ++{ ++ MirrorOp *op = opaque; ++ int ret; ++ ++ op->s->in_flight++; ++ op->s->bytes_in_flight += op->bytes; ++ *op->bytes_handled = op->bytes; ++ op->is_in_flight = true; ++ ++ ret = blk_co_pdiscard(op->s->target, op->offset, op->bytes); ++ mirror_write_complete(op, ret); ++} ++ ++static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, ++ unsigned bytes, MirrorMethod mirror_method) ++{ ++ MirrorOp *op; ++ Coroutine *co; ++ int64_t bytes_handled = -1; ++ ++ op = g_new(MirrorOp, 1); ++ *op = (MirrorOp){ ++ .s = s, ++ .offset = offset, ++ .bytes = bytes, ++ .bytes_handled = &bytes_handled, ++ }; ++ qemu_co_queue_init(&op->waiting_requests); ++ ++ switch (mirror_method) { ++ case MIRROR_METHOD_COPY: ++ co = qemu_coroutine_create(mirror_co_read, op); ++ break; ++ case MIRROR_METHOD_ZERO: ++ co = qemu_coroutine_create(mirror_co_zero, op); ++ break; ++ case MIRROR_METHOD_DISCARD: ++ co = qemu_coroutine_create(mirror_co_discard, op); ++ break; ++ default: ++ abort(); ++ } ++ op->co = co; ++ ++ QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next); ++ qemu_coroutine_enter(co); ++ /* At this point, ownership of op has been moved to the coroutine ++ * and the object may already be freed */ ++ ++ /* Assert that this value has been set */ ++ assert(bytes_handled >= 0); ++ ++ /* Same assertion as in mirror_co_read() (and for mirror_co_read() ++ * and mirror_co_discard(), bytes_handled == op->bytes, which ++ * is the @bytes parameter given to this function) */ ++ assert(bytes_handled <= UINT_MAX); ++ return bytes_handled; ++} ++ ++static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s) ++{ ++ BlockDriverState *source; ++ MirrorOp *pseudo_op; ++ int64_t offset; ++ /* At least the first dirty chunk is mirrored in one iteration. */ ++ int nb_chunks = 1; ++ bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target)); ++ int max_io_bytes = MAX(s->buf_size / MAX_IN_FLIGHT, MAX_IO_BYTES); ++ ++ bdrv_graph_co_rdlock(); ++ source = s->mirror_top_bs->backing->bs; ++ bdrv_graph_co_rdunlock(); ++ ++ bdrv_dirty_bitmap_lock(s->dirty_bitmap); ++ offset = bdrv_dirty_iter_next(s->dbi); ++ if (offset < 0) { ++ bdrv_set_dirty_iter(s->dbi, 0); ++ offset = bdrv_dirty_iter_next(s->dbi); ++ trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap)); ++ assert(offset >= 0); ++ } ++ bdrv_dirty_bitmap_unlock(s->dirty_bitmap); ++ ++ /* ++ * Wait for concurrent requests to @offset. The next loop will limit the ++ * copied area based on in_flight_bitmap so we only copy an area that does ++ * not overlap with concurrent in-flight requests. Still, we would like to ++ * copy something, so wait until there are at least no more requests to the ++ * very beginning of the area. ++ */ ++ mirror_wait_on_conflicts(NULL, s, offset, 1); ++ ++ job_pause_point(&s->common.job); ++ ++ /* Find the number of consecutive dirty chunks following the first dirty ++ * one, and wait for in flight requests in them. */ ++ bdrv_dirty_bitmap_lock(s->dirty_bitmap); ++ while (nb_chunks * s->granularity < s->buf_size) { ++ int64_t next_dirty; ++ int64_t next_offset = offset + nb_chunks * s->granularity; ++ int64_t next_chunk = next_offset / s->granularity; ++ if (next_offset >= s->bdev_length || ++ !bdrv_dirty_bitmap_get_locked(s->dirty_bitmap, next_offset)) { ++ break; ++ } ++ if (test_bit(next_chunk, s->in_flight_bitmap)) { ++ break; ++ } ++ ++ next_dirty = bdrv_dirty_iter_next(s->dbi); ++ if (next_dirty > next_offset || next_dirty < 0) { ++ /* The bitmap iterator's cache is stale, refresh it */ ++ bdrv_set_dirty_iter(s->dbi, next_offset); ++ next_dirty = bdrv_dirty_iter_next(s->dbi); ++ } ++ assert(next_dirty == next_offset); ++ nb_chunks++; ++ } ++ ++ /* Clear dirty bits before querying the block status, because ++ * calling bdrv_block_status_above could yield - if some blocks are ++ * marked dirty in this window, we need to know. ++ */ ++ bdrv_reset_dirty_bitmap_locked(s->dirty_bitmap, offset, ++ nb_chunks * s->granularity); ++ bdrv_dirty_bitmap_unlock(s->dirty_bitmap); ++ ++ /* Before claiming an area in the in-flight bitmap, we have to ++ * create a MirrorOp for it so that conflicting requests can wait ++ * for it. mirror_perform() will create the real MirrorOps later, ++ * for now we just create a pseudo operation that will wake up all ++ * conflicting requests once all real operations have been ++ * launched. */ ++ pseudo_op = g_new(MirrorOp, 1); ++ *pseudo_op = (MirrorOp){ ++ .offset = offset, ++ .bytes = nb_chunks * s->granularity, ++ .is_pseudo_op = true, ++ }; ++ qemu_co_queue_init(&pseudo_op->waiting_requests); ++ QTAILQ_INSERT_TAIL(&s->ops_in_flight, pseudo_op, next); ++ ++ bitmap_set(s->in_flight_bitmap, offset / s->granularity, nb_chunks); ++ while (nb_chunks > 0 && offset < s->bdev_length) { ++ int ret; ++ int64_t io_bytes; ++ int64_t io_bytes_acct; ++ MirrorMethod mirror_method = MIRROR_METHOD_COPY; ++ ++ assert(!(offset % s->granularity)); ++ WITH_GRAPH_RDLOCK_GUARD() { ++ ret = bdrv_co_block_status_above(source, NULL, offset, ++ nb_chunks * s->granularity, ++ &io_bytes, NULL, NULL); ++ } ++ if (ret < 0) { ++ io_bytes = MIN(nb_chunks * s->granularity, max_io_bytes); ++ } else if (ret & BDRV_BLOCK_DATA) { ++ io_bytes = MIN(io_bytes, max_io_bytes); ++ } ++ ++ io_bytes -= io_bytes % s->granularity; ++ if (io_bytes < s->granularity) { ++ io_bytes = s->granularity; ++ } else if (ret >= 0 && !(ret & BDRV_BLOCK_DATA)) { ++ int64_t target_offset; ++ int64_t target_bytes; ++ WITH_GRAPH_RDLOCK_GUARD() { ++ bdrv_round_to_subclusters(blk_bs(s->target), offset, io_bytes, ++ &target_offset, &target_bytes); ++ } ++ if (target_offset == offset && ++ target_bytes == io_bytes) { ++ mirror_method = ret & BDRV_BLOCK_ZERO ? ++ MIRROR_METHOD_ZERO : ++ MIRROR_METHOD_DISCARD; ++ } ++ } ++ ++ while (s->in_flight >= MAX_IN_FLIGHT) { ++ trace_mirror_yield_in_flight(s, offset, s->in_flight); ++ mirror_wait_for_free_in_flight_slot(s); ++ } ++ ++ if (s->ret < 0) { ++ ret = 0; ++ goto fail; ++ } ++ ++ io_bytes = mirror_clip_bytes(s, offset, io_bytes); ++ io_bytes = mirror_perform(s, offset, io_bytes, mirror_method); ++ if (mirror_method != MIRROR_METHOD_COPY && write_zeroes_ok) { ++ io_bytes_acct = 0; ++ } else { ++ io_bytes_acct = io_bytes; ++ } ++ assert(io_bytes); ++ offset += io_bytes; ++ nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity); ++ block_job_ratelimit_processed_bytes(&s->common, io_bytes_acct); ++ } ++ ++fail: ++ QTAILQ_REMOVE(&s->ops_in_flight, pseudo_op, next); ++ qemu_co_queue_restart_all(&pseudo_op->waiting_requests); ++ g_free(pseudo_op); ++} ++ ++static void mirror_free_init(MirrorBlockJob *s) ++{ ++ int granularity = s->granularity; ++ size_t buf_size = s->buf_size; ++ uint8_t *buf = s->buf; ++ ++ assert(s->buf_free_count == 0); ++ QSIMPLEQ_INIT(&s->buf_free); ++ while (buf_size != 0) { ++ MirrorBuffer *cur = (MirrorBuffer *)buf; ++ QSIMPLEQ_INSERT_TAIL(&s->buf_free, cur, next); ++ s->buf_free_count++; ++ buf_size -= granularity; ++ buf += granularity; ++ } ++} ++ ++/* This is also used for the .pause callback. There is no matching ++ * mirror_resume() because mirror_run() will begin iterating again ++ * when the job is resumed. ++ */ ++static void coroutine_fn mirror_wait_for_all_io(MirrorBlockJob *s) ++{ ++ while (s->in_flight > 0) { ++ mirror_wait_for_free_in_flight_slot(s); ++ } ++} ++ ++/** ++ * mirror_exit_common: handle both abort() and prepare() cases. ++ * for .prepare, returns 0 on success and -errno on failure. ++ * for .abort cases, denoted by abort = true, MUST return 0. ++ */ ++static int mirror_exit_common(Job *job) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); ++ BlockJob *bjob = &s->common; ++ MirrorBDSOpaque *bs_opaque; ++ BlockDriverState *src; ++ BlockDriverState *target_bs; ++ BlockDriverState *mirror_top_bs; ++ Error *local_err = NULL; ++ bool abort = job->ret < 0; ++ int ret = 0; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (s->prepared) { ++ return 0; ++ } ++ s->prepared = true; ++ ++ bdrv_graph_rdlock_main_loop(); ++ ++ mirror_top_bs = s->mirror_top_bs; ++ bs_opaque = mirror_top_bs->opaque; ++ src = mirror_top_bs->backing->bs; ++ target_bs = blk_bs(s->target); ++ ++ if (bdrv_chain_contains(src, target_bs)) { ++ bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs); ++ } ++ ++ bdrv_release_dirty_bitmap(s->dirty_bitmap); ++ ++ /* Make sure that the source BDS doesn't go away during bdrv_replace_node, ++ * before we can call bdrv_drained_end */ ++ bdrv_ref(src); ++ bdrv_ref(mirror_top_bs); ++ bdrv_ref(target_bs); ++ ++ bdrv_graph_rdunlock_main_loop(); ++ ++ /* ++ * Remove target parent that still uses BLK_PERM_WRITE/RESIZE before ++ * inserting target_bs at s->to_replace, where we might not be able to get ++ * these permissions. ++ */ ++ blk_unref(s->target); ++ s->target = NULL; ++ ++ /* We don't access the source any more. Dropping any WRITE/RESIZE is ++ * required before it could become a backing file of target_bs. Not having ++ * these permissions any more means that we can't allow any new requests on ++ * mirror_top_bs from now on, so keep it drained. */ ++ bdrv_drained_begin(mirror_top_bs); ++ bdrv_drained_begin(target_bs); ++ bs_opaque->stop = true; ++ ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing, ++ &error_abort); ++ ++ if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { ++ BlockDriverState *backing = s->is_none_mode ? src : s->base; ++ BlockDriverState *unfiltered_target = bdrv_skip_filters(target_bs); ++ ++ if (bdrv_cow_bs(unfiltered_target) != backing) { ++ bdrv_set_backing_hd(unfiltered_target, backing, &local_err); ++ if (local_err) { ++ error_report_err(local_err); ++ local_err = NULL; ++ ret = -EPERM; ++ } ++ } ++ } else if (!abort && s->backing_mode == MIRROR_OPEN_BACKING_CHAIN) { ++ assert(!bdrv_backing_chain_next(target_bs)); ++ ret = bdrv_open_backing_file(bdrv_skip_filters(target_bs), NULL, ++ "backing", &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ local_err = NULL; ++ } ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (s->should_complete && !abort) { ++ BlockDriverState *to_replace = s->to_replace ?: src; ++ bool ro = bdrv_is_read_only(to_replace); ++ ++ if (ro != bdrv_is_read_only(target_bs)) { ++ bdrv_reopen_set_read_only(target_bs, ro, NULL); ++ } ++ ++ /* The mirror job has no requests in flight any more, but we need to ++ * drain potential other users of the BDS before changing the graph. */ ++ assert(s->in_drain); ++ bdrv_drained_begin(to_replace); ++ /* ++ * Cannot use check_to_replace_node() here, because that would ++ * check for an op blocker on @to_replace, and we have our own ++ * there. ++ */ ++ bdrv_graph_wrlock(); ++ if (bdrv_recurse_can_replace(src, to_replace)) { ++ bdrv_replace_node(to_replace, target_bs, &local_err); ++ } else { ++ error_setg(&local_err, "Can no longer replace '%s' by '%s', " ++ "because it can no longer be guaranteed that doing so " ++ "would not lead to an abrupt change of visible data", ++ to_replace->node_name, target_bs->node_name); ++ } ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(to_replace); ++ if (local_err) { ++ error_report_err(local_err); ++ ret = -EPERM; ++ } ++ } ++ if (s->to_replace) { ++ bdrv_op_unblock_all(s->to_replace, s->replace_blocker); ++ error_free(s->replace_blocker); ++ bdrv_unref(s->to_replace); ++ } ++ g_free(s->replaces); ++ ++ /* ++ * Remove the mirror filter driver from the graph. Before this, get rid of ++ * the blockers on the intermediate nodes so that the resulting state is ++ * valid. ++ */ ++ block_job_remove_all_bdrv(bjob); ++ bdrv_graph_wrlock(); ++ bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort); ++ bdrv_graph_wrunlock(); ++ ++ if (abort && s->base_ro && !bdrv_is_read_only(target_bs)) { ++ bdrv_reopen_set_read_only(target_bs, true, NULL); ++ } ++ ++ bdrv_drained_end(target_bs); ++ bdrv_unref(target_bs); ++ ++ bs_opaque->job = NULL; ++ ++ bdrv_drained_end(src); ++ bdrv_drained_end(mirror_top_bs); ++ s->in_drain = false; ++ bdrv_unref(mirror_top_bs); ++ bdrv_unref(src); ++ ++ return ret; ++} ++ ++static int mirror_prepare(Job *job) ++{ ++ return mirror_exit_common(job); ++} ++ ++static void mirror_abort(Job *job) ++{ ++ int ret = mirror_exit_common(job); ++ assert(ret == 0); ++} ++ ++static void coroutine_fn mirror_throttle(MirrorBlockJob *s) ++{ ++ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); ++ ++ if (now - s->last_pause_ns > BLOCK_JOB_SLICE_TIME) { ++ s->last_pause_ns = now; ++ job_sleep_ns(&s->common.job, 0); ++ } else { ++ job_pause_point(&s->common.job); ++ } ++} ++ ++static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) ++{ ++ int64_t offset; ++ BlockDriverState *bs; ++ BlockDriverState *target_bs = blk_bs(s->target); ++ int ret; ++ int64_t count; ++ ++ bdrv_graph_co_rdlock(); ++ bs = s->mirror_top_bs->backing->bs; ++ bdrv_graph_co_rdunlock(); ++ ++ if (s->zero_target) { ++ if (!bdrv_can_write_zeroes_with_unmap(target_bs)) { ++ bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length); ++ return 0; ++ } ++ ++ s->initial_zeroing_ongoing = true; ++ for (offset = 0; offset < s->bdev_length; ) { ++ int bytes = MIN(s->bdev_length - offset, ++ QEMU_ALIGN_DOWN(INT_MAX, s->granularity)); ++ ++ mirror_throttle(s); ++ ++ if (job_is_cancelled(&s->common.job)) { ++ s->initial_zeroing_ongoing = false; ++ return 0; ++ } ++ ++ if (s->in_flight >= MAX_IN_FLIGHT) { ++ trace_mirror_yield(s, UINT64_MAX, s->buf_free_count, ++ s->in_flight); ++ mirror_wait_for_free_in_flight_slot(s); ++ continue; ++ } ++ ++ mirror_perform(s, offset, bytes, MIRROR_METHOD_ZERO); ++ offset += bytes; ++ } ++ ++ mirror_wait_for_all_io(s); ++ s->initial_zeroing_ongoing = false; ++ } ++ ++ /* First part, loop on the sectors and initialize the dirty bitmap. */ ++ for (offset = 0; offset < s->bdev_length; ) { ++ /* Just to make sure we are not exceeding int limit. */ ++ int bytes = MIN(s->bdev_length - offset, ++ QEMU_ALIGN_DOWN(INT_MAX, s->granularity)); ++ ++ mirror_throttle(s); ++ ++ if (job_is_cancelled(&s->common.job)) { ++ return 0; ++ } ++ ++ WITH_GRAPH_RDLOCK_GUARD() { ++ ret = bdrv_co_is_allocated_above(bs, s->base_overlay, true, offset, ++ bytes, &count); ++ } ++ if (ret < 0) { ++ return ret; ++ } ++ ++ assert(count); ++ if (ret > 0) { ++ bdrv_set_dirty_bitmap(s->dirty_bitmap, offset, count); ++ } ++ offset += count; ++ } ++ return 0; ++} ++ ++/* Called when going out of the streaming phase to flush the bulk of the ++ * data to the medium, or just before completing. ++ */ ++static int coroutine_fn mirror_flush(MirrorBlockJob *s) ++{ ++ int ret = blk_co_flush(s->target); ++ if (ret < 0) { ++ if (mirror_error_action(s, false, -ret) == BLOCK_ERROR_ACTION_REPORT) { ++ s->ret = ret; ++ } ++ } ++ return ret; ++} ++ ++static int coroutine_fn mirror_run(Job *job, Error **errp) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); ++ BlockDriverState *bs; ++ MirrorBDSOpaque *mirror_top_opaque = s->mirror_top_bs->opaque; ++ BlockDriverState *target_bs = blk_bs(s->target); ++ bool need_drain = true; ++ BlockDeviceIoStatus iostatus; ++ int64_t length; ++ int64_t target_length; ++ BlockDriverInfo bdi; ++ char backing_filename[2]; /* we only need 2 characters because we are only ++ checking for a NULL string */ ++ int ret = 0; ++ ++ bdrv_graph_co_rdlock(); ++ bs = bdrv_filter_bs(s->mirror_top_bs); ++ bdrv_graph_co_rdunlock(); ++ ++ if (job_is_cancelled(&s->common.job)) { ++ goto immediate_exit; ++ } ++ ++ bdrv_graph_co_rdlock(); ++ s->bdev_length = bdrv_co_getlength(bs); ++ bdrv_graph_co_rdunlock(); ++ ++ if (s->bdev_length < 0) { ++ ret = s->bdev_length; ++ goto immediate_exit; ++ } ++ ++ target_length = blk_co_getlength(s->target); ++ if (target_length < 0) { ++ ret = target_length; ++ goto immediate_exit; ++ } ++ ++ /* Active commit must resize the base image if its size differs from the ++ * active layer. */ ++ if (s->base == blk_bs(s->target)) { ++ if (s->bdev_length > target_length) { ++ ret = blk_co_truncate(s->target, s->bdev_length, false, ++ PREALLOC_MODE_OFF, 0, NULL); ++ if (ret < 0) { ++ goto immediate_exit; ++ } ++ } ++ } else if (s->bdev_length != target_length) { ++ error_setg(errp, "Source and target image have different sizes"); ++ ret = -EINVAL; ++ goto immediate_exit; ++ } ++ ++ if (s->bdev_length == 0) { ++ /* Transition to the READY state and wait for complete. */ ++ job_transition_to_ready(&s->common.job); ++ qatomic_set(&s->actively_synced, true); ++ while (!job_cancel_requested(&s->common.job) && !s->should_complete) { ++ job_yield(&s->common.job); ++ } ++ goto immediate_exit; ++ } ++ ++ length = DIV_ROUND_UP(s->bdev_length, s->granularity); ++ s->in_flight_bitmap = bitmap_new(length); ++ ++ /* If we have no backing file yet in the destination, we cannot let ++ * the destination do COW. Instead, we copy sectors around the ++ * dirty data if needed. We need a bitmap to do that. ++ */ ++ bdrv_get_backing_filename(target_bs, backing_filename, ++ sizeof(backing_filename)); ++ bdrv_graph_co_rdlock(); ++ if (!bdrv_co_get_info(target_bs, &bdi) && bdi.cluster_size) { ++ s->target_cluster_size = bdi.cluster_size; ++ } else { ++ s->target_cluster_size = BDRV_SECTOR_SIZE; ++ } ++ if (backing_filename[0] && !bdrv_backing_chain_next(target_bs) && ++ s->granularity < s->target_cluster_size) { ++ s->buf_size = MAX(s->buf_size, s->target_cluster_size); ++ s->cow_bitmap = bitmap_new(length); ++ } ++ s->max_iov = MIN(bs->bl.max_iov, target_bs->bl.max_iov); ++ bdrv_graph_co_rdunlock(); ++ ++ s->buf = qemu_try_blockalign(bs, s->buf_size); ++ if (s->buf == NULL) { ++ ret = -ENOMEM; ++ goto immediate_exit; ++ } ++ ++ mirror_free_init(s); ++ ++ s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); ++ if (!s->is_none_mode) { ++ ret = mirror_dirty_init(s); ++ if (ret < 0 || job_is_cancelled(&s->common.job)) { ++ goto immediate_exit; ++ } ++ } ++ ++ /* ++ * Only now the job is fully initialised and mirror_top_bs should start ++ * accessing it. ++ */ ++ mirror_top_opaque->job = s; ++ ++ assert(!s->dbi); ++ s->dbi = bdrv_dirty_iter_new(s->dirty_bitmap); ++ for (;;) { ++ int64_t cnt, delta; ++ bool should_complete; ++ ++ if (s->ret < 0) { ++ ret = s->ret; ++ goto immediate_exit; ++ } ++ ++ job_pause_point(&s->common.job); ++ ++ if (job_is_cancelled(&s->common.job)) { ++ ret = 0; ++ goto immediate_exit; ++ } ++ ++ cnt = bdrv_get_dirty_count(s->dirty_bitmap); ++ /* cnt is the number of dirty bytes remaining and s->bytes_in_flight is ++ * the number of bytes currently being processed; together those are ++ * the current remaining operation length */ ++ job_progress_set_remaining(&s->common.job, ++ s->bytes_in_flight + cnt + ++ s->active_write_bytes_in_flight); ++ ++ /* Note that even when no rate limit is applied we need to yield ++ * periodically with no pending I/O so that bdrv_drain_all() returns. ++ * We do so every BLKOCK_JOB_SLICE_TIME nanoseconds, or when there is ++ * an error, or when the source is clean, whichever comes first. */ ++ delta = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->last_pause_ns; ++ WITH_JOB_LOCK_GUARD() { ++ iostatus = s->common.iostatus; ++ } ++ if (delta < BLOCK_JOB_SLICE_TIME && ++ iostatus == BLOCK_DEVICE_IO_STATUS_OK) { ++ if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 || ++ (cnt == 0 && s->in_flight > 0)) { ++ trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight); ++ mirror_wait_for_free_in_flight_slot(s); ++ continue; ++ } else if (cnt != 0) { ++ mirror_iteration(s); ++ } ++ } ++ ++ should_complete = false; ++ if (s->in_flight == 0 && cnt == 0) { ++ trace_mirror_before_flush(s); ++ if (!job_is_ready(&s->common.job)) { ++ if (mirror_flush(s) < 0) { ++ /* Go check s->ret. */ ++ continue; ++ } ++ /* We're out of the streaming phase. From now on, if the job ++ * is cancelled we will actually complete all pending I/O and ++ * report completion. This way, block-job-cancel will leave ++ * the target in a consistent state. ++ */ ++ job_transition_to_ready(&s->common.job); ++ } ++ if (qatomic_read(&s->copy_mode) != MIRROR_COPY_MODE_BACKGROUND) { ++ qatomic_set(&s->actively_synced, true); ++ } ++ ++ should_complete = s->should_complete || ++ job_cancel_requested(&s->common.job); ++ cnt = bdrv_get_dirty_count(s->dirty_bitmap); ++ } ++ ++ if (cnt == 0 && should_complete) { ++ /* The dirty bitmap is not updated while operations are pending. ++ * If we're about to exit, wait for pending operations before ++ * calling bdrv_get_dirty_count(bs), or we may exit while the ++ * source has dirty data to copy! ++ * ++ * Note that I/O can be submitted by the guest while ++ * mirror_populate runs, so pause it now. Before deciding ++ * whether to switch to target check one last time if I/O has ++ * come in the meanwhile, and if not flush the data to disk. ++ */ ++ trace_mirror_before_drain(s, cnt); ++ ++ s->in_drain = true; ++ bdrv_drained_begin(bs); ++ ++ /* Must be zero because we are drained */ ++ assert(s->in_active_write_counter == 0); ++ ++ cnt = bdrv_get_dirty_count(s->dirty_bitmap); ++ if (cnt > 0 || mirror_flush(s) < 0) { ++ bdrv_drained_end(bs); ++ s->in_drain = false; ++ continue; ++ } ++ ++ /* The two disks are in sync. Exit and report successful ++ * completion. ++ */ ++ assert(QLIST_EMPTY(&bs->tracked_requests)); ++ need_drain = false; ++ break; ++ } ++ ++ if (job_is_ready(&s->common.job) && !should_complete) { ++ if (s->in_flight == 0 && cnt == 0) { ++ trace_mirror_before_sleep(s, cnt, job_is_ready(&s->common.job), ++ BLOCK_JOB_SLICE_TIME); ++ job_sleep_ns(&s->common.job, BLOCK_JOB_SLICE_TIME); ++ } ++ } else { ++ block_job_ratelimit_sleep(&s->common); ++ } ++ s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); ++ } ++ ++immediate_exit: ++ if (s->in_flight > 0) { ++ /* We get here only if something went wrong. Either the job failed, ++ * or it was cancelled prematurely so that we do not guarantee that ++ * the target is a copy of the source. ++ */ ++ assert(ret < 0 || job_is_cancelled(&s->common.job)); ++ assert(need_drain); ++ mirror_wait_for_all_io(s); ++ } ++ ++ assert(s->in_flight == 0); ++ qemu_vfree(s->buf); ++ g_free(s->cow_bitmap); ++ g_free(s->in_flight_bitmap); ++ bdrv_dirty_iter_free(s->dbi); ++ ++ if (need_drain) { ++ s->in_drain = true; ++ bdrv_drained_begin(bs); ++ } ++ ++ return ret; ++} ++ ++static void mirror_complete(Job *job, Error **errp) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); ++ ++ if (!job_is_ready(job)) { ++ error_setg(errp, "The active block job '%s' cannot be completed", ++ job->id); ++ return; ++ } ++ ++ /* block all operations on to_replace bs */ ++ if (s->replaces) { ++ s->to_replace = bdrv_find_node(s->replaces); ++ if (!s->to_replace) { ++ error_setg(errp, "Node name '%s' not found", s->replaces); ++ return; ++ } ++ ++ /* TODO Translate this into child freeze system. */ ++ error_setg(&s->replace_blocker, ++ "block device is in use by block-job-complete"); ++ bdrv_op_block_all(s->to_replace, s->replace_blocker); ++ bdrv_ref(s->to_replace); ++ } ++ ++ s->should_complete = true; ++ ++ /* If the job is paused, it will be re-entered when it is resumed */ ++ WITH_JOB_LOCK_GUARD() { ++ if (!job->paused) { ++ job_enter_cond_locked(job, NULL); ++ } ++ } ++} ++ ++static void coroutine_fn mirror_pause(Job *job) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); ++ ++ mirror_wait_for_all_io(s); ++} ++ ++static bool mirror_drained_poll(BlockJob *job) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); ++ ++ /* If the job isn't paused nor cancelled, we can't be sure that it won't ++ * issue more requests. We make an exception if we've reached this point ++ * from one of our own drain sections, to avoid a deadlock waiting for ++ * ourselves. ++ */ ++ WITH_JOB_LOCK_GUARD() { ++ if (!s->common.job.paused && !job_is_cancelled_locked(&job->job) ++ && !s->in_drain) { ++ return true; ++ } ++ } ++ ++ return !!s->in_flight; ++} ++ ++static bool mirror_cancel(Job *job, bool force) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); ++ BlockDriverState *target = blk_bs(s->target); ++ ++ /* ++ * Before the job is READY, we treat any cancellation like a ++ * force-cancellation. ++ */ ++ force = force || !job_is_ready(job); ++ ++ if (force) { ++ bdrv_cancel_in_flight(target); ++ } ++ return force; ++} ++ ++static bool commit_active_cancel(Job *job, bool force) ++{ ++ /* Same as above in mirror_cancel() */ ++ return force || !job_is_ready(job); ++} ++ ++static void mirror_change(BlockJob *job, BlockJobChangeOptions *opts, ++ Error **errp) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); ++ BlockJobChangeOptionsMirror *change_opts = &opts->u.mirror; ++ MirrorCopyMode current; ++ ++ /* ++ * The implementation relies on the fact that copy_mode is only written ++ * under the BQL. Otherwise, further synchronization would be required. ++ */ ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (qatomic_read(&s->copy_mode) == change_opts->copy_mode) { ++ return; ++ } ++ ++ if (change_opts->copy_mode != MIRROR_COPY_MODE_WRITE_BLOCKING) { ++ error_setg(errp, "Change to copy mode '%s' is not implemented", ++ MirrorCopyMode_str(change_opts->copy_mode)); ++ return; ++ } ++ ++ current = qatomic_cmpxchg(&s->copy_mode, MIRROR_COPY_MODE_BACKGROUND, ++ change_opts->copy_mode); ++ if (current != MIRROR_COPY_MODE_BACKGROUND) { ++ error_setg(errp, "Expected current copy mode '%s', got '%s'", ++ MirrorCopyMode_str(MIRROR_COPY_MODE_BACKGROUND), ++ MirrorCopyMode_str(current)); ++ } ++} ++ ++static void mirror_query(BlockJob *job, BlockJobInfo *info) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); ++ ++ info->u.mirror = (BlockJobInfoMirror) { ++ .actively_synced = qatomic_read(&s->actively_synced), ++ }; ++} ++ ++static const BlockJobDriver mirror_job_driver = { ++ .job_driver = { ++ .instance_size = sizeof(MirrorBlockJob), ++ .job_type = JOB_TYPE_MIRROR, ++ .free = block_job_free, ++ .user_resume = block_job_user_resume, ++ .run = mirror_run, ++ .prepare = mirror_prepare, ++ .abort = mirror_abort, ++ .pause = mirror_pause, ++ .complete = mirror_complete, ++ .cancel = mirror_cancel, ++ }, ++ .drained_poll = mirror_drained_poll, ++ .change = mirror_change, ++ .query = mirror_query, ++}; ++ ++static const BlockJobDriver commit_active_job_driver = { ++ .job_driver = { ++ .instance_size = sizeof(MirrorBlockJob), ++ .job_type = JOB_TYPE_COMMIT, ++ .free = block_job_free, ++ .user_resume = block_job_user_resume, ++ .run = mirror_run, ++ .prepare = mirror_prepare, ++ .abort = mirror_abort, ++ .pause = mirror_pause, ++ .complete = mirror_complete, ++ .cancel = commit_active_cancel, ++ }, ++ .drained_poll = mirror_drained_poll, ++}; ++ ++static void coroutine_fn ++do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, ++ uint64_t offset, uint64_t bytes, ++ QEMUIOVector *qiov, int flags) ++{ ++ int ret; ++ size_t qiov_offset = 0; ++ int64_t bitmap_offset, bitmap_end; ++ ++ if (!QEMU_IS_ALIGNED(offset, job->granularity) && ++ bdrv_dirty_bitmap_get(job->dirty_bitmap, offset)) ++ { ++ /* ++ * Dirty unaligned padding: ignore it. ++ * ++ * Reasoning: ++ * 1. If we copy it, we can't reset corresponding bit in ++ * dirty_bitmap as there may be some "dirty" bytes still not ++ * copied. ++ * 2. It's already dirty, so skipping it we don't diverge mirror ++ * progress. ++ * ++ * Note, that because of this, guest write may have no contribution ++ * into mirror converge, but that's not bad, as we have background ++ * process of mirroring. If under some bad circumstances (high guest ++ * IO load) background process starve, we will not converge anyway, ++ * even if each write will contribute, as guest is not guaranteed to ++ * rewrite the whole disk. ++ */ ++ qiov_offset = QEMU_ALIGN_UP(offset, job->granularity) - offset; ++ if (bytes <= qiov_offset) { ++ /* nothing to do after shrink */ ++ return; ++ } ++ offset += qiov_offset; ++ bytes -= qiov_offset; ++ } ++ ++ if (!QEMU_IS_ALIGNED(offset + bytes, job->granularity) && ++ bdrv_dirty_bitmap_get(job->dirty_bitmap, offset + bytes - 1)) ++ { ++ uint64_t tail = (offset + bytes) % job->granularity; ++ ++ if (bytes <= tail) { ++ /* nothing to do after shrink */ ++ return; ++ } ++ bytes -= tail; ++ } ++ ++ /* ++ * Tails are either clean or shrunk, so for bitmap resetting ++ * we safely align the range down. ++ */ ++ bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity); ++ bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity); ++ if (bitmap_offset < bitmap_end) { ++ bdrv_reset_dirty_bitmap(job->dirty_bitmap, bitmap_offset, ++ bitmap_end - bitmap_offset); ++ } ++ ++ job_progress_increase_remaining(&job->common.job, bytes); ++ job->active_write_bytes_in_flight += bytes; ++ ++ switch (method) { ++ case MIRROR_METHOD_COPY: ++ ret = blk_co_pwritev_part(job->target, offset, bytes, ++ qiov, qiov_offset, flags); ++ break; ++ ++ case MIRROR_METHOD_ZERO: ++ assert(!qiov); ++ ret = blk_co_pwrite_zeroes(job->target, offset, bytes, flags); ++ break; ++ ++ case MIRROR_METHOD_DISCARD: ++ assert(!qiov); ++ ret = blk_co_pdiscard(job->target, offset, bytes); ++ break; ++ ++ default: ++ abort(); ++ } ++ ++ job->active_write_bytes_in_flight -= bytes; ++ if (ret >= 0) { ++ job_progress_update(&job->common.job, bytes); ++ } else { ++ BlockErrorAction action; ++ ++ /* ++ * We failed, so we should mark dirty the whole area, aligned up. ++ * Note that we don't care about shrunk tails if any: they were dirty ++ * at function start, and they must be still dirty, as we've locked ++ * the region for in-flight op. ++ */ ++ bitmap_offset = QEMU_ALIGN_DOWN(offset, job->granularity); ++ bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity); ++ bdrv_set_dirty_bitmap(job->dirty_bitmap, bitmap_offset, ++ bitmap_end - bitmap_offset); ++ qatomic_set(&job->actively_synced, false); ++ ++ action = mirror_error_action(job, false, -ret); ++ if (action == BLOCK_ERROR_ACTION_REPORT) { ++ if (!job->ret) { ++ job->ret = ret; ++ } ++ } ++ } ++} ++ ++static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s, ++ uint64_t offset, ++ uint64_t bytes) ++{ ++ MirrorOp *op; ++ uint64_t start_chunk = offset / s->granularity; ++ uint64_t end_chunk = DIV_ROUND_UP(offset + bytes, s->granularity); ++ ++ op = g_new(MirrorOp, 1); ++ *op = (MirrorOp){ ++ .s = s, ++ .offset = offset, ++ .bytes = bytes, ++ .is_active_write = true, ++ .is_in_flight = true, ++ .co = qemu_coroutine_self(), ++ }; ++ qemu_co_queue_init(&op->waiting_requests); ++ QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next); ++ ++ s->in_active_write_counter++; ++ ++ /* ++ * Wait for concurrent requests affecting the area. If there are already ++ * running requests that are copying off now-to-be stale data in the area, ++ * we must wait for them to finish before we begin writing fresh data to the ++ * target so that the write operations appear in the correct order. ++ * Note that background requests (see mirror_iteration()) in contrast only ++ * wait for conflicting requests at the start of the dirty area, and then ++ * (based on the in_flight_bitmap) truncate the area to copy so it will not ++ * conflict with any requests beyond that. For active writes, however, we ++ * cannot truncate that area. The request from our parent must be blocked ++ * until the area is copied in full. Therefore, we must wait for the whole ++ * area to become free of concurrent requests. ++ */ ++ mirror_wait_on_conflicts(op, s, offset, bytes); ++ ++ bitmap_set(s->in_flight_bitmap, start_chunk, end_chunk - start_chunk); ++ ++ return op; ++} ++ ++static void coroutine_fn GRAPH_RDLOCK active_write_settle(MirrorOp *op) ++{ ++ uint64_t start_chunk = op->offset / op->s->granularity; ++ uint64_t end_chunk = DIV_ROUND_UP(op->offset + op->bytes, ++ op->s->granularity); ++ ++ if (!--op->s->in_active_write_counter && ++ qatomic_read(&op->s->actively_synced)) { ++ BdrvChild *source = op->s->mirror_top_bs->backing; ++ ++ if (QLIST_FIRST(&source->bs->parents) == source && ++ QLIST_NEXT(source, next_parent) == NULL) ++ { ++ /* Assert that we are back in sync once all active write ++ * operations are settled. ++ * Note that we can only assert this if the mirror node ++ * is the source node's only parent. */ ++ assert(!bdrv_get_dirty_count(op->s->dirty_bitmap)); ++ } ++ } ++ bitmap_clear(op->s->in_flight_bitmap, start_chunk, end_chunk - start_chunk); ++ QTAILQ_REMOVE(&op->s->ops_in_flight, op, next); ++ qemu_co_queue_restart_all(&op->waiting_requests); ++ g_free(op); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_mirror_top_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); ++} ++ ++static bool should_copy_to_target(MirrorBDSOpaque *s) ++{ ++ return s->job && s->job->ret >= 0 && ++ !job_is_cancelled(&s->job->common.job) && ++ qatomic_read(&s->job->copy_mode) == MIRROR_COPY_MODE_WRITE_BLOCKING; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_mirror_top_do_write(BlockDriverState *bs, MirrorMethod method, ++ bool copy_to_target, uint64_t offset, uint64_t bytes, ++ QEMUIOVector *qiov, int flags) ++{ ++ MirrorOp *op = NULL; ++ MirrorBDSOpaque *s = bs->opaque; ++ int ret = 0; ++ ++ if (copy_to_target) { ++ op = active_write_prepare(s->job, offset, bytes); ++ } ++ ++ switch (method) { ++ case MIRROR_METHOD_COPY: ++ ret = bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); ++ break; ++ ++ case MIRROR_METHOD_ZERO: ++ ret = bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags); ++ break; ++ ++ case MIRROR_METHOD_DISCARD: ++ ret = bdrv_co_pdiscard(bs->backing, offset, bytes); ++ break; ++ ++ default: ++ abort(); ++ } ++ ++ if (!copy_to_target && s->job && s->job->dirty_bitmap) { ++ qatomic_set(&s->job->actively_synced, false); ++ bdrv_set_dirty_bitmap(s->job->dirty_bitmap, offset, bytes); ++ } ++ ++ if (ret < 0) { ++ goto out; ++ } ++ ++ if (copy_to_target) { ++ do_sync_target_write(s->job, method, offset, bytes, qiov, flags); ++ } ++ ++out: ++ if (copy_to_target) { ++ active_write_settle(op); ++ } ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_mirror_top_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ QEMUIOVector bounce_qiov; ++ void *bounce_buf; ++ int ret = 0; ++ bool copy_to_target = should_copy_to_target(bs->opaque); ++ ++ if (copy_to_target) { ++ /* The guest might concurrently modify the data to write; but ++ * the data on source and destination must match, so we have ++ * to use a bounce buffer if we are going to write to the ++ * target now. */ ++ bounce_buf = qemu_blockalign(bs, bytes); ++ iov_to_buf_full(qiov->iov, qiov->niov, 0, bounce_buf, bytes); ++ ++ qemu_iovec_init(&bounce_qiov, 1); ++ qemu_iovec_add(&bounce_qiov, bounce_buf, bytes); ++ qiov = &bounce_qiov; ++ ++ flags &= ~BDRV_REQ_REGISTERED_BUF; ++ } ++ ++ ret = bdrv_mirror_top_do_write(bs, MIRROR_METHOD_COPY, copy_to_target, ++ offset, bytes, qiov, flags); ++ ++ if (copy_to_target) { ++ qemu_iovec_destroy(&bounce_qiov); ++ qemu_vfree(bounce_buf); ++ } ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK bdrv_mirror_top_flush(BlockDriverState *bs) ++{ ++ if (bs->backing == NULL) { ++ /* we can be here after failed bdrv_append in mirror_start_job */ ++ return 0; ++ } ++ return bdrv_co_flush(bs->backing->bs); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags) ++{ ++ bool copy_to_target = should_copy_to_target(bs->opaque); ++ return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_ZERO, copy_to_target, ++ offset, bytes, NULL, flags); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_mirror_top_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ bool copy_to_target = should_copy_to_target(bs->opaque); ++ return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_DISCARD, copy_to_target, ++ offset, bytes, NULL, 0); ++} ++ ++static void GRAPH_RDLOCK bdrv_mirror_top_refresh_filename(BlockDriverState *bs) ++{ ++ if (bs->backing == NULL) { ++ /* we can be here after failed bdrv_attach_child in ++ * bdrv_set_backing_hd */ ++ return; ++ } ++ pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), ++ bs->backing->bs->filename); ++} ++ ++static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ MirrorBDSOpaque *s = bs->opaque; ++ ++ if (s->stop) { ++ /* ++ * If the job is to be stopped, we do not need to forward ++ * anything to the real image. ++ */ ++ *nperm = 0; ++ *nshared = BLK_PERM_ALL; ++ return; ++ } ++ ++ bdrv_default_perms(bs, c, role, reopen_queue, ++ perm, shared, nperm, nshared); ++ ++ if (s->is_commit) { ++ /* ++ * For commit jobs, we cannot take CONSISTENT_READ, because ++ * that permission is unshared for everything above the base ++ * node (except for filters on the base node). ++ * We also have to force-share the WRITE permission, or ++ * otherwise we would block ourselves at the base node (if ++ * writes are blocked for a node, they are also blocked for ++ * its backing file). ++ * (We could also share RESIZE, because it may be needed for ++ * the target if its size is less than the top node's; but ++ * bdrv_default_perms_for_cow() automatically shares RESIZE ++ * for backing nodes if WRITE is shared, so there is no need ++ * to do it here.) ++ */ ++ *nperm &= ~BLK_PERM_CONSISTENT_READ; ++ *nshared |= BLK_PERM_WRITE; ++ } ++} ++ ++/* Dummy node that provides consistent read to its users without requiring it ++ * from its backing file and that allows writes on the backing file chain. */ ++static BlockDriver bdrv_mirror_top = { ++ .format_name = "mirror_top", ++ .bdrv_co_preadv = bdrv_mirror_top_preadv, ++ .bdrv_co_pwritev = bdrv_mirror_top_pwritev, ++ .bdrv_co_pwrite_zeroes = bdrv_mirror_top_pwrite_zeroes, ++ .bdrv_co_pdiscard = bdrv_mirror_top_pdiscard, ++ .bdrv_co_flush = bdrv_mirror_top_flush, ++ .bdrv_refresh_filename = bdrv_mirror_top_refresh_filename, ++ .bdrv_child_perm = bdrv_mirror_top_child_perm, ++ ++ .is_filter = true, ++ .filtered_child_is_backing = true, ++}; ++ ++static BlockJob *mirror_start_job( ++ const char *job_id, BlockDriverState *bs, ++ int creation_flags, BlockDriverState *target, ++ const char *replaces, int64_t speed, ++ uint32_t granularity, int64_t buf_size, ++ BlockMirrorBackingMode backing_mode, ++ bool zero_target, ++ BlockdevOnError on_source_error, ++ BlockdevOnError on_target_error, ++ bool unmap, ++ BlockCompletionFunc *cb, ++ void *opaque, ++ const BlockJobDriver *driver, ++ bool is_none_mode, BlockDriverState *base, ++ bool auto_complete, const char *filter_node_name, ++ bool is_mirror, MirrorCopyMode copy_mode, ++ bool base_ro, ++ Error **errp) ++{ ++ MirrorBlockJob *s; ++ MirrorBDSOpaque *bs_opaque; ++ BlockDriverState *mirror_top_bs; ++ bool target_is_backing; ++ uint64_t target_perms, target_shared_perms; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (granularity == 0) { ++ granularity = bdrv_get_default_bitmap_granularity(target); ++ } ++ ++ assert(is_power_of_2(granularity)); ++ ++ if (buf_size < 0) { ++ error_setg(errp, "Invalid parameter 'buf-size'"); ++ return NULL; ++ } ++ ++ if (buf_size == 0) { ++ buf_size = DEFAULT_MIRROR_BUF_SIZE; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ if (bdrv_skip_filters(bs) == bdrv_skip_filters(target)) { ++ error_setg(errp, "Can't mirror node into itself"); ++ bdrv_graph_rdunlock_main_loop(); ++ return NULL; ++ } ++ ++ target_is_backing = bdrv_chain_contains(bs, target); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ /* In the case of active commit, add dummy driver to provide consistent ++ * reads on the top, while disabling it in the intermediate nodes, and make ++ * the backing chain writable. */ ++ mirror_top_bs = bdrv_new_open_driver(&bdrv_mirror_top, filter_node_name, ++ BDRV_O_RDWR, errp); ++ if (mirror_top_bs == NULL) { ++ return NULL; ++ } ++ if (!filter_node_name) { ++ mirror_top_bs->implicit = true; ++ } ++ ++ /* So that we can always drop this node */ ++ mirror_top_bs->never_freeze = true; ++ ++ mirror_top_bs->total_sectors = bs->total_sectors; ++ mirror_top_bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; ++ mirror_top_bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | ++ BDRV_REQ_NO_FALLBACK; ++ bs_opaque = g_new0(MirrorBDSOpaque, 1); ++ mirror_top_bs->opaque = bs_opaque; ++ ++ bs_opaque->is_commit = target_is_backing; ++ ++ bdrv_drained_begin(bs); ++ ret = bdrv_append(mirror_top_bs, bs, errp); ++ bdrv_drained_end(bs); ++ ++ if (ret < 0) { ++ bdrv_unref(mirror_top_bs); ++ return NULL; ++ } ++ ++ /* Make sure that the source is not resized while the job is running */ ++ s = block_job_create(job_id, driver, NULL, mirror_top_bs, ++ BLK_PERM_CONSISTENT_READ, ++ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | ++ BLK_PERM_WRITE, speed, ++ creation_flags, cb, opaque, errp); ++ if (!s) { ++ goto fail; ++ } ++ ++ /* The block job now has a reference to this node */ ++ bdrv_unref(mirror_top_bs); ++ ++ s->mirror_top_bs = mirror_top_bs; ++ s->base_ro = base_ro; ++ ++ /* No resize for the target either; while the mirror is still running, a ++ * consistent read isn't necessarily possible. We could possibly allow ++ * writes and graph modifications, though it would likely defeat the ++ * purpose of a mirror, so leave them blocked for now. ++ * ++ * In the case of active commit, things look a bit different, though, ++ * because the target is an already populated backing file in active use. ++ * We can allow anything except resize there.*/ ++ ++ target_perms = BLK_PERM_WRITE; ++ target_shared_perms = BLK_PERM_WRITE_UNCHANGED; ++ ++ if (target_is_backing) { ++ int64_t bs_size, target_size; ++ bs_size = bdrv_getlength(bs); ++ if (bs_size < 0) { ++ error_setg_errno(errp, -bs_size, ++ "Could not inquire top image size"); ++ goto fail; ++ } ++ ++ target_size = bdrv_getlength(target); ++ if (target_size < 0) { ++ error_setg_errno(errp, -target_size, ++ "Could not inquire base image size"); ++ goto fail; ++ } ++ ++ if (target_size < bs_size) { ++ target_perms |= BLK_PERM_RESIZE; ++ } ++ ++ target_shared_perms |= BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE; ++ } else { ++ bdrv_graph_rdlock_main_loop(); ++ if (bdrv_chain_contains(bs, bdrv_skip_filters(target))) { ++ /* ++ * We may want to allow this in the future, but it would ++ * require taking some extra care. ++ */ ++ error_setg(errp, "Cannot mirror to a filter on top of a node in " ++ "the source's backing chain"); ++ bdrv_graph_rdunlock_main_loop(); ++ goto fail; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ } ++ ++ s->target = blk_new(s->common.job.aio_context, ++ target_perms, target_shared_perms); ++ ret = blk_insert_bs(s->target, target, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ if (is_mirror) { ++ /* XXX: Mirror target could be a NBD server of target QEMU in the case ++ * of non-shared block migration. To allow migration completion, we ++ * have to allow "inactivate" of the target BB. When that happens, we ++ * know the job is drained, and the vcpus are stopped, so no write ++ * operation will be performed. Block layer already has assertions to ++ * ensure that. */ ++ blk_set_force_allow_inactivate(s->target); ++ } ++ blk_set_allow_aio_context_change(s->target, true); ++ blk_set_disable_request_queuing(s->target, true); ++ ++ bdrv_graph_rdlock_main_loop(); ++ s->replaces = g_strdup(replaces); ++ s->on_source_error = on_source_error; ++ s->on_target_error = on_target_error; ++ s->is_none_mode = is_none_mode; ++ s->backing_mode = backing_mode; ++ s->zero_target = zero_target; ++ qatomic_set(&s->copy_mode, copy_mode); ++ s->base = base; ++ s->base_overlay = bdrv_find_overlay(bs, base); ++ s->granularity = granularity; ++ s->buf_size = ROUND_UP(buf_size, granularity); ++ s->unmap = unmap; ++ if (auto_complete) { ++ s->should_complete = true; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ s->dirty_bitmap = bdrv_create_dirty_bitmap(s->mirror_top_bs, granularity, ++ NULL, errp); ++ if (!s->dirty_bitmap) { ++ goto fail; ++ } ++ ++ /* ++ * The dirty bitmap is set by bdrv_mirror_top_do_write() when not in active ++ * mode. ++ */ ++ bdrv_disable_dirty_bitmap(s->dirty_bitmap); ++ ++ bdrv_graph_wrlock(); ++ ret = block_job_add_bdrv(&s->common, "source", bs, 0, ++ BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | ++ BLK_PERM_CONSISTENT_READ, ++ errp); ++ if (ret < 0) { ++ bdrv_graph_wrunlock(); ++ goto fail; ++ } ++ ++ /* Required permissions are already taken with blk_new() */ ++ block_job_add_bdrv(&s->common, "target", target, 0, BLK_PERM_ALL, ++ &error_abort); ++ ++ /* In commit_active_start() all intermediate nodes disappear, so ++ * any jobs in them must be blocked */ ++ if (target_is_backing) { ++ BlockDriverState *iter, *filtered_target; ++ uint64_t iter_shared_perms; ++ ++ /* ++ * The topmost node with ++ * bdrv_skip_filters(filtered_target) == bdrv_skip_filters(target) ++ */ ++ filtered_target = bdrv_cow_bs(bdrv_find_overlay(bs, target)); ++ ++ assert(bdrv_skip_filters(filtered_target) == ++ bdrv_skip_filters(target)); ++ ++ /* ++ * XXX BLK_PERM_WRITE needs to be allowed so we don't block ++ * ourselves at s->base (if writes are blocked for a node, they are ++ * also blocked for its backing file). The other options would be a ++ * second filter driver above s->base (== target). ++ */ ++ iter_shared_perms = BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE; ++ ++ for (iter = bdrv_filter_or_cow_bs(bs); iter != target; ++ iter = bdrv_filter_or_cow_bs(iter)) ++ { ++ if (iter == filtered_target) { ++ /* ++ * From here on, all nodes are filters on the base. ++ * This allows us to share BLK_PERM_CONSISTENT_READ. ++ */ ++ iter_shared_perms |= BLK_PERM_CONSISTENT_READ; ++ } ++ ++ ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, ++ iter_shared_perms, errp); ++ if (ret < 0) { ++ bdrv_graph_wrunlock(); ++ goto fail; ++ } ++ } ++ ++ if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) { ++ bdrv_graph_wrunlock(); ++ goto fail; ++ } ++ } ++ bdrv_graph_wrunlock(); ++ ++ QTAILQ_INIT(&s->ops_in_flight); ++ ++ trace_mirror_start(bs, s, opaque); ++ job_start(&s->common.job); ++ ++ return &s->common; ++ ++fail: ++ if (s) { ++ /* Make sure this BDS does not go away until we have completed the graph ++ * changes below */ ++ bdrv_ref(mirror_top_bs); ++ ++ g_free(s->replaces); ++ blk_unref(s->target); ++ bs_opaque->job = NULL; ++ if (s->dirty_bitmap) { ++ bdrv_release_dirty_bitmap(s->dirty_bitmap); ++ } ++ job_early_fail(&s->common.job); ++ } ++ ++ bs_opaque->stop = true; ++ bdrv_drained_begin(bs); ++ bdrv_graph_wrlock(); ++ assert(mirror_top_bs->backing->bs == bs); ++ bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing, ++ &error_abort); ++ bdrv_replace_node(mirror_top_bs, bs, &error_abort); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(bs); ++ ++ bdrv_unref(mirror_top_bs); ++ ++ return NULL; ++} ++ ++void mirror_start(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *target, const char *replaces, ++ int creation_flags, int64_t speed, ++ uint32_t granularity, int64_t buf_size, ++ MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, ++ bool zero_target, ++ BlockdevOnError on_source_error, ++ BlockdevOnError on_target_error, ++ bool unmap, const char *filter_node_name, ++ MirrorCopyMode copy_mode, Error **errp) ++{ ++ bool is_none_mode; ++ BlockDriverState *base; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if ((mode == MIRROR_SYNC_MODE_INCREMENTAL) || ++ (mode == MIRROR_SYNC_MODE_BITMAP)) { ++ error_setg(errp, "Sync mode '%s' not supported", ++ MirrorSyncMode_str(mode)); ++ return; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ is_none_mode = mode == MIRROR_SYNC_MODE_NONE; ++ base = mode == MIRROR_SYNC_MODE_TOP ? bdrv_backing_chain_next(bs) : NULL; ++ bdrv_graph_rdunlock_main_loop(); ++ ++ mirror_start_job(job_id, bs, creation_flags, target, replaces, ++ speed, granularity, buf_size, backing_mode, zero_target, ++ on_source_error, on_target_error, unmap, NULL, NULL, ++ &mirror_job_driver, is_none_mode, base, false, ++ filter_node_name, true, copy_mode, false, errp); ++} ++ ++BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *base, int creation_flags, ++ int64_t speed, BlockdevOnError on_error, ++ const char *filter_node_name, ++ BlockCompletionFunc *cb, void *opaque, ++ bool auto_complete, Error **errp) ++{ ++ bool base_read_only; ++ BlockJob *job; ++ ++ GLOBAL_STATE_CODE(); ++ ++ base_read_only = bdrv_is_read_only(base); ++ ++ if (base_read_only) { ++ if (bdrv_reopen_set_read_only(base, false, errp) < 0) { ++ return NULL; ++ } ++ } ++ ++ job = mirror_start_job( ++ job_id, bs, creation_flags, base, NULL, speed, 0, 0, ++ MIRROR_LEAVE_BACKING_CHAIN, false, ++ on_error, on_error, true, cb, opaque, ++ &commit_active_job_driver, false, base, auto_complete, ++ filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, ++ base_read_only, errp); ++ if (!job) { ++ goto error_restore_flags; ++ } ++ ++ return job; ++ ++error_restore_flags: ++ /* ignore error and errp for bdrv_reopen, because we want to propagate ++ * the original error */ ++ if (base_read_only) { ++ bdrv_reopen_set_read_only(base, true, NULL); ++ } ++ return NULL; ++} +diff --git a/qcow2/lib/block/monitor/bitmap-qmp-cmds.c b/qcow2/lib/block/monitor/bitmap-qmp-cmds.c +new file mode 100644 +index 00000000..a738e7bb +--- /dev/null ++++ b/qcow2/lib/block/monitor/bitmap-qmp-cmds.c +@@ -0,0 +1,317 @@ ++/* ++ * QEMU block dirty bitmap QMP commands ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ * ++ * This file incorporates work covered by the following copyright and ++ * permission notice: ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "block/block-io.h" ++#include "block/block_int.h" ++#include "block/dirty-bitmap.h" ++#include "qapi/qapi-commands-block.h" ++#include "qapi/error.h" ++ ++/** ++ * block_dirty_bitmap_lookup: ++ * Return a dirty bitmap (if present), after validating ++ * the node reference and bitmap names. ++ * ++ * @node: The name of the BDS node to search for bitmaps ++ * @name: The name of the bitmap to search for ++ * @pbs: Output pointer for BDS lookup, if desired. Can be NULL. ++ * @errp: Output pointer for error information. Can be NULL. ++ * ++ * @return: A bitmap object on success, or NULL on failure. ++ */ ++BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, ++ const char *name, ++ BlockDriverState **pbs, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvDirtyBitmap *bitmap; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!node) { ++ error_setg(errp, "Node cannot be NULL"); ++ return NULL; ++ } ++ if (!name) { ++ error_setg(errp, "Bitmap name cannot be NULL"); ++ return NULL; ++ } ++ bs = bdrv_lookup_bs(node, node, NULL); ++ if (!bs) { ++ error_setg(errp, "Node '%s' not found", node); ++ return NULL; ++ } ++ ++ bitmap = bdrv_find_dirty_bitmap(bs, name); ++ if (!bitmap) { ++ error_setg(errp, "Dirty bitmap '%s' not found", name); ++ return NULL; ++ } ++ ++ if (pbs) { ++ *pbs = bs; ++ } ++ ++ return bitmap; ++} ++ ++void qmp_block_dirty_bitmap_add(const char *node, const char *name, ++ bool has_granularity, uint32_t granularity, ++ bool has_persistent, bool persistent, ++ bool has_disabled, bool disabled, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvDirtyBitmap *bitmap; ++ ++ if (!name || name[0] == '\0') { ++ error_setg(errp, "Bitmap name cannot be empty"); ++ return; ++ } ++ ++ bs = bdrv_lookup_bs(node, node, errp); ++ if (!bs) { ++ return; ++ } ++ ++ if (has_granularity) { ++ if (granularity < 512 || !is_power_of_2(granularity)) { ++ error_setg(errp, "Granularity must be power of 2 " ++ "and at least 512"); ++ return; ++ } ++ } else { ++ /* Default to cluster size, if available: */ ++ granularity = bdrv_get_default_bitmap_granularity(bs); ++ } ++ ++ if (!has_persistent) { ++ persistent = false; ++ } ++ ++ if (!has_disabled) { ++ disabled = false; ++ } ++ ++ if (persistent && ++ !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) ++ { ++ return; ++ } ++ ++ bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp); ++ if (bitmap == NULL) { ++ return; ++ } ++ ++ if (disabled) { ++ bdrv_disable_dirty_bitmap(bitmap); ++ } ++ ++ bdrv_dirty_bitmap_set_persistence(bitmap, persistent); ++} ++ ++BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, ++ bool release, ++ BlockDriverState **bitmap_bs, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvDirtyBitmap *bitmap; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); ++ if (!bitmap || !bs) { ++ return NULL; ++ } ++ ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO, ++ errp)) { ++ return NULL; ++ } ++ ++ if (bdrv_dirty_bitmap_get_persistence(bitmap) && ++ bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0) ++ { ++ return NULL; ++ } ++ ++ if (release) { ++ bdrv_release_dirty_bitmap(bitmap); ++ } ++ ++ if (bitmap_bs) { ++ *bitmap_bs = bs; ++ } ++ ++ return release ? NULL : bitmap; ++} ++ ++void qmp_block_dirty_bitmap_remove(const char *node, const char *name, ++ Error **errp) ++{ ++ block_dirty_bitmap_remove(node, name, true, NULL, errp); ++} ++ ++/** ++ * Completely clear a bitmap, for the purposes of synchronizing a bitmap ++ * immediately after a full backup operation. ++ */ ++void qmp_block_dirty_bitmap_clear(const char *node, const char *name, ++ Error **errp) ++{ ++ BdrvDirtyBitmap *bitmap; ++ BlockDriverState *bs; ++ ++ bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); ++ if (!bitmap || !bs) { ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { ++ return; ++ } ++ ++ bdrv_clear_dirty_bitmap(bitmap, NULL); ++} ++ ++void qmp_block_dirty_bitmap_enable(const char *node, const char *name, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvDirtyBitmap *bitmap; ++ ++ bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); ++ if (!bitmap) { ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { ++ return; ++ } ++ ++ bdrv_enable_dirty_bitmap(bitmap); ++} ++ ++void qmp_block_dirty_bitmap_disable(const char *node, const char *name, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvDirtyBitmap *bitmap; ++ ++ bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); ++ if (!bitmap) { ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { ++ return; ++ } ++ ++ bdrv_disable_dirty_bitmap(bitmap); ++} ++ ++BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *dst_node, ++ const char *dst_bitmap, ++ BlockDirtyBitmapOrStrList *bms, ++ HBitmap **backup, Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvDirtyBitmap *dst, *src; ++ BlockDirtyBitmapOrStrList *lst; ++ const char *src_node, *src_bitmap; ++ HBitmap *local_backup = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ dst = block_dirty_bitmap_lookup(dst_node, dst_bitmap, &bs, errp); ++ if (!dst) { ++ return NULL; ++ } ++ ++ for (lst = bms; lst; lst = lst->next) { ++ switch (lst->value->type) { ++ case QTYPE_QSTRING: ++ src_bitmap = lst->value->u.local; ++ src = bdrv_find_dirty_bitmap(bs, src_bitmap); ++ if (!src) { ++ error_setg(errp, "Dirty bitmap '%s' not found", src_bitmap); ++ goto fail; ++ } ++ break; ++ case QTYPE_QDICT: ++ src_node = lst->value->u.external.node; ++ src_bitmap = lst->value->u.external.name; ++ src = block_dirty_bitmap_lookup(src_node, src_bitmap, NULL, errp); ++ if (!src) { ++ goto fail; ++ } ++ break; ++ default: ++ abort(); ++ } ++ ++ /* We do backup only for first merge operation */ ++ if (!bdrv_merge_dirty_bitmap(dst, src, ++ local_backup ? NULL : &local_backup, ++ errp)) ++ { ++ goto fail; ++ } ++ } ++ ++ if (backup) { ++ *backup = local_backup; ++ } else { ++ hbitmap_free(local_backup); ++ } ++ ++ return dst; ++ ++fail: ++ if (local_backup) { ++ bdrv_restore_dirty_bitmap(dst, local_backup); ++ } ++ ++ return NULL; ++} ++ ++void qmp_block_dirty_bitmap_merge(const char *node, const char *target, ++ BlockDirtyBitmapOrStrList *bitmaps, ++ Error **errp) ++{ ++ block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp); ++} +diff --git a/qcow2/lib/block/progress_meter.c b/qcow2/lib/block/progress_meter.c +new file mode 100644 +index 00000000..31a170a2 +--- /dev/null ++++ b/qcow2/lib/block/progress_meter.c +@@ -0,0 +1,66 @@ ++/* ++ * Helper functionality for some process progress tracking. ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012, 2018 Red Hat, Inc. ++ * Copyright (c) 2020 Virtuozzo International GmbH ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/coroutine.h" ++#include "qemu/progress_meter.h" ++ ++void progress_init(ProgressMeter *pm) ++{ ++ qemu_mutex_init(&pm->lock); ++} ++ ++void progress_destroy(ProgressMeter *pm) ++{ ++ qemu_mutex_destroy(&pm->lock); ++} ++ ++void progress_get_snapshot(ProgressMeter *pm, uint64_t *current, ++ uint64_t *total) ++{ ++ QEMU_LOCK_GUARD(&pm->lock); ++ ++ *current = pm->current; ++ *total = pm->total; ++} ++ ++void progress_work_done(ProgressMeter *pm, uint64_t done) ++{ ++ QEMU_LOCK_GUARD(&pm->lock); ++ pm->current += done; ++} ++ ++void progress_set_remaining(ProgressMeter *pm, uint64_t remaining) ++{ ++ QEMU_LOCK_GUARD(&pm->lock); ++ pm->total = pm->current + remaining; ++} ++ ++void progress_increase_remaining(ProgressMeter *pm, uint64_t delta) ++{ ++ QEMU_LOCK_GUARD(&pm->lock); ++ pm->total += delta; ++} +diff --git a/qcow2/lib/block/qapi.c b/qcow2/lib/block/qapi.c +new file mode 100644 +index 00000000..2b5793f1 +--- /dev/null ++++ b/qcow2/lib/block/qapi.c +@@ -0,0 +1,1012 @@ ++/* ++ * Block layer qmp and info dump related functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++#include "block/qapi.h" ++#include "block/block_int.h" ++#include "block/dirty-bitmap.h" ++#include "block/throttle-groups.h" ++#include "block/write-threshold.h" ++#include "qapi/error.h" ++#include "qapi/qapi-commands-block-core.h" ++#include "qapi/qobject-output-visitor.h" ++#include "qapi/qapi-visit-block-core.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qemu/qemu-print.h" ++#include "sysemu/block-backend.h" ++ ++BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, ++ BlockDriverState *bs, ++ bool flat, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ ImageInfo **p_image_info; ++ ImageInfo *backing_info; ++ BlockDriverState *backing; ++ BlockDeviceInfo *info; ++ ++ if (!bs->drv) { ++ error_setg(errp, "Block device %s is ejected", bs->node_name); ++ return NULL; ++ } ++ ++ bdrv_refresh_filename(bs); ++ ++ info = g_malloc0(sizeof(*info)); ++ info->file = g_strdup(bs->filename); ++ info->ro = bdrv_is_read_only(bs); ++ info->drv = g_strdup(bs->drv->format_name); ++ info->encrypted = bs->encrypted; ++ ++ info->cache = g_new(BlockdevCacheInfo, 1); ++ *info->cache = (BlockdevCacheInfo) { ++ .writeback = blk ? blk_enable_write_cache(blk) : true, ++ .direct = !!(bs->open_flags & BDRV_O_NOCACHE), ++ .no_flush = !!(bs->open_flags & BDRV_O_NO_FLUSH), ++ }; ++ ++ if (bs->node_name[0]) { ++ info->node_name = g_strdup(bs->node_name); ++ } ++ ++ backing = bdrv_cow_bs(bs); ++ if (backing) { ++ info->backing_file = g_strdup(backing->filename); ++ } ++ ++ if (!QLIST_EMPTY(&bs->dirty_bitmaps)) { ++ info->has_dirty_bitmaps = true; ++ info->dirty_bitmaps = bdrv_query_dirty_bitmaps(bs); ++ } ++ ++ info->detect_zeroes = bs->detect_zeroes; ++ ++ if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) { ++ ThrottleConfig cfg; ++ BlockBackendPublic *blkp = blk_get_public(blk); ++ ++ throttle_group_get_config(&blkp->throttle_group_member, &cfg); ++ ++ info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg; ++ info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg; ++ info->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg; ++ ++ info->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg; ++ info->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg; ++ info->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg; ++ ++ info->has_bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max; ++ info->bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max; ++ info->has_bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max; ++ info->bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max; ++ info->has_bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max; ++ info->bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max; ++ ++ info->has_iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max; ++ info->iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max; ++ info->has_iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max; ++ info->iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max; ++ info->has_iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max; ++ info->iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max; ++ ++ info->has_bps_max_length = info->has_bps_max; ++ info->bps_max_length = ++ cfg.buckets[THROTTLE_BPS_TOTAL].burst_length; ++ info->has_bps_rd_max_length = info->has_bps_rd_max; ++ info->bps_rd_max_length = ++ cfg.buckets[THROTTLE_BPS_READ].burst_length; ++ info->has_bps_wr_max_length = info->has_bps_wr_max; ++ info->bps_wr_max_length = ++ cfg.buckets[THROTTLE_BPS_WRITE].burst_length; ++ ++ info->has_iops_max_length = info->has_iops_max; ++ info->iops_max_length = ++ cfg.buckets[THROTTLE_OPS_TOTAL].burst_length; ++ info->has_iops_rd_max_length = info->has_iops_rd_max; ++ info->iops_rd_max_length = ++ cfg.buckets[THROTTLE_OPS_READ].burst_length; ++ info->has_iops_wr_max_length = info->has_iops_wr_max; ++ info->iops_wr_max_length = ++ cfg.buckets[THROTTLE_OPS_WRITE].burst_length; ++ ++ info->has_iops_size = cfg.op_size; ++ info->iops_size = cfg.op_size; ++ ++ info->group = ++ g_strdup(throttle_group_get_name(&blkp->throttle_group_member)); ++ } ++ ++ info->write_threshold = bdrv_write_threshold_get(bs); ++ ++ p_image_info = &info->image; ++ info->backing_file_depth = 0; ++ ++ /* ++ * Skip automatically inserted nodes that the user isn't aware of for ++ * query-block (blk != NULL), but not for query-named-block-nodes ++ */ ++ bdrv_query_image_info(bs, p_image_info, flat, blk != NULL, errp); ++ if (*errp) { ++ qapi_free_BlockDeviceInfo(info); ++ return NULL; ++ } ++ ++ backing_info = info->image->backing_image; ++ while (backing_info) { ++ info->backing_file_depth++; ++ backing_info = backing_info->backing_image; ++ } ++ ++ return info; ++} ++ ++/* ++ * Returns 0 on success, with *p_list either set to describe snapshot ++ * information, or NULL because there are no snapshots. Returns -errno on ++ * error, with *p_list untouched. ++ */ ++int bdrv_query_snapshot_info_list(BlockDriverState *bs, ++ SnapshotInfoList **p_list, ++ Error **errp) ++{ ++ int i, sn_count; ++ QEMUSnapshotInfo *sn_tab = NULL; ++ SnapshotInfoList *head = NULL, **tail = &head; ++ SnapshotInfo *info; ++ ++ sn_count = bdrv_snapshot_list(bs, &sn_tab); ++ if (sn_count < 0) { ++ const char *dev = bdrv_get_device_name(bs); ++ switch (sn_count) { ++ case -ENOMEDIUM: ++ error_setg(errp, "Device '%s' is not inserted", dev); ++ break; ++ case -ENOTSUP: ++ error_setg(errp, ++ "Device '%s' does not support internal snapshots", ++ dev); ++ break; ++ default: ++ error_setg_errno(errp, -sn_count, ++ "Can't list snapshots of device '%s'", dev); ++ break; ++ } ++ return sn_count; ++ } ++ ++ for (i = 0; i < sn_count; i++) { ++ info = g_new0(SnapshotInfo, 1); ++ info->id = g_strdup(sn_tab[i].id_str); ++ info->name = g_strdup(sn_tab[i].name); ++ info->vm_state_size = sn_tab[i].vm_state_size; ++ info->date_sec = sn_tab[i].date_sec; ++ info->date_nsec = sn_tab[i].date_nsec; ++ info->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000; ++ info->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000; ++ info->icount = sn_tab[i].icount; ++ info->has_icount = sn_tab[i].icount != -1ULL; ++ ++ QAPI_LIST_APPEND(tail, info); ++ } ++ ++ g_free(sn_tab); ++ *p_list = head; ++ return 0; ++} ++ ++/** ++ * Helper function for other query info functions. Store information about @bs ++ * in @info, setting @errp on error. ++ */ ++static void GRAPH_RDLOCK ++bdrv_do_query_node_info(BlockDriverState *bs, BlockNodeInfo *info, Error **errp) ++{ ++ int64_t size; ++ const char *backing_filename; ++ BlockDriverInfo bdi; ++ int ret; ++ Error *err = NULL; ++ ++ size = bdrv_getlength(bs); ++ if (size < 0) { ++ error_setg_errno(errp, -size, "Can't get image size '%s'", ++ bs->exact_filename); ++ return; ++ } ++ ++ bdrv_refresh_filename(bs); ++ ++ info->filename = g_strdup(bs->filename); ++ info->format = g_strdup(bdrv_get_format_name(bs)); ++ info->virtual_size = size; ++ info->actual_size = bdrv_get_allocated_file_size(bs); ++ info->has_actual_size = info->actual_size >= 0; ++ if (bs->encrypted) { ++ info->encrypted = true; ++ info->has_encrypted = true; ++ } ++ if (bdrv_get_info(bs, &bdi) >= 0) { ++ if (bdi.cluster_size != 0) { ++ info->cluster_size = bdi.cluster_size; ++ info->has_cluster_size = true; ++ } ++ info->dirty_flag = bdi.is_dirty; ++ info->has_dirty_flag = true; ++ } ++ info->format_specific = bdrv_get_specific_info(bs, &err); ++ if (err) { ++ error_propagate(errp, err); ++ return; ++ } ++ backing_filename = bs->backing_file; ++ if (backing_filename[0] != '\0') { ++ char *backing_filename2; ++ ++ info->backing_filename = g_strdup(backing_filename); ++ backing_filename2 = bdrv_get_full_backing_filename(bs, NULL); ++ ++ /* Always report the full_backing_filename if present, even if it's the ++ * same as backing_filename. That they are same is useful info. */ ++ if (backing_filename2) { ++ info->full_backing_filename = g_strdup(backing_filename2); ++ } ++ ++ if (bs->backing_format[0]) { ++ info->backing_filename_format = g_strdup(bs->backing_format); ++ } ++ g_free(backing_filename2); ++ } ++ ++ ret = bdrv_query_snapshot_info_list(bs, &info->snapshots, &err); ++ switch (ret) { ++ case 0: ++ if (info->snapshots) { ++ info->has_snapshots = true; ++ } ++ break; ++ /* recoverable error */ ++ case -ENOMEDIUM: ++ case -ENOTSUP: ++ error_free(err); ++ break; ++ default: ++ error_propagate(errp, err); ++ return; ++ } ++} ++ ++/** ++ * bdrv_query_image_info: ++ * @bs: block node to examine ++ * @p_info: location to store image information ++ * @flat: skip backing node information ++ * @skip_implicit_filters: skip implicit filters in the backing chain ++ * @errp: location to store error information ++ * ++ * Store image information in @p_info, potentially recursively covering the ++ * backing chain. ++ * ++ * If @flat is true, do not query backing image information, i.e. ++ * (*p_info)->has_backing_image will be set to false and ++ * (*p_info)->backing_image to NULL even when the image does in fact have a ++ * backing image. ++ * ++ * If @skip_implicit_filters is true, implicit filter nodes in the backing chain ++ * will be skipped when querying backing image information. ++ * (@skip_implicit_filters is ignored when @flat is true.) ++ * ++ * @p_info will be set only on success. On error, store error in @errp. ++ */ ++void bdrv_query_image_info(BlockDriverState *bs, ++ ImageInfo **p_info, ++ bool flat, ++ bool skip_implicit_filters, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ ImageInfo *info; ++ ++ info = g_new0(ImageInfo, 1); ++ bdrv_do_query_node_info(bs, qapi_ImageInfo_base(info), errp); ++ if (*errp) { ++ goto fail; ++ } ++ ++ if (!flat) { ++ BlockDriverState *backing; ++ ++ /* ++ * Use any filtered child here (for backwards compatibility to when ++ * we always took bs->backing, which might be any filtered child). ++ */ ++ backing = bdrv_filter_or_cow_bs(bs); ++ if (skip_implicit_filters) { ++ backing = bdrv_skip_implicit_filters(backing); ++ } ++ ++ if (backing) { ++ bdrv_query_image_info(backing, &info->backing_image, false, ++ skip_implicit_filters, errp); ++ if (*errp) { ++ goto fail; ++ } ++ } ++ } ++ ++ *p_info = info; ++ return; ++ ++fail: ++ assert(*errp); ++ qapi_free_ImageInfo(info); ++} ++ ++/** ++ * bdrv_query_block_graph_info: ++ * @bs: root node to start from ++ * @p_info: location to store image information ++ * @errp: location to store error information ++ * ++ * Store image information about the graph starting from @bs in @p_info. ++ * ++ * @p_info will be set only on success. On error, store error in @errp. ++ */ ++void bdrv_query_block_graph_info(BlockDriverState *bs, ++ BlockGraphInfo **p_info, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ BlockGraphInfo *info; ++ BlockChildInfoList **children_list_tail; ++ BdrvChild *c; ++ ++ info = g_new0(BlockGraphInfo, 1); ++ bdrv_do_query_node_info(bs, qapi_BlockGraphInfo_base(info), errp); ++ if (*errp) { ++ goto fail; ++ } ++ ++ children_list_tail = &info->children; ++ ++ QLIST_FOREACH(c, &bs->children, next) { ++ BlockChildInfo *c_info; ++ ++ c_info = g_new0(BlockChildInfo, 1); ++ QAPI_LIST_APPEND(children_list_tail, c_info); ++ ++ c_info->name = g_strdup(c->name); ++ bdrv_query_block_graph_info(c->bs, &c_info->info, errp); ++ if (*errp) { ++ goto fail; ++ } ++ } ++ ++ *p_info = info; ++ return; ++ ++fail: ++ assert(*errp != NULL); ++ qapi_free_BlockGraphInfo(info); ++} ++ ++/* @p_info will be set only on success. */ ++static void GRAPH_RDLOCK ++bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, Error **errp) ++{ ++ BlockInfo *info = g_malloc0(sizeof(*info)); ++ BlockDriverState *bs = blk_bs(blk); ++ char *qdev; ++ ++ /* Skip automatically inserted nodes that the user isn't aware of */ ++ bs = bdrv_skip_implicit_filters(bs); ++ ++ info->device = g_strdup(blk_name(blk)); ++ info->type = g_strdup("unknown"); ++ info->locked = blk_dev_is_medium_locked(blk); ++ info->removable = blk_dev_has_removable_media(blk); ++ ++ qdev = blk_get_attached_dev_id(blk); ++ if (qdev && *qdev) { ++ info->qdev = qdev; ++ } else { ++ g_free(qdev); ++ } ++ ++ if (blk_dev_has_tray(blk)) { ++ info->has_tray_open = true; ++ info->tray_open = blk_dev_is_tray_open(blk); ++ } ++ ++ if (blk_iostatus_is_enabled(blk)) { ++ info->has_io_status = true; ++ info->io_status = blk_iostatus(blk); ++ } ++ ++ if (bs && bs->drv) { ++ info->inserted = bdrv_block_device_info(blk, bs, false, errp); ++ if (info->inserted == NULL) { ++ goto err; ++ } ++ } ++ ++ *p_info = info; ++ return; ++ ++ err: ++ qapi_free_BlockInfo(info); ++} ++ ++static uint64List *uint64_list(uint64_t *list, int size) ++{ ++ int i; ++ uint64List *out_list = NULL; ++ uint64List **tail = &out_list; ++ ++ for (i = 0; i < size; i++) { ++ QAPI_LIST_APPEND(tail, list[i]); ++ } ++ ++ return out_list; ++} ++ ++static BlockLatencyHistogramInfo * ++bdrv_latency_histogram_stats(BlockLatencyHistogram *hist) ++{ ++ BlockLatencyHistogramInfo *info; ++ ++ if (!hist->bins) { ++ return NULL; ++ } ++ ++ info = g_new0(BlockLatencyHistogramInfo, 1); ++ info->boundaries = uint64_list(hist->boundaries, hist->nbins - 1); ++ info->bins = uint64_list(hist->bins, hist->nbins); ++ return info; ++} ++ ++static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) ++{ ++ BlockAcctStats *stats = blk_get_stats(blk); ++ BlockAcctTimedStats *ts = NULL; ++ BlockLatencyHistogram *hgram; ++ ++ ds->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ]; ++ ds->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE]; ++ ds->zone_append_bytes = stats->nr_bytes[BLOCK_ACCT_ZONE_APPEND]; ++ ds->unmap_bytes = stats->nr_bytes[BLOCK_ACCT_UNMAP]; ++ ds->rd_operations = stats->nr_ops[BLOCK_ACCT_READ]; ++ ds->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE]; ++ ds->zone_append_operations = stats->nr_ops[BLOCK_ACCT_ZONE_APPEND]; ++ ds->unmap_operations = stats->nr_ops[BLOCK_ACCT_UNMAP]; ++ ++ ds->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ]; ++ ds->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE]; ++ ds->failed_zone_append_operations = ++ stats->failed_ops[BLOCK_ACCT_ZONE_APPEND]; ++ ds->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH]; ++ ds->failed_unmap_operations = stats->failed_ops[BLOCK_ACCT_UNMAP]; ++ ++ ds->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ]; ++ ds->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE]; ++ ds->invalid_zone_append_operations = ++ stats->invalid_ops[BLOCK_ACCT_ZONE_APPEND]; ++ ds->invalid_flush_operations = ++ stats->invalid_ops[BLOCK_ACCT_FLUSH]; ++ ds->invalid_unmap_operations = stats->invalid_ops[BLOCK_ACCT_UNMAP]; ++ ++ ds->rd_merged = stats->merged[BLOCK_ACCT_READ]; ++ ds->wr_merged = stats->merged[BLOCK_ACCT_WRITE]; ++ ds->zone_append_merged = stats->merged[BLOCK_ACCT_ZONE_APPEND]; ++ ds->unmap_merged = stats->merged[BLOCK_ACCT_UNMAP]; ++ ds->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH]; ++ ds->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE]; ++ ds->zone_append_total_time_ns = ++ stats->total_time_ns[BLOCK_ACCT_ZONE_APPEND]; ++ ds->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ]; ++ ds->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH]; ++ ds->unmap_total_time_ns = stats->total_time_ns[BLOCK_ACCT_UNMAP]; ++ ++ ds->has_idle_time_ns = stats->last_access_time_ns > 0; ++ if (ds->has_idle_time_ns) { ++ ds->idle_time_ns = block_acct_idle_time_ns(stats); ++ } ++ ++ ds->account_invalid = stats->account_invalid; ++ ds->account_failed = stats->account_failed; ++ ++ while ((ts = block_acct_interval_next(stats, ts))) { ++ BlockDeviceTimedStats *dev_stats = g_malloc0(sizeof(*dev_stats)); ++ ++ TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ]; ++ TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE]; ++ TimedAverage *zap = &ts->latency[BLOCK_ACCT_ZONE_APPEND]; ++ TimedAverage *fl = &ts->latency[BLOCK_ACCT_FLUSH]; ++ ++ dev_stats->interval_length = ts->interval_length; ++ ++ dev_stats->min_rd_latency_ns = timed_average_min(rd); ++ dev_stats->max_rd_latency_ns = timed_average_max(rd); ++ dev_stats->avg_rd_latency_ns = timed_average_avg(rd); ++ ++ dev_stats->min_wr_latency_ns = timed_average_min(wr); ++ dev_stats->max_wr_latency_ns = timed_average_max(wr); ++ dev_stats->avg_wr_latency_ns = timed_average_avg(wr); ++ ++ dev_stats->min_zone_append_latency_ns = timed_average_min(zap); ++ dev_stats->max_zone_append_latency_ns = timed_average_max(zap); ++ dev_stats->avg_zone_append_latency_ns = timed_average_avg(zap); ++ ++ dev_stats->min_flush_latency_ns = timed_average_min(fl); ++ dev_stats->max_flush_latency_ns = timed_average_max(fl); ++ dev_stats->avg_flush_latency_ns = timed_average_avg(fl); ++ ++ dev_stats->avg_rd_queue_depth = ++ block_acct_queue_depth(ts, BLOCK_ACCT_READ); ++ dev_stats->avg_wr_queue_depth = ++ block_acct_queue_depth(ts, BLOCK_ACCT_WRITE); ++ dev_stats->avg_zone_append_queue_depth = ++ block_acct_queue_depth(ts, BLOCK_ACCT_ZONE_APPEND); ++ ++ QAPI_LIST_PREPEND(ds->timed_stats, dev_stats); ++ } ++ ++ hgram = stats->latency_histogram; ++ ds->rd_latency_histogram ++ = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_READ]); ++ ds->wr_latency_histogram ++ = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_WRITE]); ++ ds->zone_append_latency_histogram ++ = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_ZONE_APPEND]); ++ ds->flush_latency_histogram ++ = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_FLUSH]); ++} ++ ++static BlockStats * GRAPH_RDLOCK ++bdrv_query_bds_stats(BlockDriverState *bs, bool blk_level) ++{ ++ BdrvChild *parent_child; ++ BlockDriverState *filter_or_cow_bs; ++ BlockStats *s = NULL; ++ ++ s = g_malloc0(sizeof(*s)); ++ s->stats = g_malloc0(sizeof(*s->stats)); ++ ++ if (!bs) { ++ return s; ++ } ++ ++ /* Skip automatically inserted nodes that the user isn't aware of in ++ * a BlockBackend-level command. Stay at the exact node for a node-level ++ * command. */ ++ if (blk_level) { ++ bs = bdrv_skip_implicit_filters(bs); ++ } ++ ++ if (bdrv_get_node_name(bs)[0]) { ++ s->node_name = g_strdup(bdrv_get_node_name(bs)); ++ } ++ ++ s->stats->wr_highest_offset = stat64_get(&bs->wr_highest_offset); ++ ++ s->driver_specific = bdrv_get_specific_stats(bs); ++ ++ parent_child = bdrv_primary_child(bs); ++ if (!parent_child || ++ !(parent_child->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED))) ++ { ++ BdrvChild *c; ++ ++ /* ++ * Look for a unique data-storing child. We do not need to look for ++ * filtered children, as there would be only one and it would have been ++ * the primary child. ++ */ ++ parent_child = NULL; ++ QLIST_FOREACH(c, &bs->children, next) { ++ if (c->role & BDRV_CHILD_DATA) { ++ if (parent_child) { ++ /* ++ * There are multiple data-storing children and we cannot ++ * choose between them. ++ */ ++ parent_child = NULL; ++ break; ++ } ++ parent_child = c; ++ } ++ } ++ } ++ if (parent_child) { ++ s->parent = bdrv_query_bds_stats(parent_child->bs, blk_level); ++ } ++ ++ filter_or_cow_bs = bdrv_filter_or_cow_bs(bs); ++ if (blk_level && filter_or_cow_bs) { ++ /* ++ * Put any filtered or COW child here (for backwards ++ * compatibility to when we put bs0->backing here, which might ++ * be either) ++ */ ++ s->backing = bdrv_query_bds_stats(filter_or_cow_bs, blk_level); ++ } ++ ++ return s; ++} ++ ++BlockInfoList *qmp_query_block(Error **errp) ++{ ++ BlockInfoList *head = NULL, **p_next = &head; ++ BlockBackend *blk; ++ Error *local_err = NULL; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { ++ BlockInfoList *info; ++ ++ if (!*blk_name(blk) && !blk_get_attached_dev(blk)) { ++ continue; ++ } ++ ++ info = g_malloc0(sizeof(*info)); ++ bdrv_query_info(blk, &info->value, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ g_free(info); ++ qapi_free_BlockInfoList(head); ++ return NULL; ++ } ++ ++ *p_next = info; ++ p_next = &info->next; ++ } ++ ++ return head; ++} ++ ++BlockStatsList *qmp_query_blockstats(bool has_query_nodes, ++ bool query_nodes, ++ Error **errp) ++{ ++ BlockStatsList *head = NULL, **tail = &head; ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ /* Just to be safe if query_nodes is not always initialized */ ++ if (has_query_nodes && query_nodes) { ++ for (bs = bdrv_next_node(NULL); bs; bs = bdrv_next_node(bs)) { ++ QAPI_LIST_APPEND(tail, bdrv_query_bds_stats(bs, false)); ++ } ++ } else { ++ for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { ++ BlockStats *s; ++ char *qdev; ++ ++ if (!*blk_name(blk) && !blk_get_attached_dev(blk)) { ++ continue; ++ } ++ ++ s = bdrv_query_bds_stats(blk_bs(blk), true); ++ s->device = g_strdup(blk_name(blk)); ++ ++ qdev = blk_get_attached_dev_id(blk); ++ if (qdev && *qdev) { ++ s->qdev = qdev; ++ } else { ++ g_free(qdev); ++ } ++ ++ bdrv_query_blk_stats(s->stats, blk); ++ ++ QAPI_LIST_APPEND(tail, s); ++ } ++ } ++ ++ return head; ++} ++ ++void bdrv_snapshot_dump(QEMUSnapshotInfo *sn) ++{ ++ char clock_buf[128]; ++ char icount_buf[128] = {0}; ++ int64_t secs; ++ char *sizing = NULL; ++ ++ if (!sn) { ++ qemu_printf("%-7s %-16s %8s %19s %15s %10s", ++ "ID", "TAG", "VM_SIZE", "DATE", "VM_CLOCK", "ICOUNT"); ++ } else { ++ g_autoptr(GDateTime) date = g_date_time_new_from_unix_local(sn->date_sec); ++ g_autofree char *date_buf = g_date_time_format(date, "%Y-%m-%d %H:%M:%S"); ++ ++ secs = sn->vm_clock_nsec / 1000000000; ++ snprintf(clock_buf, sizeof(clock_buf), ++ "%04d:%02d:%02d.%03d", ++ (int)(secs / 3600), ++ (int)((secs / 60) % 60), ++ (int)(secs % 60), ++ (int)((sn->vm_clock_nsec / 1000000) % 1000)); ++ sizing = size_to_str(sn->vm_state_size); ++ if (sn->icount != -1ULL) { ++ snprintf(icount_buf, sizeof(icount_buf), ++ "%"PRId64, sn->icount); ++ } else { ++ snprintf(icount_buf, sizeof(icount_buf), "--"); ++ } ++ qemu_printf("%-7s %-16s %8s %19s %15s %10s", ++ sn->id_str, sn->name, ++ sizing, ++ date_buf, ++ clock_buf, ++ icount_buf); ++ } ++ g_free(sizing); ++} ++ ++static void dump_qdict(int indentation, QDict *dict); ++static void dump_qlist(int indentation, QList *list); ++ ++static void dump_qobject(int comp_indent, QObject *obj) ++{ ++ switch (qobject_type(obj)) { ++ case QTYPE_QNUM: { ++ QNum *value = qobject_to(QNum, obj); ++ char *tmp = qnum_to_string(value); ++ qemu_printf("%s", tmp); ++ g_free(tmp); ++ break; ++ } ++ case QTYPE_QSTRING: { ++ QString *value = qobject_to(QString, obj); ++ qemu_printf("%s", qstring_get_str(value)); ++ break; ++ } ++ case QTYPE_QDICT: { ++ QDict *value = qobject_to(QDict, obj); ++ dump_qdict(comp_indent, value); ++ break; ++ } ++ case QTYPE_QLIST: { ++ QList *value = qobject_to(QList, obj); ++ dump_qlist(comp_indent, value); ++ break; ++ } ++ case QTYPE_QBOOL: { ++ QBool *value = qobject_to(QBool, obj); ++ qemu_printf("%s", qbool_get_bool(value) ? "true" : "false"); ++ break; ++ } ++ default: ++ abort(); ++ } ++} ++ ++static void dump_qlist(int indentation, QList *list) ++{ ++ const QListEntry *entry; ++ int i = 0; ++ ++ for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) { ++ QType type = qobject_type(entry->value); ++ bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); ++ qemu_printf("%*s[%i]:%c", indentation * 4, "", i, ++ composite ? '\n' : ' '); ++ dump_qobject(indentation + 1, entry->value); ++ if (!composite) { ++ qemu_printf("\n"); ++ } ++ } ++} ++ ++static void dump_qdict(int indentation, QDict *dict) ++{ ++ const QDictEntry *entry; ++ ++ for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) { ++ QType type = qobject_type(entry->value); ++ bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); ++ char *key = g_malloc(strlen(entry->key) + 1); ++ int i; ++ ++ /* replace dashes with spaces in key (variable) names */ ++ for (i = 0; entry->key[i]; i++) { ++ key[i] = entry->key[i] == '-' ? ' ' : entry->key[i]; ++ } ++ key[i] = 0; ++ qemu_printf("%*s%s:%c", indentation * 4, "", key, ++ composite ? '\n' : ' '); ++ dump_qobject(indentation + 1, entry->value); ++ if (!composite) { ++ qemu_printf("\n"); ++ } ++ g_free(key); ++ } ++} ++ ++/* ++ * Return whether dumping the given QObject with dump_qobject() would ++ * yield an empty dump, i.e. not print anything. ++ */ ++static bool qobject_is_empty_dump(const QObject *obj) ++{ ++ switch (qobject_type(obj)) { ++ case QTYPE_QNUM: ++ case QTYPE_QSTRING: ++ case QTYPE_QBOOL: ++ return false; ++ ++ case QTYPE_QDICT: ++ return qdict_size(qobject_to(QDict, obj)) == 0; ++ ++ case QTYPE_QLIST: ++ return qlist_empty(qobject_to(QList, obj)); ++ ++ default: ++ abort(); ++ } ++} ++ ++/** ++ * Dumps the given ImageInfoSpecific object in a human-readable form, ++ * prepending an optional prefix if the dump is not empty. ++ */ ++void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec, ++ const char *prefix, ++ int indentation) ++{ ++ QObject *obj, *data; ++ Visitor *v = qobject_output_visitor_new(&obj); ++ ++ visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort); ++ visit_complete(v, &obj); ++ data = qdict_get(qobject_to(QDict, obj), "data"); ++ if (!qobject_is_empty_dump(data)) { ++ if (prefix) { ++ qemu_printf("%*s%s", indentation * 4, "", prefix); ++ } ++ dump_qobject(indentation + 1, data); ++ } ++ qobject_unref(obj); ++ visit_free(v); ++} ++ ++/** ++ * Print the given @info object in human-readable form. Every field is indented ++ * using the given @indentation (four spaces per indentation level). ++ * ++ * When using this to print a whole block graph, @protocol can be set to true to ++ * signify that the given information is associated with a protocol node, i.e. ++ * just data storage for an image, such that the data it presents is not really ++ * a full VM disk. If so, several fields change name: For example, "virtual ++ * size" is printed as "file length". ++ * (Consider a qcow2 image, which is represented by a qcow2 node and a file ++ * node. Printing a "virtual size" for the file node does not make sense, ++ * because without the qcow2 node, it is not really a guest disk, so it does not ++ * have a "virtual size". Therefore, we call it "file length" instead.) ++ * ++ * @protocol is ignored when @indentation is 0, because we take that to mean ++ * that the associated node is the root node in the queried block graph, and ++ * thus is always to be interpreted as a standalone guest disk. ++ */ ++void bdrv_node_info_dump(BlockNodeInfo *info, int indentation, bool protocol) ++{ ++ char *size_buf, *dsize_buf; ++ g_autofree char *ind_s = g_strdup_printf("%*s", indentation * 4, ""); ++ ++ if (indentation == 0) { ++ /* Top level, consider this a normal image */ ++ protocol = false; ++ } ++ ++ if (!info->has_actual_size) { ++ dsize_buf = g_strdup("unavailable"); ++ } else { ++ dsize_buf = size_to_str(info->actual_size); ++ } ++ size_buf = size_to_str(info->virtual_size); ++ qemu_printf("%s%s: %s\n" ++ "%s%s: %s\n" ++ "%s%s: %s (%" PRId64 " bytes)\n" ++ "%sdisk size: %s\n", ++ ind_s, protocol ? "filename" : "image", info->filename, ++ ind_s, protocol ? "protocol type" : "file format", ++ info->format, ++ ind_s, protocol ? "file length" : "virtual size", ++ size_buf, info->virtual_size, ++ ind_s, dsize_buf); ++ g_free(size_buf); ++ g_free(dsize_buf); ++ ++ if (info->has_encrypted && info->encrypted) { ++ qemu_printf("%sencrypted: yes\n", ind_s); ++ } ++ ++ if (info->has_cluster_size) { ++ qemu_printf("%scluster_size: %" PRId64 "\n", ++ ind_s, info->cluster_size); ++ } ++ ++ if (info->has_dirty_flag && info->dirty_flag) { ++ qemu_printf("%scleanly shut down: no\n", ind_s); ++ } ++ ++ if (info->backing_filename) { ++ qemu_printf("%sbacking file: %s", ind_s, info->backing_filename); ++ if (!info->full_backing_filename) { ++ qemu_printf(" (cannot determine actual path)"); ++ } else if (strcmp(info->backing_filename, ++ info->full_backing_filename) != 0) { ++ qemu_printf(" (actual path: %s)", info->full_backing_filename); ++ } ++ qemu_printf("\n"); ++ if (info->backing_filename_format) { ++ qemu_printf("%sbacking file format: %s\n", ++ ind_s, info->backing_filename_format); ++ } ++ } ++ ++ if (info->has_snapshots) { ++ SnapshotInfoList *elem; ++ ++ qemu_printf("%sSnapshot list:\n", ind_s); ++ qemu_printf("%s", ind_s); ++ bdrv_snapshot_dump(NULL); ++ qemu_printf("\n"); ++ ++ /* Ideally bdrv_snapshot_dump() would operate on SnapshotInfoList but ++ * we convert to the block layer's native QEMUSnapshotInfo for now. ++ */ ++ for (elem = info->snapshots; elem; elem = elem->next) { ++ QEMUSnapshotInfo sn = { ++ .vm_state_size = elem->value->vm_state_size, ++ .date_sec = elem->value->date_sec, ++ .date_nsec = elem->value->date_nsec, ++ .vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL + ++ elem->value->vm_clock_nsec, ++ .icount = elem->value->has_icount ? ++ elem->value->icount : -1ULL, ++ }; ++ ++ pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id); ++ pstrcpy(sn.name, sizeof(sn.name), elem->value->name); ++ qemu_printf("%s", ind_s); ++ bdrv_snapshot_dump(&sn); ++ qemu_printf("\n"); ++ } ++ } ++ ++ if (info->format_specific) { ++ bdrv_image_info_specific_dump(info->format_specific, ++ "Format specific information:\n", ++ indentation); ++ } ++} +diff --git a/qcow2/lib/block/raw-format.c b/qcow2/lib/block/raw-format.c +new file mode 100644 +index 00000000..ac7e8495 +--- /dev/null ++++ b/qcow2/lib/block/raw-format.c +@@ -0,0 +1,682 @@ ++/* BlockDriver implementation for "raw" format driver ++ * ++ * Copyright (C) 2010-2016 Red Hat, Inc. ++ * Copyright (C) 2010, Blue Swirl ++ * Copyright (C) 2009, Anthony Liguori ++ * ++ * Author: ++ * Laszlo Ersek ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/block-io.h" ++#include "block/block_int.h" ++#include "qapi/error.h" ++#include "qemu/module.h" ++#include "qemu/option.h" ++#include "qemu/memalign.h" ++ ++typedef struct BDRVRawState { ++ uint64_t offset; ++ uint64_t size; ++ bool has_size; ++} BDRVRawState; ++ ++static const char *const mutable_opts[] = { "offset", "size", NULL }; ++ ++static QemuOptsList raw_runtime_opts = { ++ .name = "raw", ++ .head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head), ++ .desc = { ++ { ++ .name = "offset", ++ .type = QEMU_OPT_SIZE, ++ .help = "offset in the disk where the image starts", ++ }, ++ { ++ .name = "size", ++ .type = QEMU_OPT_SIZE, ++ .help = "virtual disk size", ++ }, ++ { /* end of list */ } ++ }, ++}; ++ ++static QemuOptsList raw_create_opts = { ++ .name = "raw-create-opts", ++ .head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head), ++ .desc = { ++ { ++ .name = BLOCK_OPT_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Virtual disk size" ++ }, ++ { /* end of list */ } ++ } ++}; ++ ++static int raw_read_options(QDict *options, uint64_t *offset, bool *has_size, ++ uint64_t *size, Error **errp) ++{ ++ QemuOpts *opts = NULL; ++ int ret; ++ ++ opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_absorb_qdict(opts, options, errp)) { ++ ret = -EINVAL; ++ goto end; ++ } ++ ++ *offset = qemu_opt_get_size(opts, "offset", 0); ++ *has_size = qemu_opt_find(opts, "size"); ++ *size = qemu_opt_get_size(opts, "size", 0); ++ ++ ret = 0; ++end: ++ qemu_opts_del(opts); ++ return ret; ++} ++ ++static int GRAPH_RDLOCK ++raw_apply_options(BlockDriverState *bs, BDRVRawState *s, uint64_t offset, ++ bool has_size, uint64_t size, Error **errp) ++{ ++ int64_t real_size = 0; ++ ++ real_size = bdrv_getlength(bs->file->bs); ++ if (real_size < 0) { ++ error_setg_errno(errp, -real_size, "Could not get image size"); ++ return real_size; ++ } ++ ++ /* Check size and offset */ ++ if (offset > real_size) { ++ error_setg(errp, "Offset (%" PRIu64 ") cannot be greater than " ++ "size of the containing file (%" PRId64 ")", ++ s->offset, real_size); ++ return -EINVAL; ++ } ++ ++ if (has_size && (real_size - offset) < size) { ++ error_setg(errp, "The sum of offset (%" PRIu64 ") and size " ++ "(%" PRIu64 ") has to be smaller or equal to the " ++ " actual size of the containing file (%" PRId64 ")", ++ s->offset, s->size, real_size); ++ return -EINVAL; ++ } ++ ++ /* Make sure size is multiple of BDRV_SECTOR_SIZE to prevent rounding ++ * up and leaking out of the specified area. */ ++ if (has_size && !QEMU_IS_ALIGNED(size, BDRV_SECTOR_SIZE)) { ++ error_setg(errp, "Specified size is not multiple of %llu", ++ BDRV_SECTOR_SIZE); ++ return -EINVAL; ++ } ++ ++ s->offset = offset; ++ s->has_size = has_size; ++ s->size = has_size ? size : real_size - offset; ++ ++ return 0; ++} ++ ++static int raw_reopen_prepare(BDRVReopenState *reopen_state, ++ BlockReopenQueue *queue, Error **errp) ++{ ++ bool has_size; ++ uint64_t offset, size; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ assert(reopen_state != NULL); ++ assert(reopen_state->bs != NULL); ++ ++ reopen_state->opaque = g_new0(BDRVRawState, 1); ++ ++ ret = raw_read_options(reopen_state->options, &offset, &has_size, &size, ++ errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ ret = raw_apply_options(reopen_state->bs, reopen_state->opaque, ++ offset, has_size, size, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void raw_reopen_commit(BDRVReopenState *state) ++{ ++ BDRVRawState *new_s = state->opaque; ++ BDRVRawState *s = state->bs->opaque; ++ ++ memcpy(s, new_s, sizeof(BDRVRawState)); ++ ++ g_free(state->opaque); ++ state->opaque = NULL; ++} ++ ++static void raw_reopen_abort(BDRVReopenState *state) ++{ ++ g_free(state->opaque); ++ state->opaque = NULL; ++} ++ ++/* Check and adjust the offset, against 'offset' and 'size' options. */ ++static inline int raw_adjust_offset(BlockDriverState *bs, int64_t *offset, ++ int64_t bytes, bool is_write) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (s->has_size && (*offset > s->size || bytes > (s->size - *offset))) { ++ /* There's not enough space for the write, or the read request is ++ * out-of-range. Don't read/write anything to prevent leaking out of ++ * the size specified in options. */ ++ return is_write ? -ENOSPC : -EINVAL; ++ } ++ ++ if (*offset > INT64_MAX - s->offset) { ++ return -EINVAL; ++ } ++ *offset += s->offset; ++ ++ return 0; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ int ret; ++ ++ ret = raw_adjust_offset(bs, &offset, bytes, false); ++ if (ret) { ++ return ret; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_AIO); ++ return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ void *buf = NULL; ++ BlockDriver *drv; ++ QEMUIOVector local_qiov; ++ int ret; ++ ++ if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) { ++ /* Handling partial writes would be a pain - so we just ++ * require that guests have 512-byte request alignment if ++ * probing occurred */ ++ QEMU_BUILD_BUG_ON(BLOCK_PROBE_BUF_SIZE != 512); ++ QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != 512); ++ assert(offset == 0 && bytes >= BLOCK_PROBE_BUF_SIZE); ++ ++ buf = qemu_try_blockalign(bs->file->bs, 512); ++ if (!buf) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ ret = qemu_iovec_to_buf(qiov, 0, buf, 512); ++ if (ret != 512) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ drv = bdrv_probe_all(buf, 512, NULL); ++ if (drv != bs->drv) { ++ ret = -EPERM; ++ goto fail; ++ } ++ ++ /* Use the checked buffer, a malicious guest might be overwriting its ++ * original buffer in the background. */ ++ qemu_iovec_init(&local_qiov, qiov->niov + 1); ++ qemu_iovec_add(&local_qiov, buf, 512); ++ qemu_iovec_concat(&local_qiov, qiov, 512, qiov->size - 512); ++ qiov = &local_qiov; ++ ++ flags &= ~BDRV_REQ_REGISTERED_BUF; ++ } ++ ++ ret = raw_adjust_offset(bs, &offset, bytes, true); ++ if (ret) { ++ goto fail; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); ++ ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); ++ ++fail: ++ if (qiov == &local_qiov) { ++ qemu_iovec_destroy(&local_qiov); ++ } ++ qemu_vfree(buf); ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, ++ int64_t bytes, int64_t *pnum, int64_t *map, ++ BlockDriverState **file) ++{ ++ BDRVRawState *s = bs->opaque; ++ *pnum = bytes; ++ *file = bs->file->bs; ++ *map = offset + s->offset; ++ return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ ++ ret = raw_adjust_offset(bs, &offset, bytes, true); ++ if (ret) { ++ return ret; ++ } ++ return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ int ret; ++ ++ ret = raw_adjust_offset(bs, &offset, bytes, true); ++ if (ret) { ++ return ret; ++ } ++ return bdrv_co_pdiscard(bs->file, offset, bytes); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_zone_report(BlockDriverState *bs, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones) ++{ ++ return bdrv_co_zone_report(bs->file->bs, offset, nr_zones, zones); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, ++ int64_t offset, int64_t len) ++{ ++ return bdrv_co_zone_mgmt(bs->file->bs, op, offset, len); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_zone_append(BlockDriverState *bs,int64_t *offset, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ return bdrv_co_zone_append(bs->file->bs, offset, qiov, flags); ++} ++ ++static int64_t coroutine_fn GRAPH_RDLOCK ++raw_co_getlength(BlockDriverState *bs) ++{ ++ int64_t len; ++ BDRVRawState *s = bs->opaque; ++ ++ /* Update size. It should not change unless the file was externally ++ * modified. */ ++ len = bdrv_co_getlength(bs->file->bs); ++ if (len < 0) { ++ return len; ++ } ++ ++ if (len < s->offset) { ++ s->size = 0; ++ } else { ++ if (s->has_size) { ++ /* Try to honour the size */ ++ s->size = MIN(s->size, len - s->offset); ++ } else { ++ s->size = len - s->offset; ++ } ++ } ++ ++ return s->size; ++} ++ ++static BlockMeasureInfo *raw_measure(QemuOpts *opts, BlockDriverState *in_bs, ++ Error **errp) ++{ ++ BlockMeasureInfo *info; ++ int64_t required; ++ ++ if (in_bs) { ++ required = bdrv_getlength(in_bs); ++ if (required < 0) { ++ error_setg_errno(errp, -required, "Unable to get image size"); ++ return NULL; ++ } ++ } else { ++ required = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), ++ BDRV_SECTOR_SIZE); ++ } ++ ++ info = g_new0(BlockMeasureInfo, 1); ++ info->required = required; ++ ++ /* Unallocated sectors count towards the file size in raw images */ ++ info->fully_allocated = info->required; ++ return info; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) ++{ ++ return bdrv_co_get_info(bs->file->bs, bdi); ++} ++ ++static void GRAPH_RDLOCK raw_refresh_limits(BlockDriverState *bs, Error **errp) ++{ ++ bs->bl.has_variable_length = bs->file->bs->bl.has_variable_length; ++ ++ if (bs->probed) { ++ /* To make it easier to protect the first sector, any probed ++ * image is restricted to read-modify-write on sub-sector ++ * operations. */ ++ bs->bl.request_alignment = BDRV_SECTOR_SIZE; ++ } ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (s->has_size) { ++ error_setg(errp, "Cannot resize fixed-size raw disks"); ++ return -ENOTSUP; ++ } ++ ++ if (INT64_MAX - offset < s->offset) { ++ error_setg(errp, "Disk size too large for the chosen offset"); ++ return -EINVAL; ++ } ++ ++ s->size = offset; ++ offset += s->offset; ++ return bdrv_co_truncate(bs->file, offset, exact, prealloc, flags, errp); ++} ++ ++static void coroutine_fn GRAPH_RDLOCK ++raw_co_eject(BlockDriverState *bs, bool eject_flag) ++{ ++ bdrv_co_eject(bs->file->bs, eject_flag); ++} ++ ++static void coroutine_fn GRAPH_RDLOCK ++raw_co_lock_medium(BlockDriverState *bs, bool locked) ++{ ++ bdrv_co_lock_medium(bs->file->bs, locked); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) ++{ ++ BDRVRawState *s = bs->opaque; ++ if (s->offset || s->has_size) { ++ return -ENOTSUP; ++ } ++ return bdrv_co_ioctl(bs->file->bs, req, buf); ++} ++ ++static int GRAPH_RDLOCK raw_has_zero_init(BlockDriverState *bs) ++{ ++ return bdrv_has_zero_init(bs->file->bs); ++} ++ ++static int coroutine_fn GRAPH_UNLOCKED ++raw_co_create_opts(BlockDriver *drv, const char *filename, ++ QemuOpts *opts, Error **errp) ++{ ++ return bdrv_co_create_file(filename, opts, errp); ++} ++ ++static int raw_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ bool has_size; ++ uint64_t offset, size; ++ BdrvChildRole file_role; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ ret = raw_read_options(options, &offset, &has_size, &size, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* ++ * Without offset and a size limit, this driver behaves very much ++ * like a filter. With any such limit, it does not. ++ */ ++ if (offset || has_size) { ++ file_role = BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY; ++ } else { ++ file_role = BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY; ++ } ++ ++ bdrv_open_child(NULL, options, "file", bs, &child_of_bds, ++ file_role, false, errp); ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ if (!bs->file) { ++ return -EINVAL; ++ } ++ ++ bs->sg = bdrv_is_sg(bs->file->bs); ++ bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | ++ (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); ++ bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | ++ ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & ++ bs->file->bs->supported_zero_flags); ++ bs->supported_truncate_flags = bs->file->bs->supported_truncate_flags & ++ BDRV_REQ_ZERO_WRITE; ++ ++ if (bs->probed && !bdrv_is_read_only(bs)) { ++ bdrv_refresh_filename(bs->file->bs); ++ fprintf(stderr, ++ "WARNING: Image format was not specified for '%s' and probing " ++ "guessed raw.\n" ++ " Automatically detecting the format is dangerous for " ++ "raw images, write operations on block 0 will be restricted.\n" ++ " Specify the 'raw' format explicitly to remove the " ++ "restrictions.\n", ++ bs->file->bs->filename); ++ } ++ ++ ret = raw_apply_options(bs, s, offset, has_size, size, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (bdrv_is_sg(bs) && (s->offset || s->has_size)) { ++ error_setg(errp, "Cannot use offset/size with SCSI generic devices"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) ++{ ++ /* smallest possible positive score so that raw is used if and only if no ++ * other block driver works ++ */ ++ return 1; ++} ++ ++static int GRAPH_RDLOCK ++raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++ ret = bdrv_probe_blocksizes(bs->file->bs, bsz); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (!QEMU_IS_ALIGNED(s->offset, MAX(bsz->log, bsz->phys))) { ++ return -ENOTSUP; ++ } ++ ++ return 0; ++} ++ ++static int GRAPH_RDLOCK ++raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) ++{ ++ BDRVRawState *s = bs->opaque; ++ if (s->offset || s->has_size) { ++ return -ENOTSUP; ++ } ++ return bdrv_probe_geometry(bs->file->bs, geo); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_copy_range_from(BlockDriverState *bs, ++ BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ int ret; ++ ++ ret = raw_adjust_offset(bs, &src_offset, bytes, false); ++ if (ret) { ++ return ret; ++ } ++ return bdrv_co_copy_range_from(bs->file, src_offset, dst, dst_offset, ++ bytes, read_flags, write_flags); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_copy_range_to(BlockDriverState *bs, ++ BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ int ret; ++ ++ ret = raw_adjust_offset(bs, &dst_offset, bytes, true); ++ if (ret) { ++ return ret; ++ } ++ return bdrv_co_copy_range_to(src, src_offset, bs->file, dst_offset, bytes, ++ read_flags, write_flags); ++} ++ ++static const char *const raw_strong_runtime_opts[] = { ++ "offset", ++ "size", ++ ++ NULL ++}; ++ ++static void GRAPH_RDLOCK raw_cancel_in_flight(BlockDriverState *bs) ++{ ++ bdrv_cancel_in_flight(bs->file->bs); ++} ++ ++static void raw_child_perm(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t parent_perm, uint64_t parent_shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ bdrv_default_perms(bs, c, role, reopen_queue, parent_perm, ++ parent_shared, nperm, nshared); ++ ++ /* ++ * bdrv_default_perms() may add WRITE and/or RESIZE (see comment in ++ * bdrv_default_perms_for_storage() for an explanation) but we only need ++ * them if they are in parent_perm. Drop WRITE and RESIZE whenever possible ++ * to avoid permission conflicts. ++ */ ++ *nperm &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); ++ *nperm |= parent_perm & (BLK_PERM_WRITE | BLK_PERM_RESIZE); ++} ++ ++BlockDriver bdrv_raw = { ++ .format_name = "raw", ++ .instance_size = sizeof(BDRVRawState), ++ .supports_zoned_children = true, ++ .bdrv_probe = &raw_probe, ++ .bdrv_reopen_prepare = &raw_reopen_prepare, ++ .bdrv_reopen_commit = &raw_reopen_commit, ++ .bdrv_reopen_abort = &raw_reopen_abort, ++ .bdrv_open = &raw_open, ++ .bdrv_child_perm = raw_child_perm, ++ .bdrv_co_create_opts = &raw_co_create_opts, ++ .bdrv_co_preadv = &raw_co_preadv, ++ .bdrv_co_pwritev = &raw_co_pwritev, ++ .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes, ++ .bdrv_co_pdiscard = &raw_co_pdiscard, ++ .bdrv_co_zone_report = &raw_co_zone_report, ++ .bdrv_co_zone_mgmt = &raw_co_zone_mgmt, ++ .bdrv_co_zone_append = &raw_co_zone_append, ++ .bdrv_co_block_status = &raw_co_block_status, ++ .bdrv_co_copy_range_from = &raw_co_copy_range_from, ++ .bdrv_co_copy_range_to = &raw_co_copy_range_to, ++ .bdrv_co_truncate = &raw_co_truncate, ++ .bdrv_co_getlength = &raw_co_getlength, ++ .is_format = true, ++ .bdrv_measure = &raw_measure, ++ .bdrv_co_get_info = &raw_co_get_info, ++ .bdrv_refresh_limits = &raw_refresh_limits, ++ .bdrv_probe_blocksizes = &raw_probe_blocksizes, ++ .bdrv_probe_geometry = &raw_probe_geometry, ++ .bdrv_co_eject = &raw_co_eject, ++ .bdrv_co_lock_medium = &raw_co_lock_medium, ++ .bdrv_co_ioctl = &raw_co_ioctl, ++ .create_opts = &raw_create_opts, ++ .bdrv_has_zero_init = &raw_has_zero_init, ++ .strong_runtime_opts = raw_strong_runtime_opts, ++ .mutable_opts = mutable_opts, ++ .bdrv_cancel_in_flight = raw_cancel_in_flight, ++}; ++ ++static void bdrv_raw_init(void) ++{ ++ bdrv_register(&bdrv_raw); ++} ++ ++block_init(bdrv_raw_init); +diff --git a/qcow2/lib/block/snapshot.c b/qcow2/lib/block/snapshot.c +new file mode 100644 +index 00000000..8fd17567 +--- /dev/null ++++ b/qcow2/lib/block/snapshot.c +@@ -0,0 +1,780 @@ ++/* ++ * Block layer snapshot related functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/snapshot.h" ++#include "block/block_int.h" ++#include "block/qdict.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qstring.h" ++#include "qemu/option.h" ++#include "sysemu/block-backend.h" ++ ++QemuOptsList internal_snapshot_opts = { ++ .name = "snapshot", ++ .head = QTAILQ_HEAD_INITIALIZER(internal_snapshot_opts.head), ++ .desc = { ++ { ++ .name = SNAPSHOT_OPT_ID, ++ .type = QEMU_OPT_STRING, ++ .help = "snapshot id" ++ },{ ++ .name = SNAPSHOT_OPT_NAME, ++ .type = QEMU_OPT_STRING, ++ .help = "snapshot name" ++ },{ ++ /* end of list */ ++ } ++ }, ++}; ++ ++int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, ++ const char *name) ++{ ++ QEMUSnapshotInfo *sn_tab, *sn; ++ int nb_sns, i, ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ ret = -ENOENT; ++ nb_sns = bdrv_snapshot_list(bs, &sn_tab); ++ if (nb_sns < 0) { ++ return ret; ++ } ++ for (i = 0; i < nb_sns; i++) { ++ sn = &sn_tab[i]; ++ if (!strcmp(sn->name, name)) { ++ *sn_info = *sn; ++ ret = 0; ++ break; ++ } ++ } ++ g_free(sn_tab); ++ return ret; ++} ++ ++/** ++ * Look up an internal snapshot by @id and @name. ++ * @bs: block device to search ++ * @id: unique snapshot ID, or NULL ++ * @name: snapshot name, or NULL ++ * @sn_info: location to store information on the snapshot found ++ * @errp: location to store error, will be set only for exception ++ * ++ * This function will traverse snapshot list in @bs to search the matching ++ * one, @id and @name are the matching condition: ++ * If both @id and @name are specified, find the first one with id @id and ++ * name @name. ++ * If only @id is specified, find the first one with id @id. ++ * If only @name is specified, find the first one with name @name. ++ * if none is specified, abort(). ++ * ++ * Returns: true when a snapshot is found and @sn_info will be filled, false ++ * when error or not found. If all operation succeed but no matching one is ++ * found, @errp will NOT be set. ++ */ ++bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, ++ const char *id, ++ const char *name, ++ QEMUSnapshotInfo *sn_info, ++ Error **errp) ++{ ++ QEMUSnapshotInfo *sn_tab, *sn; ++ int nb_sns, i; ++ bool ret = false; ++ ++ assert(id || name); ++ GLOBAL_STATE_CODE(); ++ ++ nb_sns = bdrv_snapshot_list(bs, &sn_tab); ++ if (nb_sns < 0) { ++ error_setg_errno(errp, -nb_sns, "Failed to get a snapshot list"); ++ return false; ++ } else if (nb_sns == 0) { ++ return false; ++ } ++ ++ if (id && name) { ++ for (i = 0; i < nb_sns; i++) { ++ sn = &sn_tab[i]; ++ if (!strcmp(sn->id_str, id) && !strcmp(sn->name, name)) { ++ *sn_info = *sn; ++ ret = true; ++ break; ++ } ++ } ++ } else if (id) { ++ for (i = 0; i < nb_sns; i++) { ++ sn = &sn_tab[i]; ++ if (!strcmp(sn->id_str, id)) { ++ *sn_info = *sn; ++ ret = true; ++ break; ++ } ++ } ++ } else if (name) { ++ for (i = 0; i < nb_sns; i++) { ++ sn = &sn_tab[i]; ++ if (!strcmp(sn->name, name)) { ++ *sn_info = *sn; ++ ret = true; ++ break; ++ } ++ } ++ } ++ ++ g_free(sn_tab); ++ return ret; ++} ++ ++/** ++ * Return a pointer to child of given BDS to which we can fall ++ * back if the given BDS does not support snapshots. ++ * Return NULL if there is no BDS to (safely) fall back to. ++ */ ++static BdrvChild * GRAPH_RDLOCK ++bdrv_snapshot_fallback_child(BlockDriverState *bs) ++{ ++ BdrvChild *fallback = bdrv_primary_child(bs); ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ /* We allow fallback only to primary child */ ++ if (!fallback) { ++ return NULL; ++ } ++ ++ /* ++ * Check that there are no other children that would need to be ++ * snapshotted. If there are, it is not safe to fall back to ++ * fallback. ++ */ ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (child->role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA | ++ BDRV_CHILD_FILTERED) && ++ child != fallback) ++ { ++ return NULL; ++ } ++ } ++ ++ return fallback; ++} ++ ++static BlockDriverState * GRAPH_RDLOCK ++bdrv_snapshot_fallback(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ return child_bs(bdrv_snapshot_fallback_child(bs)); ++} ++ ++int bdrv_can_snapshot(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv || !bdrv_is_inserted(bs) || !bdrv_is_writable(bs)) { ++ return 0; ++ } ++ ++ if (!drv->bdrv_snapshot_create) { ++ BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); ++ if (fallback_bs) { ++ return bdrv_can_snapshot(fallback_bs); ++ } ++ return 0; ++ } ++ ++ return 1; ++} ++ ++int bdrv_snapshot_create(BlockDriverState *bs, ++ QEMUSnapshotInfo *sn_info) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ if (drv->bdrv_snapshot_create) { ++ return drv->bdrv_snapshot_create(bs, sn_info); ++ } ++ if (fallback_bs) { ++ return bdrv_snapshot_create(fallback_bs, sn_info); ++ } ++ return -ENOTSUP; ++} ++ ++int bdrv_snapshot_goto(BlockDriverState *bs, ++ const char *snapshot_id, ++ Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ BdrvChild *fallback; ++ int ret, open_ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv) { ++ error_setg(errp, "Block driver is closed"); ++ return -ENOMEDIUM; ++ } ++ ++ if (!QLIST_EMPTY(&bs->dirty_bitmaps)) { ++ error_setg(errp, "Device has active dirty bitmaps"); ++ return -EBUSY; ++ } ++ ++ if (drv->bdrv_snapshot_goto) { ++ ret = drv->bdrv_snapshot_goto(bs, snapshot_id); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to load snapshot"); ++ } ++ return ret; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ fallback = bdrv_snapshot_fallback_child(bs); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (fallback) { ++ QDict *options; ++ QDict *file_options; ++ Error *local_err = NULL; ++ BlockDriverState *fallback_bs = fallback->bs; ++ char *subqdict_prefix = g_strdup_printf("%s.", fallback->name); ++ ++ options = qdict_clone_shallow(bs->options); ++ ++ /* Prevent it from getting deleted when detached from bs */ ++ bdrv_ref(fallback_bs); ++ ++ qdict_extract_subqdict(options, &file_options, subqdict_prefix); ++ qobject_unref(file_options); ++ g_free(subqdict_prefix); ++ ++ /* Force .bdrv_open() below to re-attach fallback_bs on fallback */ ++ qdict_put_str(options, fallback->name, ++ bdrv_get_node_name(fallback_bs)); ++ ++ /* Now close bs, apply the snapshot on fallback_bs, and re-open bs */ ++ if (drv->bdrv_close) { ++ drv->bdrv_close(bs); ++ } ++ ++ /* .bdrv_open() will re-attach it */ ++ bdrv_graph_wrlock(); ++ bdrv_unref_child(bs, fallback); ++ bdrv_graph_wrunlock(); ++ ++ ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp); ++ open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); ++ qobject_unref(options); ++ if (open_ret < 0) { ++ bdrv_unref(fallback_bs); ++ bs->drv = NULL; ++ /* A bdrv_snapshot_goto() error takes precedence */ ++ error_propagate(errp, local_err); ++ return ret < 0 ? ret : open_ret; ++ } ++ ++ /* ++ * fallback was a primary child. It was closed above and set to NULL, ++ * but the .bdrv_open() call has opened it again, because we set the ++ * respective option (with the qdict_put_str() call above). ++ * Assert that .bdrv_open() has attached the right BDS as primary child. ++ */ ++ bdrv_graph_rdlock_main_loop(); ++ assert(bdrv_primary_bs(bs) == fallback_bs); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_unref(fallback_bs); ++ return ret; ++ } ++ ++ error_setg(errp, "Block driver does not support snapshots"); ++ return -ENOTSUP; ++} ++ ++/** ++ * Delete an internal snapshot by @snapshot_id and @name. ++ * @bs: block device used in the operation ++ * @snapshot_id: unique snapshot ID, or NULL ++ * @name: snapshot name, or NULL ++ * @errp: location to store error ++ * ++ * If both @snapshot_id and @name are specified, delete the first one with ++ * id @snapshot_id and name @name. ++ * If only @snapshot_id is specified, delete the first one with id ++ * @snapshot_id. ++ * If only @name is specified, delete the first one with name @name. ++ * if none is specified, return -EINVAL. ++ * ++ * Returns: 0 on success, -errno on failure. If @bs is not inserted, return ++ * -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs ++ * does not support internal snapshot deletion, return -ENOTSUP. If @bs does ++ * not support parameter @snapshot_id or @name, or one of them is not correctly ++ * specified, return -EINVAL. If @bs can't find one matching @id and @name, ++ * return -ENOENT. If @errp != NULL, it will always be filled with error ++ * message on failure. ++ */ ++int bdrv_snapshot_delete(BlockDriverState *bs, ++ const char *snapshot_id, ++ const char *name, ++ Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv) { ++ error_setg(errp, "Device '%s' has no medium", ++ bdrv_get_device_name(bs)); ++ return -ENOMEDIUM; ++ } ++ if (!snapshot_id && !name) { ++ error_setg(errp, "snapshot_id and name are both NULL"); ++ return -EINVAL; ++ } ++ ++ /* drain all pending i/o before deleting snapshot */ ++ bdrv_drained_begin(bs); ++ ++ if (drv->bdrv_snapshot_delete) { ++ ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); ++ } else if (fallback_bs) { ++ ret = bdrv_snapshot_delete(fallback_bs, snapshot_id, name, errp); ++ } else { ++ error_setg(errp, "Block format '%s' used by device '%s' " ++ "does not support internal snapshot deletion", ++ drv->format_name, bdrv_get_device_name(bs)); ++ ret = -ENOTSUP; ++ } ++ ++ bdrv_drained_end(bs); ++ return ret; ++} ++ ++int bdrv_snapshot_list(BlockDriverState *bs, ++ QEMUSnapshotInfo **psn_info) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ if (drv->bdrv_snapshot_list) { ++ return drv->bdrv_snapshot_list(bs, psn_info); ++ } ++ if (fallback_bs) { ++ return bdrv_snapshot_list(fallback_bs, psn_info); ++ } ++ return -ENOTSUP; ++} ++ ++/** ++ * Temporarily load an internal snapshot by @snapshot_id and @name. ++ * @bs: block device used in the operation ++ * @snapshot_id: unique snapshot ID, or NULL ++ * @name: snapshot name, or NULL ++ * @errp: location to store error ++ * ++ * If both @snapshot_id and @name are specified, load the first one with ++ * id @snapshot_id and name @name. ++ * If only @snapshot_id is specified, load the first one with id ++ * @snapshot_id. ++ * If only @name is specified, load the first one with name @name. ++ * if none is specified, return -EINVAL. ++ * ++ * Returns: 0 on success, -errno on fail. If @bs is not inserted, return ++ * -ENOMEDIUM. If @bs is not readonly, return -EINVAL. If @bs did not support ++ * internal snapshot, return -ENOTSUP. If qemu can't find a matching @id and ++ * @name, return -ENOENT. If @errp != NULL, it will always be filled on ++ * failure. ++ */ ++int bdrv_snapshot_load_tmp(BlockDriverState *bs, ++ const char *snapshot_id, ++ const char *name, ++ Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!drv) { ++ error_setg(errp, "Device '%s' has no medium", ++ bdrv_get_device_name(bs)); ++ return -ENOMEDIUM; ++ } ++ if (!snapshot_id && !name) { ++ error_setg(errp, "snapshot_id and name are both NULL"); ++ return -EINVAL; ++ } ++ if (!bdrv_is_read_only(bs)) { ++ error_setg(errp, "Device is not readonly"); ++ return -EINVAL; ++ } ++ if (drv->bdrv_snapshot_load_tmp) { ++ return drv->bdrv_snapshot_load_tmp(bs, snapshot_id, name, errp); ++ } ++ error_setg(errp, "Block format '%s' used by device '%s' " ++ "does not support temporarily loading internal snapshots", ++ drv->format_name, bdrv_get_device_name(bs)); ++ return -ENOTSUP; ++} ++ ++int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, ++ const char *id_or_name, ++ Error **errp) ++{ ++ int ret; ++ Error *local_err = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ ret = bdrv_snapshot_load_tmp(bs, id_or_name, NULL, &local_err); ++ if (ret == -ENOENT || ret == -EINVAL) { ++ error_free(local_err); ++ local_err = NULL; ++ ret = bdrv_snapshot_load_tmp(bs, NULL, id_or_name, &local_err); ++ } ++ ++ error_propagate(errp, local_err); ++ ++ return ret; ++} ++ ++ ++static int GRAPH_RDLOCK ++bdrv_all_get_snapshot_devices(bool has_devices, strList *devices, ++ GList **all_bdrvs, Error **errp) ++{ ++ g_autoptr(GList) bdrvs = NULL; ++ ++ if (has_devices) { ++ if (!devices) { ++ error_setg(errp, "At least one device is required for snapshot"); ++ return -1; ++ } ++ ++ while (devices) { ++ BlockDriverState *bs = bdrv_find_node(devices->value); ++ if (!bs) { ++ error_setg(errp, "No block device node '%s'", devices->value); ++ return -1; ++ } ++ bdrvs = g_list_append(bdrvs, bs); ++ devices = devices->next; ++ } ++ } else { ++ BlockDriverState *bs; ++ BdrvNextIterator it; ++ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { ++ bdrvs = g_list_append(bdrvs, bs); ++ } ++ } ++ ++ *all_bdrvs = g_steal_pointer(&bdrvs); ++ return 0; ++} ++ ++ ++static bool GRAPH_RDLOCK bdrv_all_snapshots_includes_bs(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { ++ return false; ++ } ++ ++ /* Include all nodes that are either in use by a BlockBackend, or that ++ * aren't attached to any node, but owned by the monitor. */ ++ return bdrv_has_blk(bs) || QLIST_EMPTY(&bs->parents); ++} ++ ++/* Group operations. All block drivers are involved. */ ++ ++bool bdrv_all_can_snapshot(bool has_devices, strList *devices, ++ Error **errp) ++{ ++ g_autoptr(GList) bdrvs = NULL; ++ GList *iterbdrvs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { ++ return false; ++ } ++ ++ iterbdrvs = bdrvs; ++ while (iterbdrvs) { ++ BlockDriverState *bs = iterbdrvs->data; ++ bool ok = true; ++ ++ if (devices || bdrv_all_snapshots_includes_bs(bs)) { ++ ok = bdrv_can_snapshot(bs); ++ } ++ if (!ok) { ++ error_setg(errp, "Device '%s' is writable but does not support " ++ "snapshots", bdrv_get_device_or_node_name(bs)); ++ return false; ++ } ++ ++ iterbdrvs = iterbdrvs->next; ++ } ++ ++ return true; ++} ++ ++int bdrv_all_delete_snapshot(const char *name, ++ bool has_devices, strList *devices, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ g_autoptr(GList) bdrvs = NULL; ++ GList *iterbdrvs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { ++ return -1; ++ } ++ ++ iterbdrvs = bdrvs; ++ while (iterbdrvs) { ++ BlockDriverState *bs = iterbdrvs->data; ++ QEMUSnapshotInfo sn1, *snapshot = &sn1; ++ int ret = 0; ++ ++ if ((devices || bdrv_all_snapshots_includes_bs(bs)) && ++ bdrv_snapshot_find(bs, snapshot, name) >= 0) ++ { ++ ret = bdrv_snapshot_delete(bs, snapshot->id_str, ++ snapshot->name, errp); ++ } ++ if (ret < 0) { ++ error_prepend(errp, "Could not delete snapshot '%s' on '%s': ", ++ name, bdrv_get_device_or_node_name(bs)); ++ return -1; ++ } ++ ++ iterbdrvs = iterbdrvs->next; ++ } ++ ++ return 0; ++} ++ ++ ++int bdrv_all_goto_snapshot(const char *name, ++ bool has_devices, strList *devices, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ g_autoptr(GList) bdrvs = NULL; ++ GList *iterbdrvs; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_graph_rdlock_main_loop(); ++ ret = bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (ret < 0) { ++ return -1; ++ } ++ ++ iterbdrvs = bdrvs; ++ while (iterbdrvs) { ++ BlockDriverState *bs = iterbdrvs->data; ++ bool all_snapshots_includes_bs; ++ ++ bdrv_graph_rdlock_main_loop(); ++ all_snapshots_includes_bs = bdrv_all_snapshots_includes_bs(bs); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ ret = (devices || all_snapshots_includes_bs) ? ++ bdrv_snapshot_goto(bs, name, errp) : 0; ++ if (ret < 0) { ++ bdrv_graph_rdlock_main_loop(); ++ error_prepend(errp, "Could not load snapshot '%s' on '%s': ", ++ name, bdrv_get_device_or_node_name(bs)); ++ bdrv_graph_rdunlock_main_loop(); ++ return -1; ++ } ++ ++ iterbdrvs = iterbdrvs->next; ++ } ++ ++ return 0; ++} ++ ++int bdrv_all_has_snapshot(const char *name, ++ bool has_devices, strList *devices, ++ Error **errp) ++{ ++ g_autoptr(GList) bdrvs = NULL; ++ GList *iterbdrvs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { ++ return -1; ++ } ++ ++ iterbdrvs = bdrvs; ++ while (iterbdrvs) { ++ BlockDriverState *bs = iterbdrvs->data; ++ QEMUSnapshotInfo sn; ++ int ret = 0; ++ ++ if (devices || bdrv_all_snapshots_includes_bs(bs)) { ++ ret = bdrv_snapshot_find(bs, &sn, name); ++ } ++ if (ret < 0) { ++ if (ret == -ENOENT) { ++ return 0; ++ } else { ++ error_setg_errno(errp, errno, ++ "Could not check snapshot '%s' on '%s'", ++ name, bdrv_get_device_or_node_name(bs)); ++ return -1; ++ } ++ } ++ ++ iterbdrvs = iterbdrvs->next; ++ } ++ ++ return 1; ++} ++ ++int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, ++ BlockDriverState *vm_state_bs, ++ uint64_t vm_state_size, ++ bool has_devices, strList *devices, ++ Error **errp) ++{ ++ g_autoptr(GList) bdrvs = NULL; ++ GList *iterbdrvs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { ++ return -1; ++ } ++ ++ iterbdrvs = bdrvs; ++ while (iterbdrvs) { ++ BlockDriverState *bs = iterbdrvs->data; ++ int ret = 0; ++ ++ if (bs == vm_state_bs) { ++ sn->vm_state_size = vm_state_size; ++ ret = bdrv_snapshot_create(bs, sn); ++ } else if (devices || bdrv_all_snapshots_includes_bs(bs)) { ++ sn->vm_state_size = 0; ++ ret = bdrv_snapshot_create(bs, sn); ++ } ++ if (ret < 0) { ++ error_setg(errp, "Could not create snapshot '%s' on '%s'", ++ sn->name, bdrv_get_device_or_node_name(bs)); ++ return -1; ++ } ++ ++ iterbdrvs = iterbdrvs->next; ++ } ++ ++ return 0; ++} ++ ++ ++BlockDriverState *bdrv_all_find_vmstate_bs(const char *vmstate_bs, ++ bool has_devices, strList *devices, ++ Error **errp) ++{ ++ g_autoptr(GList) bdrvs = NULL; ++ GList *iterbdrvs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { ++ return NULL; ++ } ++ ++ iterbdrvs = bdrvs; ++ while (iterbdrvs) { ++ BlockDriverState *bs = iterbdrvs->data; ++ bool found = false; ++ ++ found = (devices || bdrv_all_snapshots_includes_bs(bs)) && ++ bdrv_can_snapshot(bs); ++ ++ if (vmstate_bs) { ++ if (g_str_equal(vmstate_bs, ++ bdrv_get_node_name(bs))) { ++ if (found) { ++ return bs; ++ } else { ++ error_setg(errp, ++ "vmstate block device '%s' does not support snapshots", ++ vmstate_bs); ++ return NULL; ++ } ++ } ++ } else if (found) { ++ return bs; ++ } ++ ++ iterbdrvs = iterbdrvs->next; ++ } ++ ++ if (vmstate_bs) { ++ error_setg(errp, ++ "vmstate block device '%s' does not exist", vmstate_bs); ++ } else { ++ error_setg(errp, ++ "no block device can store vmstate for snapshot"); ++ } ++ return NULL; ++} +diff --git a/qcow2/lib/blockdev.c b/qcow2/lib/blockdev.c +new file mode 100644 +index 00000000..835064ed +--- /dev/null ++++ b/qcow2/lib/blockdev.c +@@ -0,0 +1,3649 @@ ++/* ++ * QEMU host block devices ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ * ++ * This file incorporates work covered by the following copyright and ++ * permission notice: ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "sysemu/block-backend.h" ++#include "sysemu/blockdev.h" ++#include "hw/block/block.h" ++#include "block/blockjob.h" ++#include "block/dirty-bitmap.h" ++#include "block/qdict.h" ++#include "block/throttle-groups.h" ++#include "monitor/monitor.h" ++#include "qemu/error-report.h" ++#include "qemu/option.h" ++#include "qemu/qemu-print.h" ++#include "qemu/config-file.h" ++#include "qapi/qapi-commands-block.h" ++#include "qapi/qapi-commands-transaction.h" ++#include "qapi/qapi-visit-block-core.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qerror.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qobject-output-visitor.h" ++#include "sysemu/sysemu.h" ++#include "sysemu/iothread.h" ++#include "block/block_int.h" ++#include "block/trace.h" ++#include "sysemu/runstate.h" ++#include "sysemu/replay.h" ++#include "qemu/cutils.h" ++#include "qemu/help_option.h" ++#include "qemu/main-loop.h" ++#include "qemu/throttle-options.h" ++ ++/* Protected by BQL */ ++QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = ++ QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); ++ ++void bdrv_set_monitor_owned(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list); ++} ++ ++static const char *const if_name[IF_COUNT] = { ++ [IF_NONE] = "none", ++ [IF_IDE] = "ide", ++ [IF_SCSI] = "scsi", ++ [IF_FLOPPY] = "floppy", ++ [IF_PFLASH] = "pflash", ++ [IF_MTD] = "mtd", ++ [IF_SD] = "sd", ++ [IF_VIRTIO] = "virtio", ++ [IF_XEN] = "xen", ++}; ++ ++static int if_max_devs[IF_COUNT] = { ++ /* ++ * Do not change these numbers! They govern how drive option ++ * index maps to unit and bus. That mapping is ABI. ++ * ++ * All controllers used to implement if=T drives need to support ++ * if_max_devs[T] units, for any T with if_max_devs[T] != 0. ++ * Otherwise, some index values map to "impossible" bus, unit ++ * values. ++ * ++ * For instance, if you change [IF_SCSI] to 255, -drive ++ * if=scsi,index=12 no longer means bus=1,unit=5, but ++ * bus=0,unit=12. With an lsi53c895a controller (7 units max), ++ * the drive can't be set up. Regression. ++ */ ++ [IF_IDE] = 2, ++ [IF_SCSI] = 7, ++}; ++ ++/** ++ * Boards may call this to offer board-by-board overrides ++ * of the default, global values. ++ */ ++void override_max_devs(BlockInterfaceType type, int max_devs) ++{ ++ BlockBackend *blk; ++ DriveInfo *dinfo; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (max_devs <= 0) { ++ return; ++ } ++ ++ for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { ++ dinfo = blk_legacy_dinfo(blk); ++ if (dinfo->type == type) { ++ fprintf(stderr, "Cannot override units-per-bus property of" ++ " the %s interface, because a drive of that type has" ++ " already been added.\n", if_name[type]); ++ g_assert_not_reached(); ++ } ++ } ++ ++ if_max_devs[type] = max_devs; ++} ++ ++/* ++ * We automatically delete the drive when a device using it gets ++ * unplugged. Questionable feature, but we can't just drop it. ++ * Device models call blockdev_mark_auto_del() to schedule the ++ * automatic deletion, and generic qdev code calls blockdev_auto_del() ++ * when deletion is actually safe. ++ */ ++void blockdev_mark_auto_del(BlockBackend *blk) ++{ ++ DriveInfo *dinfo = blk_legacy_dinfo(blk); ++ BlockJob *job; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!dinfo) { ++ return; ++ } ++ ++ JOB_LOCK_GUARD(); ++ ++ do { ++ job = block_job_next_locked(NULL); ++ while (job && (job->job.cancelled || ++ job->job.deferred_to_main_loop || ++ !block_job_has_bdrv(job, blk_bs(blk)))) ++ { ++ job = block_job_next_locked(job); ++ } ++ if (job) { ++ /* ++ * This drops the job lock temporarily and polls, so we need to ++ * restart processing the list from the start after this. ++ */ ++ job_cancel_locked(&job->job, false); ++ } ++ } while (job); ++ ++ dinfo->auto_del = 1; ++} ++ ++void blockdev_auto_del(BlockBackend *blk) ++{ ++ DriveInfo *dinfo = blk_legacy_dinfo(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (dinfo && dinfo->auto_del) { ++ monitor_remove_blk(blk); ++ blk_unref(blk); ++ } ++} ++ ++static int drive_index_to_bus_id(BlockInterfaceType type, int index) ++{ ++ int max_devs = if_max_devs[type]; ++ return max_devs ? index / max_devs : 0; ++} ++ ++static int drive_index_to_unit_id(BlockInterfaceType type, int index) ++{ ++ int max_devs = if_max_devs[type]; ++ return max_devs ? index % max_devs : index; ++} ++ ++QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, ++ const char *optstr) ++{ ++ QemuOpts *opts; ++ ++ GLOBAL_STATE_CODE(); ++ ++ opts = qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false); ++ if (!opts) { ++ return NULL; ++ } ++ if (type != IF_DEFAULT) { ++ qemu_opt_set(opts, "if", if_name[type], &error_abort); ++ } ++ if (index >= 0) { ++ qemu_opt_set_number(opts, "index", index, &error_abort); ++ } ++ if (file) ++ qemu_opt_set(opts, "file", file, &error_abort); ++ return opts; ++} ++ ++DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) ++{ ++ BlockBackend *blk; ++ DriveInfo *dinfo; ++ ++ GLOBAL_STATE_CODE(); ++ ++ for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { ++ dinfo = blk_legacy_dinfo(blk); ++ if (dinfo && dinfo->type == type ++ && dinfo->bus == bus && dinfo->unit == unit) { ++ return dinfo; ++ } ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Check board claimed all -drive that are meant to be claimed. ++ * Fatal error if any remain unclaimed. ++ */ ++void drive_check_orphaned(void) ++{ ++ BlockBackend *blk; ++ DriveInfo *dinfo; ++ Location loc; ++ bool orphans = false; ++ ++ GLOBAL_STATE_CODE(); ++ ++ for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { ++ dinfo = blk_legacy_dinfo(blk); ++ /* ++ * Ignore default drives, because we create certain default ++ * drives unconditionally, then leave them unclaimed. Not the ++ * users fault. ++ * Ignore IF_VIRTIO or IF_XEN, because it gets desugared into ++ * -device, so we can leave failing to -device. ++ * Ignore IF_NONE, because leaving unclaimed IF_NONE remains ++ * available for device_add is a feature. ++ */ ++ if (dinfo->is_default || dinfo->type == IF_VIRTIO ++ || dinfo->type == IF_XEN || dinfo->type == IF_NONE) { ++ continue; ++ } ++ if (!blk_get_attached_dev(blk)) { ++ loc_push_none(&loc); ++ qemu_opts_loc_restore(dinfo->opts); ++ error_report("machine type does not support" ++ " if=%s,bus=%d,unit=%d", ++ if_name[dinfo->type], dinfo->bus, dinfo->unit); ++ loc_pop(&loc); ++ orphans = true; ++ } ++ } ++ ++ if (orphans) { ++ exit(1); ++ } ++} ++ ++DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) ++{ ++ GLOBAL_STATE_CODE(); ++ return drive_get(type, ++ drive_index_to_bus_id(type, index), ++ drive_index_to_unit_id(type, index)); ++} ++ ++int drive_get_max_bus(BlockInterfaceType type) ++{ ++ int max_bus; ++ BlockBackend *blk; ++ DriveInfo *dinfo; ++ ++ GLOBAL_STATE_CODE(); ++ ++ max_bus = -1; ++ for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { ++ dinfo = blk_legacy_dinfo(blk); ++ if (dinfo && dinfo->type == type && dinfo->bus > max_bus) { ++ max_bus = dinfo->bus; ++ } ++ } ++ return max_bus; ++} ++ ++static void bdrv_format_print(void *opaque, const char *name) ++{ ++ qemu_printf(" %s", name); ++} ++ ++typedef struct { ++ QEMUBH *bh; ++ BlockDriverState *bs; ++} BDRVPutRefBH; ++ ++static int parse_block_error_action(const char *buf, bool is_read, Error **errp) ++{ ++ if (!strcmp(buf, "ignore")) { ++ return BLOCKDEV_ON_ERROR_IGNORE; ++ } else if (!is_read && !strcmp(buf, "enospc")) { ++ return BLOCKDEV_ON_ERROR_ENOSPC; ++ } else if (!strcmp(buf, "stop")) { ++ return BLOCKDEV_ON_ERROR_STOP; ++ } else if (!strcmp(buf, "report")) { ++ return BLOCKDEV_ON_ERROR_REPORT; ++ } else { ++ error_setg(errp, "'%s' invalid %s error action", ++ buf, is_read ? "read" : "write"); ++ return -1; ++ } ++} ++ ++static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals, ++ Error **errp) ++{ ++ const QListEntry *entry; ++ for (entry = qlist_first(intervals); entry; entry = qlist_next(entry)) { ++ switch (qobject_type(entry->value)) { ++ ++ case QTYPE_QSTRING: { ++ uint64_t length; ++ const char *str = qstring_get_str(qobject_to(QString, ++ entry->value)); ++ if (parse_uint_full(str, 10, &length) == 0 && ++ length > 0 && length <= UINT_MAX) { ++ block_acct_add_interval(stats, (unsigned) length); ++ } else { ++ error_setg(errp, "Invalid interval length: %s", str); ++ return false; ++ } ++ break; ++ } ++ ++ case QTYPE_QNUM: { ++ int64_t length = qnum_get_int(qobject_to(QNum, entry->value)); ++ ++ if (length > 0 && length <= UINT_MAX) { ++ block_acct_add_interval(stats, (unsigned) length); ++ } else { ++ error_setg(errp, "Invalid interval length: %" PRId64, length); ++ return false; ++ } ++ break; ++ } ++ ++ default: ++ error_setg(errp, "The specification of stats-intervals is invalid"); ++ return false; ++ } ++ } ++ return true; ++} ++ ++typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType; ++ ++/* All parameters but @opts are optional and may be set to NULL. */ ++static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags, ++ const char **throttling_group, ThrottleConfig *throttle_cfg, ++ BlockdevDetectZeroesOptions *detect_zeroes, Error **errp) ++{ ++ Error *local_error = NULL; ++ const char *aio; ++ ++ if (bdrv_flags) { ++ if (qemu_opt_get_bool(opts, "copy-on-read", false)) { ++ *bdrv_flags |= BDRV_O_COPY_ON_READ; ++ } ++ ++ if ((aio = qemu_opt_get(opts, "aio")) != NULL) { ++ if (bdrv_parse_aio(aio, bdrv_flags) < 0) { ++ error_setg(errp, "invalid aio option"); ++ return; ++ } ++ } ++ } ++ ++ /* disk I/O throttling */ ++ if (throttling_group) { ++ *throttling_group = qemu_opt_get(opts, "throttling.group"); ++ } ++ ++ if (throttle_cfg) { ++ throttle_config_init(throttle_cfg); ++ throttle_cfg->buckets[THROTTLE_BPS_TOTAL].avg = ++ qemu_opt_get_number(opts, "throttling.bps-total", 0); ++ throttle_cfg->buckets[THROTTLE_BPS_READ].avg = ++ qemu_opt_get_number(opts, "throttling.bps-read", 0); ++ throttle_cfg->buckets[THROTTLE_BPS_WRITE].avg = ++ qemu_opt_get_number(opts, "throttling.bps-write", 0); ++ throttle_cfg->buckets[THROTTLE_OPS_TOTAL].avg = ++ qemu_opt_get_number(opts, "throttling.iops-total", 0); ++ throttle_cfg->buckets[THROTTLE_OPS_READ].avg = ++ qemu_opt_get_number(opts, "throttling.iops-read", 0); ++ throttle_cfg->buckets[THROTTLE_OPS_WRITE].avg = ++ qemu_opt_get_number(opts, "throttling.iops-write", 0); ++ ++ throttle_cfg->buckets[THROTTLE_BPS_TOTAL].max = ++ qemu_opt_get_number(opts, "throttling.bps-total-max", 0); ++ throttle_cfg->buckets[THROTTLE_BPS_READ].max = ++ qemu_opt_get_number(opts, "throttling.bps-read-max", 0); ++ throttle_cfg->buckets[THROTTLE_BPS_WRITE].max = ++ qemu_opt_get_number(opts, "throttling.bps-write-max", 0); ++ throttle_cfg->buckets[THROTTLE_OPS_TOTAL].max = ++ qemu_opt_get_number(opts, "throttling.iops-total-max", 0); ++ throttle_cfg->buckets[THROTTLE_OPS_READ].max = ++ qemu_opt_get_number(opts, "throttling.iops-read-max", 0); ++ throttle_cfg->buckets[THROTTLE_OPS_WRITE].max = ++ qemu_opt_get_number(opts, "throttling.iops-write-max", 0); ++ ++ throttle_cfg->buckets[THROTTLE_BPS_TOTAL].burst_length = ++ qemu_opt_get_number(opts, "throttling.bps-total-max-length", 1); ++ throttle_cfg->buckets[THROTTLE_BPS_READ].burst_length = ++ qemu_opt_get_number(opts, "throttling.bps-read-max-length", 1); ++ throttle_cfg->buckets[THROTTLE_BPS_WRITE].burst_length = ++ qemu_opt_get_number(opts, "throttling.bps-write-max-length", 1); ++ throttle_cfg->buckets[THROTTLE_OPS_TOTAL].burst_length = ++ qemu_opt_get_number(opts, "throttling.iops-total-max-length", 1); ++ throttle_cfg->buckets[THROTTLE_OPS_READ].burst_length = ++ qemu_opt_get_number(opts, "throttling.iops-read-max-length", 1); ++ throttle_cfg->buckets[THROTTLE_OPS_WRITE].burst_length = ++ qemu_opt_get_number(opts, "throttling.iops-write-max-length", 1); ++ ++ throttle_cfg->op_size = ++ qemu_opt_get_number(opts, "throttling.iops-size", 0); ++ ++ if (!throttle_is_valid(throttle_cfg, errp)) { ++ return; ++ } ++ } ++ ++ if (detect_zeroes) { ++ *detect_zeroes = ++ qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup, ++ qemu_opt_get(opts, "detect-zeroes"), ++ BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, ++ &local_error); ++ if (local_error) { ++ error_propagate(errp, local_error); ++ return; ++ } ++ } ++} ++ ++static OnOffAuto account_get_opt(QemuOpts *opts, const char *name) ++{ ++ if (!qemu_opt_find(opts, name)) { ++ return ON_OFF_AUTO_AUTO; ++ } ++ if (qemu_opt_get_bool(opts, name, true)) { ++ return ON_OFF_AUTO_ON; ++ } ++ return ON_OFF_AUTO_OFF; ++} ++ ++/* Takes the ownership of bs_opts */ ++static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, ++ Error **errp) ++{ ++ const char *buf; ++ int bdrv_flags = 0; ++ int on_read_error, on_write_error; ++ OnOffAuto account_invalid, account_failed; ++ bool writethrough, read_only; ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ ThrottleConfig cfg; ++ int snapshot = 0; ++ Error *error = NULL; ++ QemuOpts *opts; ++ QDict *interval_dict = NULL; ++ QList *interval_list = NULL; ++ const char *id; ++ BlockdevDetectZeroesOptions detect_zeroes = ++ BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; ++ const char *throttling_group = NULL; ++ ++ /* Check common options by copying from bs_opts to opts, all other options ++ * stay in bs_opts for processing by bdrv_open(). */ ++ id = qdict_get_try_str(bs_opts, "id"); ++ opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, errp); ++ if (!opts) { ++ goto err_no_opts; ++ } ++ ++ if (!qemu_opts_absorb_qdict(opts, bs_opts, errp)) { ++ goto early_err; ++ } ++ ++ if (id) { ++ qdict_del(bs_opts, "id"); ++ } ++ ++ /* extract parameters */ ++ snapshot = qemu_opt_get_bool(opts, "snapshot", 0); ++ ++ account_invalid = account_get_opt(opts, "stats-account-invalid"); ++ account_failed = account_get_opt(opts, "stats-account-failed"); ++ ++ writethrough = !qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true); ++ ++ id = qemu_opts_id(opts); ++ ++ qdict_extract_subqdict(bs_opts, &interval_dict, "stats-intervals."); ++ qdict_array_split(interval_dict, &interval_list); ++ ++ if (qdict_size(interval_dict) != 0) { ++ error_setg(errp, "Invalid option stats-intervals.%s", ++ qdict_first(interval_dict)->key); ++ goto early_err; ++ } ++ ++ extract_common_blockdev_options(opts, &bdrv_flags, &throttling_group, &cfg, ++ &detect_zeroes, &error); ++ if (error) { ++ error_propagate(errp, error); ++ goto early_err; ++ } ++ ++ if ((buf = qemu_opt_get(opts, "format")) != NULL) { ++ if (is_help_option(buf)) { ++ qemu_printf("Supported formats:"); ++ bdrv_iterate_format(bdrv_format_print, NULL, false); ++ qemu_printf("\nSupported formats (read-only):"); ++ bdrv_iterate_format(bdrv_format_print, NULL, true); ++ qemu_printf("\n"); ++ goto early_err; ++ } ++ ++ if (qdict_haskey(bs_opts, "driver")) { ++ error_setg(errp, "Cannot specify both 'driver' and 'format'"); ++ goto early_err; ++ } ++ qdict_put_str(bs_opts, "driver", buf); ++ } ++ ++ on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; ++ if ((buf = qemu_opt_get(opts, "werror")) != NULL) { ++ on_write_error = parse_block_error_action(buf, 0, &error); ++ if (error) { ++ error_propagate(errp, error); ++ goto early_err; ++ } ++ } ++ ++ on_read_error = BLOCKDEV_ON_ERROR_REPORT; ++ if ((buf = qemu_opt_get(opts, "rerror")) != NULL) { ++ on_read_error = parse_block_error_action(buf, 1, &error); ++ if (error) { ++ error_propagate(errp, error); ++ goto early_err; ++ } ++ } ++ ++ if (snapshot) { ++ bdrv_flags |= BDRV_O_SNAPSHOT; ++ } ++ ++ read_only = qemu_opt_get_bool(opts, BDRV_OPT_READ_ONLY, false); ++ ++ /* init */ ++ if ((!file || !*file) && !qdict_size(bs_opts)) { ++ BlockBackendRootState *blk_rs; ++ ++ blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); ++ blk_rs = blk_get_root_state(blk); ++ blk_rs->open_flags = bdrv_flags | (read_only ? 0 : BDRV_O_RDWR); ++ blk_rs->detect_zeroes = detect_zeroes; ++ ++ qobject_unref(bs_opts); ++ } else { ++ if (file && !*file) { ++ file = NULL; ++ } ++ ++ /* bdrv_open() defaults to the values in bdrv_flags (for compatibility ++ * with other callers) rather than what we want as the real defaults. ++ * Apply the defaults here instead. */ ++ qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off"); ++ qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off"); ++ qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY, ++ read_only ? "on" : "off"); ++ qdict_set_default_str(bs_opts, BDRV_OPT_AUTO_READ_ONLY, "on"); ++ assert((bdrv_flags & BDRV_O_CACHE_MASK) == 0); ++ ++ if (runstate_check(RUN_STATE_INMIGRATE)) { ++ bdrv_flags |= BDRV_O_INACTIVE; ++ } ++ ++ blk = blk_new_open(file, NULL, bs_opts, bdrv_flags, errp); ++ if (!blk) { ++ goto err_no_bs_opts; ++ } ++ bs = blk_bs(blk); ++ ++ bs->detect_zeroes = detect_zeroes; ++ ++ block_acct_setup(blk_get_stats(blk), account_invalid, account_failed); ++ ++ if (!parse_stats_intervals(blk_get_stats(blk), interval_list, errp)) { ++ blk_unref(blk); ++ blk = NULL; ++ goto err_no_bs_opts; ++ } ++ } ++ ++ /* disk I/O throttling */ ++ if (throttle_enabled(&cfg)) { ++ if (!throttling_group) { ++ throttling_group = id; ++ } ++ blk_io_limits_enable(blk, throttling_group); ++ blk_set_io_limits(blk, &cfg); ++ } ++ ++ blk_set_enable_write_cache(blk, !writethrough); ++ blk_set_on_error(blk, on_read_error, on_write_error); ++ ++ if (!monitor_add_blk(blk, id, errp)) { ++ blk_unref(blk); ++ blk = NULL; ++ goto err_no_bs_opts; ++ } ++ ++err_no_bs_opts: ++ qemu_opts_del(opts); ++ qobject_unref(interval_dict); ++ qobject_unref(interval_list); ++ return blk; ++ ++early_err: ++ qemu_opts_del(opts); ++ qobject_unref(interval_dict); ++ qobject_unref(interval_list); ++err_no_opts: ++ qobject_unref(bs_opts); ++ return NULL; ++} ++ ++/* Takes the ownership of bs_opts */ ++BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp) ++{ ++ int bdrv_flags = 0; ++ ++ GLOBAL_STATE_CODE(); ++ /* bdrv_open() defaults to the values in bdrv_flags (for compatibility ++ * with other callers) rather than what we want as the real defaults. ++ * Apply the defaults here instead. */ ++ qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off"); ++ qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off"); ++ qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY, "off"); ++ ++ if (runstate_check(RUN_STATE_INMIGRATE)) { ++ bdrv_flags |= BDRV_O_INACTIVE; ++ } ++ ++ return bdrv_open(NULL, NULL, bs_opts, bdrv_flags, errp); ++} ++ ++void blockdev_close_all_bdrv_states(void) ++{ ++ BlockDriverState *bs, *next_bs; ++ ++ GLOBAL_STATE_CODE(); ++ QTAILQ_FOREACH_SAFE(bs, &monitor_bdrv_states, monitor_list, next_bs) { ++ bdrv_unref(bs); ++ } ++} ++ ++/* Iterates over the list of monitor-owned BlockDriverStates */ ++BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ return bs ? QTAILQ_NEXT(bs, monitor_list) ++ : QTAILQ_FIRST(&monitor_bdrv_states); ++} ++ ++static bool qemu_opt_rename(QemuOpts *opts, const char *from, const char *to, ++ Error **errp) ++{ ++ const char *value; ++ ++ value = qemu_opt_get(opts, from); ++ if (value) { ++ if (qemu_opt_find(opts, to)) { ++ error_setg(errp, "'%s' and its alias '%s' can't be used at the " ++ "same time", to, from); ++ return false; ++ } ++ } ++ ++ /* rename all items in opts */ ++ while ((value = qemu_opt_get(opts, from))) { ++ qemu_opt_set(opts, to, value, &error_abort); ++ qemu_opt_unset(opts, from); ++ } ++ return true; ++} ++ ++QemuOptsList qemu_legacy_drive_opts = { ++ .name = "drive", ++ .head = QTAILQ_HEAD_INITIALIZER(qemu_legacy_drive_opts.head), ++ .desc = { ++ { ++ .name = "bus", ++ .type = QEMU_OPT_NUMBER, ++ .help = "bus number", ++ },{ ++ .name = "unit", ++ .type = QEMU_OPT_NUMBER, ++ .help = "unit number (i.e. lun for scsi)", ++ },{ ++ .name = "index", ++ .type = QEMU_OPT_NUMBER, ++ .help = "index number", ++ },{ ++ .name = "media", ++ .type = QEMU_OPT_STRING, ++ .help = "media type (disk, cdrom)", ++ },{ ++ .name = "if", ++ .type = QEMU_OPT_STRING, ++ .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", ++ },{ ++ .name = "file", ++ .type = QEMU_OPT_STRING, ++ .help = "file name", ++ }, ++ ++ /* Options that are passed on, but have special semantics with -drive */ ++ { ++ .name = BDRV_OPT_READ_ONLY, ++ .type = QEMU_OPT_BOOL, ++ .help = "open drive file as read-only", ++ },{ ++ .name = "rerror", ++ .type = QEMU_OPT_STRING, ++ .help = "read error action", ++ },{ ++ .name = "werror", ++ .type = QEMU_OPT_STRING, ++ .help = "write error action", ++ },{ ++ .name = "copy-on-read", ++ .type = QEMU_OPT_BOOL, ++ .help = "copy read data from backing file into image file", ++ }, ++ ++ { /* end of list */ } ++ }, ++}; ++ ++DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type, ++ Error **errp) ++{ ++ const char *value; ++ BlockBackend *blk; ++ DriveInfo *dinfo = NULL; ++ QDict *bs_opts; ++ QemuOpts *legacy_opts; ++ DriveMediaType media = MEDIA_DISK; ++ BlockInterfaceType type; ++ int max_devs, bus_id, unit_id, index; ++ const char *werror, *rerror; ++ bool read_only = false; ++ bool copy_on_read; ++ const char *filename; ++ int i; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* Change legacy command line options into QMP ones */ ++ static const struct { ++ const char *from; ++ const char *to; ++ } opt_renames[] = { ++ { "iops", "throttling.iops-total" }, ++ { "iops_rd", "throttling.iops-read" }, ++ { "iops_wr", "throttling.iops-write" }, ++ ++ { "bps", "throttling.bps-total" }, ++ { "bps_rd", "throttling.bps-read" }, ++ { "bps_wr", "throttling.bps-write" }, ++ ++ { "iops_max", "throttling.iops-total-max" }, ++ { "iops_rd_max", "throttling.iops-read-max" }, ++ { "iops_wr_max", "throttling.iops-write-max" }, ++ ++ { "bps_max", "throttling.bps-total-max" }, ++ { "bps_rd_max", "throttling.bps-read-max" }, ++ { "bps_wr_max", "throttling.bps-write-max" }, ++ ++ { "iops_size", "throttling.iops-size" }, ++ ++ { "group", "throttling.group" }, ++ ++ { "readonly", BDRV_OPT_READ_ONLY }, ++ }; ++ ++ for (i = 0; i < ARRAY_SIZE(opt_renames); i++) { ++ if (!qemu_opt_rename(all_opts, opt_renames[i].from, ++ opt_renames[i].to, errp)) { ++ return NULL; ++ } ++ } ++ ++ value = qemu_opt_get(all_opts, "cache"); ++ if (value) { ++ int flags = 0; ++ bool writethrough; ++ ++ if (bdrv_parse_cache_mode(value, &flags, &writethrough) != 0) { ++ error_setg(errp, "invalid cache option"); ++ return NULL; ++ } ++ ++ /* Specific options take precedence */ ++ if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_WB)) { ++ qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_WB, ++ !writethrough, &error_abort); ++ } ++ if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_DIRECT)) { ++ qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_DIRECT, ++ !!(flags & BDRV_O_NOCACHE), &error_abort); ++ } ++ if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_NO_FLUSH)) { ++ qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_NO_FLUSH, ++ !!(flags & BDRV_O_NO_FLUSH), &error_abort); ++ } ++ qemu_opt_unset(all_opts, "cache"); ++ } ++ ++ /* Get a QDict for processing the options */ ++ bs_opts = qdict_new(); ++ qemu_opts_to_qdict(all_opts, bs_opts); ++ ++ legacy_opts = qemu_opts_create(&qemu_legacy_drive_opts, NULL, 0, ++ &error_abort); ++ if (!qemu_opts_absorb_qdict(legacy_opts, bs_opts, errp)) { ++ goto fail; ++ } ++ ++ /* Media type */ ++ value = qemu_opt_get(legacy_opts, "media"); ++ if (value) { ++ if (!strcmp(value, "disk")) { ++ media = MEDIA_DISK; ++ } else if (!strcmp(value, "cdrom")) { ++ media = MEDIA_CDROM; ++ read_only = true; ++ } else { ++ error_setg(errp, "'%s' invalid media", value); ++ goto fail; ++ } ++ } ++ ++ /* copy-on-read is disabled with a warning for read-only devices */ ++ read_only |= qemu_opt_get_bool(legacy_opts, BDRV_OPT_READ_ONLY, false); ++ copy_on_read = qemu_opt_get_bool(legacy_opts, "copy-on-read", false); ++ ++ if (read_only && copy_on_read) { ++ warn_report("disabling copy-on-read on read-only drive"); ++ copy_on_read = false; ++ } ++ ++ qdict_put_str(bs_opts, BDRV_OPT_READ_ONLY, read_only ? "on" : "off"); ++ qdict_put_str(bs_opts, "copy-on-read", copy_on_read ? "on" : "off"); ++ ++ /* Controller type */ ++ value = qemu_opt_get(legacy_opts, "if"); ++ if (value) { ++ for (type = 0; ++ type < IF_COUNT && strcmp(value, if_name[type]); ++ type++) { ++ } ++ if (type == IF_COUNT) { ++ error_setg(errp, "unsupported bus type '%s'", value); ++ goto fail; ++ } ++ } else { ++ type = block_default_type; ++ } ++ ++ /* Device address specified by bus/unit or index. ++ * If none was specified, try to find the first free one. */ ++ bus_id = qemu_opt_get_number(legacy_opts, "bus", 0); ++ unit_id = qemu_opt_get_number(legacy_opts, "unit", -1); ++ index = qemu_opt_get_number(legacy_opts, "index", -1); ++ ++ max_devs = if_max_devs[type]; ++ ++ if (index != -1) { ++ if (bus_id != 0 || unit_id != -1) { ++ error_setg(errp, "index cannot be used with bus and unit"); ++ goto fail; ++ } ++ bus_id = drive_index_to_bus_id(type, index); ++ unit_id = drive_index_to_unit_id(type, index); ++ } ++ ++ if (unit_id == -1) { ++ unit_id = 0; ++ while (drive_get(type, bus_id, unit_id) != NULL) { ++ unit_id++; ++ if (max_devs && unit_id >= max_devs) { ++ unit_id -= max_devs; ++ bus_id++; ++ } ++ } ++ } ++ ++ if (max_devs && unit_id >= max_devs) { ++ error_setg(errp, "unit %d too big (max is %d)", unit_id, max_devs - 1); ++ goto fail; ++ } ++ ++ if (drive_get(type, bus_id, unit_id) != NULL) { ++ error_setg(errp, "drive with bus=%d, unit=%d (index=%d) exists", ++ bus_id, unit_id, index); ++ goto fail; ++ } ++ ++ /* no id supplied -> create one */ ++ if (qemu_opts_id(all_opts) == NULL) { ++ char *new_id; ++ const char *mediastr = ""; ++ if (type == IF_IDE || type == IF_SCSI) { ++ mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd"; ++ } ++ if (max_devs) { ++ new_id = g_strdup_printf("%s%i%s%i", if_name[type], bus_id, ++ mediastr, unit_id); ++ } else { ++ new_id = g_strdup_printf("%s%s%i", if_name[type], ++ mediastr, unit_id); ++ } ++ qdict_put_str(bs_opts, "id", new_id); ++ g_free(new_id); ++ } ++ ++ /* Add virtio block device */ ++ if (type == IF_VIRTIO) { ++ QemuOpts *devopts; ++ devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, ++ &error_abort); ++ qemu_opt_set(devopts, "driver", "virtio-blk", &error_abort); ++ qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), ++ &error_abort); ++ } else if (type == IF_XEN) { ++ QemuOpts *devopts; ++ devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, ++ &error_abort); ++ qemu_opt_set(devopts, "driver", ++ (media == MEDIA_CDROM) ? "xen-cdrom" : "xen-disk", ++ &error_abort); ++ qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), ++ &error_abort); ++ } ++ ++ filename = qemu_opt_get(legacy_opts, "file"); ++ ++ /* Check werror/rerror compatibility with if=... */ ++ werror = qemu_opt_get(legacy_opts, "werror"); ++ if (werror != NULL) { ++ if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && ++ type != IF_NONE) { ++ error_setg(errp, "werror is not supported by this bus type"); ++ goto fail; ++ } ++ qdict_put_str(bs_opts, "werror", werror); ++ } ++ ++ rerror = qemu_opt_get(legacy_opts, "rerror"); ++ if (rerror != NULL) { ++ if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && ++ type != IF_NONE) { ++ error_setg(errp, "rerror is not supported by this bus type"); ++ goto fail; ++ } ++ qdict_put_str(bs_opts, "rerror", rerror); ++ } ++ ++ /* Actual block device init: Functionality shared with blockdev-add */ ++ blk = blockdev_init(filename, bs_opts, errp); ++ bs_opts = NULL; ++ if (!blk) { ++ goto fail; ++ } ++ ++ /* Create legacy DriveInfo */ ++ dinfo = g_malloc0(sizeof(*dinfo)); ++ dinfo->opts = all_opts; ++ ++ dinfo->type = type; ++ dinfo->bus = bus_id; ++ dinfo->unit = unit_id; ++ ++ blk_set_legacy_dinfo(blk, dinfo); ++ ++ switch(type) { ++ case IF_IDE: ++ case IF_SCSI: ++ case IF_XEN: ++ case IF_NONE: ++ dinfo->media_cd = media == MEDIA_CDROM; ++ break; ++ default: ++ break; ++ } ++ ++fail: ++ qemu_opts_del(legacy_opts); ++ qobject_unref(bs_opts); ++ return dinfo; ++} ++ ++static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) ++{ ++ BlockDriverState *bs; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ bs = bdrv_lookup_bs(name, name, errp); ++ if (bs == NULL) { ++ return NULL; ++ } ++ ++ if (!bdrv_is_root_node(bs)) { ++ error_setg(errp, "Need a root block node"); ++ return NULL; ++ } ++ ++ if (!bdrv_is_inserted(bs)) { ++ error_setg(errp, "Device has no medium"); ++ bs = NULL; ++ } ++ ++ return bs; ++} ++ ++static void blockdev_do_action(TransactionAction *action, Error **errp) ++{ ++ TransactionActionList list; ++ ++ list.value = action; ++ list.next = NULL; ++ qmp_transaction(&list, NULL, errp); ++} ++ ++void qmp_blockdev_snapshot_sync(const char *device, const char *node_name, ++ const char *snapshot_file, ++ const char *snapshot_node_name, ++ const char *format, ++ bool has_mode, NewImageMode mode, Error **errp) ++{ ++ BlockdevSnapshotSync snapshot = { ++ .device = (char *) device, ++ .node_name = (char *) node_name, ++ .snapshot_file = (char *) snapshot_file, ++ .snapshot_node_name = (char *) snapshot_node_name, ++ .format = (char *) format, ++ .has_mode = has_mode, ++ .mode = mode, ++ }; ++ TransactionAction action = { ++ .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC, ++ .u.blockdev_snapshot_sync.data = &snapshot, ++ }; ++ blockdev_do_action(&action, errp); ++} ++ ++void qmp_blockdev_snapshot(const char *node, const char *overlay, ++ Error **errp) ++{ ++ BlockdevSnapshot snapshot_data = { ++ .node = (char *) node, ++ .overlay = (char *) overlay ++ }; ++ TransactionAction action = { ++ .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT, ++ .u.blockdev_snapshot.data = &snapshot_data, ++ }; ++ blockdev_do_action(&action, errp); ++} ++ ++void qmp_blockdev_snapshot_internal_sync(const char *device, ++ const char *name, ++ Error **errp) ++{ ++ BlockdevSnapshotInternal snapshot = { ++ .device = (char *) device, ++ .name = (char *) name ++ }; ++ TransactionAction action = { ++ .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC, ++ .u.blockdev_snapshot_internal_sync.data = &snapshot, ++ }; ++ blockdev_do_action(&action, errp); ++} ++ ++SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, ++ const char *id, ++ const char *name, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ QEMUSnapshotInfo sn; ++ Error *local_err = NULL; ++ SnapshotInfo *info = NULL; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ bs = qmp_get_root_bs(device, errp); ++ if (!bs) { ++ return NULL; ++ } ++ ++ if (!id && !name) { ++ error_setg(errp, "Name or id must be provided"); ++ return NULL; ++ } ++ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) { ++ return NULL; ++ } ++ ++ ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return NULL; ++ } ++ if (!ret) { ++ error_setg(errp, ++ "Snapshot with id '%s' and name '%s' does not exist on " ++ "device '%s'", ++ STR_OR_NULL(id), STR_OR_NULL(name), device); ++ return NULL; ++ } ++ ++ bdrv_snapshot_delete(bs, id, name, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return NULL; ++ } ++ ++ info = g_new0(SnapshotInfo, 1); ++ info->id = g_strdup(sn.id_str); ++ info->name = g_strdup(sn.name); ++ info->date_nsec = sn.date_nsec; ++ info->date_sec = sn.date_sec; ++ info->vm_state_size = sn.vm_state_size; ++ info->vm_clock_nsec = sn.vm_clock_nsec % 1000000000; ++ info->vm_clock_sec = sn.vm_clock_nsec / 1000000000; ++ if (sn.icount != -1ULL) { ++ info->icount = sn.icount; ++ info->has_icount = true; ++ } ++ ++ return info; ++} ++ ++/* internal snapshot private data */ ++typedef struct InternalSnapshotState { ++ BlockDriverState *bs; ++ QEMUSnapshotInfo sn; ++ bool created; ++} InternalSnapshotState; ++ ++static void internal_snapshot_abort(void *opaque); ++static void internal_snapshot_clean(void *opaque); ++TransactionActionDrv internal_snapshot_drv = { ++ .abort = internal_snapshot_abort, ++ .clean = internal_snapshot_clean, ++}; ++ ++static void internal_snapshot_action(BlockdevSnapshotInternal *internal, ++ Transaction *tran, Error **errp) ++{ ++ Error *local_err = NULL; ++ const char *device; ++ const char *name; ++ BlockDriverState *bs; ++ QEMUSnapshotInfo old_sn, *sn; ++ bool ret; ++ int64_t rt; ++ InternalSnapshotState *state = g_new0(InternalSnapshotState, 1); ++ int ret1; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ tran_add(tran, &internal_snapshot_drv, state); ++ ++ device = internal->device; ++ name = internal->name; ++ ++ bs = qmp_get_root_bs(device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ state->bs = bs; ++ ++ /* Paired with .clean() */ ++ bdrv_drained_begin(bs); ++ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) { ++ return; ++ } ++ ++ if (bdrv_is_read_only(bs)) { ++ error_setg(errp, "Device '%s' is read only", device); ++ return; ++ } ++ ++ if (!bdrv_can_snapshot(bs)) { ++ error_setg(errp, "Block format '%s' used by device '%s' " ++ "does not support internal snapshots", ++ bs->drv->format_name, device); ++ return; ++ } ++ ++ if (!strlen(name)) { ++ error_setg(errp, "Name is empty"); ++ return; ++ } ++ ++ /* check whether a snapshot with name exist */ ++ ret = bdrv_snapshot_find_by_id_and_name(bs, NULL, name, &old_sn, ++ &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } else if (ret) { ++ error_setg(errp, ++ "Snapshot with name '%s' already exists on device '%s'", ++ name, device); ++ return; ++ } ++ ++ /* 3. take the snapshot */ ++ sn = &state->sn; ++ pstrcpy(sn->name, sizeof(sn->name), name); ++ rt = g_get_real_time(); ++ sn->date_sec = rt / G_USEC_PER_SEC; ++ sn->date_nsec = (rt % G_USEC_PER_SEC) * 1000; ++ sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ++ if (replay_mode != REPLAY_MODE_NONE) { ++ sn->icount = replay_get_current_icount(); ++ } else { ++ sn->icount = -1ULL; ++ } ++ ++ ret1 = bdrv_snapshot_create(bs, sn); ++ if (ret1 < 0) { ++ error_setg_errno(errp, -ret1, ++ "Failed to create snapshot '%s' on device '%s'", ++ name, device); ++ return; ++ } ++ ++ /* 4. succeed, mark a snapshot is created */ ++ state->created = true; ++} ++ ++static void internal_snapshot_abort(void *opaque) ++{ ++ InternalSnapshotState *state = opaque; ++ BlockDriverState *bs = state->bs; ++ QEMUSnapshotInfo *sn = &state->sn; ++ Error *local_error = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!state->created) { ++ return; ++ } ++ ++ if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) { ++ error_reportf_err(local_error, ++ "Failed to delete snapshot with id '%s' and " ++ "name '%s' on device '%s' in abort: ", ++ sn->id_str, sn->name, ++ bdrv_get_device_name(bs)); ++ } ++} ++ ++static void internal_snapshot_clean(void *opaque) ++{ ++ g_autofree InternalSnapshotState *state = opaque; ++ ++ if (!state->bs) { ++ return; ++ } ++ ++ bdrv_drained_end(state->bs); ++} ++ ++/* external snapshot private data */ ++typedef struct ExternalSnapshotState { ++ BlockDriverState *old_bs; ++ BlockDriverState *new_bs; ++ bool overlay_appended; ++} ExternalSnapshotState; ++ ++static void external_snapshot_commit(void *opaque); ++static void external_snapshot_abort(void *opaque); ++static void external_snapshot_clean(void *opaque); ++TransactionActionDrv external_snapshot_drv = { ++ .commit = external_snapshot_commit, ++ .abort = external_snapshot_abort, ++ .clean = external_snapshot_clean, ++}; ++ ++static void external_snapshot_action(TransactionAction *action, ++ Transaction *tran, Error **errp) ++{ ++ int ret; ++ int flags = 0; ++ QDict *options = NULL; ++ Error *local_err = NULL; ++ /* Device and node name of the image to generate the snapshot from */ ++ const char *device; ++ const char *node_name; ++ /* Reference to the new image (for 'blockdev-snapshot') */ ++ const char *snapshot_ref; ++ /* File name of the new image (for 'blockdev-snapshot-sync') */ ++ const char *new_image_file; ++ ExternalSnapshotState *state = g_new0(ExternalSnapshotState, 1); ++ uint64_t perm, shared; ++ ++ /* TODO We'll eventually have to take a writer lock in this function */ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ tran_add(tran, &external_snapshot_drv, state); ++ ++ /* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar ++ * purpose but a different set of parameters */ ++ switch (action->type) { ++ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT: ++ { ++ BlockdevSnapshot *s = action->u.blockdev_snapshot.data; ++ device = s->node; ++ node_name = s->node; ++ new_image_file = NULL; ++ snapshot_ref = s->overlay; ++ } ++ break; ++ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC: ++ { ++ BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data; ++ device = s->device; ++ node_name = s->node_name; ++ new_image_file = s->snapshot_file; ++ snapshot_ref = NULL; ++ } ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ ++ /* start processing */ ++ ++ state->old_bs = bdrv_lookup_bs(device, node_name, errp); ++ if (!state->old_bs) { ++ return; ++ } ++ ++ /* Paired with .clean() */ ++ bdrv_drained_begin(state->old_bs); ++ ++ if (!bdrv_is_inserted(state->old_bs)) { ++ error_setg(errp, "Device '%s' has no medium", ++ bdrv_get_device_or_node_name(state->old_bs)); ++ return; ++ } ++ ++ if (bdrv_op_is_blocked(state->old_bs, ++ BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) { ++ return; ++ } ++ ++ if (!bdrv_is_read_only(state->old_bs)) { ++ ret = bdrv_flush(state->old_bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Write to node '%s' failed", ++ bdrv_get_device_or_node_name(state->old_bs)); ++ return; ++ } ++ } ++ ++ if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) { ++ BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data; ++ const char *format = s->format ?: "qcow2"; ++ enum NewImageMode mode; ++ const char *snapshot_node_name = s->snapshot_node_name; ++ ++ if (node_name && !snapshot_node_name) { ++ error_setg(errp, "New overlay node-name missing"); ++ return; ++ } ++ ++ if (snapshot_node_name && ++ bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) { ++ error_setg(errp, "New overlay node-name already in use"); ++ return; ++ } ++ ++ flags = state->old_bs->open_flags; ++ flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_COPY_ON_READ); ++ flags |= BDRV_O_NO_BACKING; ++ ++ /* create new image w/backing file */ ++ mode = s->has_mode ? s->mode : NEW_IMAGE_MODE_ABSOLUTE_PATHS; ++ if (mode != NEW_IMAGE_MODE_EXISTING) { ++ int64_t size = bdrv_getlength(state->old_bs); ++ if (size < 0) { ++ error_setg_errno(errp, -size, "bdrv_getlength failed"); ++ return; ++ } ++ bdrv_refresh_filename(state->old_bs); ++ ++ bdrv_img_create(new_image_file, format, ++ state->old_bs->filename, ++ state->old_bs->drv->format_name, ++ NULL, size, flags, false, &local_err); ++ ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ } ++ ++ options = qdict_new(); ++ if (snapshot_node_name) { ++ qdict_put_str(options, "node-name", snapshot_node_name); ++ } ++ qdict_put_str(options, "driver", format); ++ } ++ ++ state->new_bs = bdrv_open(new_image_file, snapshot_ref, options, flags, ++ errp); ++ ++ /* We will manually add the backing_hd field to the bs later */ ++ if (!state->new_bs) { ++ return; ++ } ++ ++ /* ++ * Allow attaching a backing file to an overlay that's already in use only ++ * if the parents don't assume that they are already seeing a valid image. ++ * (Specifically, allow it as a mirror target, which is write-only access.) ++ */ ++ bdrv_get_cumulative_perm(state->new_bs, &perm, &shared); ++ if (perm & BLK_PERM_CONSISTENT_READ) { ++ error_setg(errp, "The overlay is already in use"); ++ return; ++ } ++ ++ if (state->new_bs->drv->is_filter) { ++ error_setg(errp, "Filters cannot be used as overlays"); ++ return; ++ } ++ ++ if (bdrv_cow_child(state->new_bs)) { ++ error_setg(errp, "The overlay already has a backing image"); ++ return; ++ } ++ ++ if (!state->new_bs->drv->supports_backing) { ++ error_setg(errp, "The overlay does not support backing images"); ++ return; ++ } ++ ++ ret = bdrv_append(state->new_bs, state->old_bs, errp); ++ if (ret < 0) { ++ return; ++ } ++ state->overlay_appended = true; ++} ++ ++static void external_snapshot_commit(void *opaque) ++{ ++ ExternalSnapshotState *state = opaque; ++ ++ /* We don't need (or want) to use the transactional ++ * bdrv_reopen_multiple() across all the entries at once, because we ++ * don't want to abort all of them if one of them fails the reopen */ ++ if (!qatomic_read(&state->old_bs->copy_on_read)) { ++ bdrv_reopen_set_read_only(state->old_bs, true, NULL); ++ } ++} ++ ++static void external_snapshot_abort(void *opaque) ++{ ++ ExternalSnapshotState *state = opaque; ++ if (state->new_bs) { ++ if (state->overlay_appended) { ++ AioContext *aio_context; ++ AioContext *tmp_context; ++ int ret; ++ ++ aio_context = bdrv_get_aio_context(state->old_bs); ++ ++ bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd() ++ close state->old_bs; we need it */ ++ bdrv_set_backing_hd(state->new_bs, NULL, &error_abort); ++ ++ /* ++ * The call to bdrv_set_backing_hd() above returns state->old_bs to ++ * the main AioContext. As we're still going to be using it, return ++ * it to the AioContext it was before. ++ */ ++ tmp_context = bdrv_get_aio_context(state->old_bs); ++ if (aio_context != tmp_context) { ++ ret = bdrv_try_change_aio_context(state->old_bs, ++ aio_context, NULL, NULL); ++ assert(ret == 0); ++ } ++ ++ bdrv_drained_begin(state->new_bs); ++ bdrv_graph_wrlock(); ++ bdrv_replace_node(state->new_bs, state->old_bs, &error_abort); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(state->new_bs); ++ ++ bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */ ++ } ++ } ++} ++ ++static void external_snapshot_clean(void *opaque) ++{ ++ g_autofree ExternalSnapshotState *state = opaque; ++ ++ if (!state->old_bs) { ++ return; ++ } ++ ++ bdrv_drained_end(state->old_bs); ++ bdrv_unref(state->new_bs); ++} ++ ++typedef struct DriveBackupState { ++ BlockDriverState *bs; ++ BlockJob *job; ++} DriveBackupState; ++ ++static BlockJob *do_backup_common(BackupCommon *backup, ++ BlockDriverState *bs, ++ BlockDriverState *target_bs, ++ AioContext *aio_context, ++ JobTxn *txn, Error **errp); ++ ++static void drive_backup_commit(void *opaque); ++static void drive_backup_abort(void *opaque); ++static void drive_backup_clean(void *opaque); ++TransactionActionDrv drive_backup_drv = { ++ .commit = drive_backup_commit, ++ .abort = drive_backup_abort, ++ .clean = drive_backup_clean, ++}; ++ ++static void drive_backup_action(DriveBackup *backup, ++ JobTxn *block_job_txn, ++ Transaction *tran, Error **errp) ++{ ++ DriveBackupState *state = g_new0(DriveBackupState, 1); ++ BlockDriverState *bs; ++ BlockDriverState *target_bs; ++ BlockDriverState *source = NULL; ++ AioContext *aio_context; ++ const char *format; ++ QDict *options; ++ Error *local_err = NULL; ++ int flags; ++ int64_t size; ++ bool set_backing_hd = false; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ tran_add(tran, &drive_backup_drv, state); ++ ++ if (!backup->has_mode) { ++ backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; ++ } ++ ++ bs = bdrv_lookup_bs(backup->device, backup->device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ if (!bs->drv) { ++ error_setg(errp, "Device has no medium"); ++ return; ++ } ++ ++ aio_context = bdrv_get_aio_context(bs); ++ ++ state->bs = bs; ++ /* Paired with .clean() */ ++ bdrv_drained_begin(bs); ++ ++ format = backup->format; ++ if (!format && backup->mode != NEW_IMAGE_MODE_EXISTING) { ++ format = bs->drv->format_name; ++ } ++ ++ /* Early check to avoid creating target */ ++ bdrv_graph_rdlock_main_loop(); ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { ++ bdrv_graph_rdunlock_main_loop(); ++ return; ++ } ++ ++ flags = bs->open_flags | BDRV_O_RDWR; ++ ++ /* ++ * See if we have a backing HD we can use to create our new image ++ * on top of. ++ */ ++ if (backup->sync == MIRROR_SYNC_MODE_TOP) { ++ /* ++ * Backup will not replace the source by the target, so none ++ * of the filters skipped here will be removed (in contrast to ++ * mirror). Therefore, we can skip all of them when looking ++ * for the first COW relationship. ++ */ ++ source = bdrv_cow_bs(bdrv_skip_filters(bs)); ++ if (!source) { ++ backup->sync = MIRROR_SYNC_MODE_FULL; ++ } ++ } ++ if (backup->sync == MIRROR_SYNC_MODE_NONE) { ++ source = bs; ++ flags |= BDRV_O_NO_BACKING; ++ set_backing_hd = true; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ size = bdrv_getlength(bs); ++ if (size < 0) { ++ error_setg_errno(errp, -size, "bdrv_getlength failed"); ++ return; ++ } ++ ++ if (backup->mode != NEW_IMAGE_MODE_EXISTING) { ++ assert(format); ++ if (source) { ++ /* Implicit filters should not appear in the filename */ ++ BlockDriverState *explicit_backing; ++ ++ bdrv_graph_rdlock_main_loop(); ++ explicit_backing = bdrv_skip_implicit_filters(source); ++ bdrv_refresh_filename(explicit_backing); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_img_create(backup->target, format, ++ explicit_backing->filename, ++ explicit_backing->drv->format_name, NULL, ++ size, flags, false, &local_err); ++ } else { ++ bdrv_img_create(backup->target, format, NULL, NULL, NULL, ++ size, flags, false, &local_err); ++ } ++ } ++ ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ options = qdict_new(); ++ qdict_put_str(options, "discard", "unmap"); ++ qdict_put_str(options, "detect-zeroes", "unmap"); ++ if (format) { ++ qdict_put_str(options, "driver", format); ++ } ++ ++ target_bs = bdrv_open(backup->target, NULL, options, flags, errp); ++ if (!target_bs) { ++ return; ++ } ++ ++ ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); ++ if (ret < 0) { ++ bdrv_unref(target_bs); ++ return; ++ } ++ ++ if (set_backing_hd) { ++ if (bdrv_set_backing_hd(target_bs, source, errp) < 0) { ++ goto unref; ++ } ++ } ++ ++ state->job = do_backup_common(qapi_DriveBackup_base(backup), ++ bs, target_bs, aio_context, ++ block_job_txn, errp); ++ ++unref: ++ bdrv_unref(target_bs); ++} ++ ++static void drive_backup_commit(void *opaque) ++{ ++ DriveBackupState *state = opaque; ++ ++ assert(state->job); ++ job_start(&state->job->job); ++} ++ ++static void drive_backup_abort(void *opaque) ++{ ++ DriveBackupState *state = opaque; ++ ++ if (state->job) { ++ job_cancel_sync(&state->job->job, true); ++ } ++} ++ ++static void drive_backup_clean(void *opaque) ++{ ++ g_autofree DriveBackupState *state = opaque; ++ ++ if (!state->bs) { ++ return; ++ } ++ ++ bdrv_drained_end(state->bs); ++} ++ ++typedef struct BlockdevBackupState { ++ BlockDriverState *bs; ++ BlockJob *job; ++} BlockdevBackupState; ++ ++static void blockdev_backup_commit(void *opaque); ++static void blockdev_backup_abort(void *opaque); ++static void blockdev_backup_clean(void *opaque); ++TransactionActionDrv blockdev_backup_drv = { ++ .commit = blockdev_backup_commit, ++ .abort = blockdev_backup_abort, ++ .clean = blockdev_backup_clean, ++}; ++ ++static void blockdev_backup_action(BlockdevBackup *backup, ++ JobTxn *block_job_txn, ++ Transaction *tran, Error **errp) ++{ ++ BlockdevBackupState *state = g_new0(BlockdevBackupState, 1); ++ BlockDriverState *bs; ++ BlockDriverState *target_bs; ++ AioContext *aio_context; ++ int ret; ++ ++ tran_add(tran, &blockdev_backup_drv, state); ++ ++ bs = bdrv_lookup_bs(backup->device, backup->device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ target_bs = bdrv_lookup_bs(backup->target, backup->target, errp); ++ if (!target_bs) { ++ return; ++ } ++ ++ /* Honor bdrv_try_change_aio_context() context acquisition requirements. */ ++ aio_context = bdrv_get_aio_context(bs); ++ ++ ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); ++ if (ret < 0) { ++ return; ++ } ++ ++ state->bs = bs; ++ ++ /* Paired with .clean() */ ++ bdrv_drained_begin(state->bs); ++ ++ state->job = do_backup_common(qapi_BlockdevBackup_base(backup), ++ bs, target_bs, aio_context, ++ block_job_txn, errp); ++} ++ ++static void blockdev_backup_commit(void *opaque) ++{ ++ BlockdevBackupState *state = opaque; ++ ++ assert(state->job); ++ job_start(&state->job->job); ++} ++ ++static void blockdev_backup_abort(void *opaque) ++{ ++ BlockdevBackupState *state = opaque; ++ ++ if (state->job) { ++ job_cancel_sync(&state->job->job, true); ++ } ++} ++ ++static void blockdev_backup_clean(void *opaque) ++{ ++ g_autofree BlockdevBackupState *state = opaque; ++ ++ if (!state->bs) { ++ return; ++ } ++ ++ bdrv_drained_end(state->bs); ++} ++ ++typedef struct BlockDirtyBitmapState { ++ BdrvDirtyBitmap *bitmap; ++ BlockDriverState *bs; ++ HBitmap *backup; ++ bool was_enabled; ++} BlockDirtyBitmapState; ++ ++static void block_dirty_bitmap_add_abort(void *opaque); ++TransactionActionDrv block_dirty_bitmap_add_drv = { ++ .abort = block_dirty_bitmap_add_abort, ++ .clean = g_free, ++}; ++ ++static void block_dirty_bitmap_add_action(BlockDirtyBitmapAdd *action, ++ Transaction *tran, Error **errp) ++{ ++ Error *local_err = NULL; ++ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); ++ ++ tran_add(tran, &block_dirty_bitmap_add_drv, state); ++ ++ /* AIO context taken and released within qmp_block_dirty_bitmap_add */ ++ qmp_block_dirty_bitmap_add(action->node, action->name, ++ action->has_granularity, action->granularity, ++ action->has_persistent, action->persistent, ++ action->has_disabled, action->disabled, ++ &local_err); ++ ++ if (!local_err) { ++ state->bitmap = block_dirty_bitmap_lookup(action->node, action->name, ++ NULL, &error_abort); ++ } else { ++ error_propagate(errp, local_err); ++ } ++} ++ ++static void block_dirty_bitmap_add_abort(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ if (state->bitmap) { ++ bdrv_release_dirty_bitmap(state->bitmap); ++ } ++} ++ ++static void block_dirty_bitmap_restore(void *opaque); ++static void block_dirty_bitmap_free_backup(void *opaque); ++TransactionActionDrv block_dirty_bitmap_clear_drv = { ++ .abort = block_dirty_bitmap_restore, ++ .commit = block_dirty_bitmap_free_backup, ++ .clean = g_free, ++}; ++ ++static void block_dirty_bitmap_clear_action(BlockDirtyBitmap *action, ++ Transaction *tran, Error **errp) ++{ ++ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); ++ ++ tran_add(tran, &block_dirty_bitmap_clear_drv, state); ++ ++ state->bitmap = block_dirty_bitmap_lookup(action->node, ++ action->name, ++ &state->bs, ++ errp); ++ if (!state->bitmap) { ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_DEFAULT, errp)) { ++ return; ++ } ++ ++ bdrv_clear_dirty_bitmap(state->bitmap, &state->backup); ++} ++ ++static void block_dirty_bitmap_restore(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ if (state->backup) { ++ bdrv_restore_dirty_bitmap(state->bitmap, state->backup); ++ } ++} ++ ++static void block_dirty_bitmap_free_backup(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ hbitmap_free(state->backup); ++} ++ ++static void block_dirty_bitmap_enable_abort(void *opaque); ++TransactionActionDrv block_dirty_bitmap_enable_drv = { ++ .abort = block_dirty_bitmap_enable_abort, ++ .clean = g_free, ++}; ++ ++static void block_dirty_bitmap_enable_action(BlockDirtyBitmap *action, ++ Transaction *tran, Error **errp) ++{ ++ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); ++ ++ tran_add(tran, &block_dirty_bitmap_enable_drv, state); ++ ++ state->bitmap = block_dirty_bitmap_lookup(action->node, ++ action->name, ++ NULL, ++ errp); ++ if (!state->bitmap) { ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { ++ return; ++ } ++ ++ state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); ++ bdrv_enable_dirty_bitmap(state->bitmap); ++} ++ ++static void block_dirty_bitmap_enable_abort(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ if (!state->was_enabled) { ++ bdrv_disable_dirty_bitmap(state->bitmap); ++ } ++} ++ ++static void block_dirty_bitmap_disable_abort(void *opaque); ++TransactionActionDrv block_dirty_bitmap_disable_drv = { ++ .abort = block_dirty_bitmap_disable_abort, ++ .clean = g_free, ++}; ++ ++static void block_dirty_bitmap_disable_action(BlockDirtyBitmap *action, ++ Transaction *tran, Error **errp) ++{ ++ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); ++ ++ tran_add(tran, &block_dirty_bitmap_disable_drv, state); ++ ++ state->bitmap = block_dirty_bitmap_lookup(action->node, ++ action->name, ++ NULL, ++ errp); ++ if (!state->bitmap) { ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { ++ return; ++ } ++ ++ state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); ++ bdrv_disable_dirty_bitmap(state->bitmap); ++} ++ ++static void block_dirty_bitmap_disable_abort(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ if (state->was_enabled) { ++ bdrv_enable_dirty_bitmap(state->bitmap); ++ } ++} ++ ++TransactionActionDrv block_dirty_bitmap_merge_drv = { ++ .commit = block_dirty_bitmap_free_backup, ++ .abort = block_dirty_bitmap_restore, ++ .clean = g_free, ++}; ++ ++static void block_dirty_bitmap_merge_action(BlockDirtyBitmapMerge *action, ++ Transaction *tran, Error **errp) ++{ ++ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); ++ ++ tran_add(tran, &block_dirty_bitmap_merge_drv, state); ++ ++ state->bitmap = block_dirty_bitmap_merge(action->node, action->target, ++ action->bitmaps, &state->backup, ++ errp); ++} ++ ++static void block_dirty_bitmap_remove_commit(void *opaque); ++static void block_dirty_bitmap_remove_abort(void *opaque); ++TransactionActionDrv block_dirty_bitmap_remove_drv = { ++ .commit = block_dirty_bitmap_remove_commit, ++ .abort = block_dirty_bitmap_remove_abort, ++ .clean = g_free, ++}; ++ ++static void block_dirty_bitmap_remove_action(BlockDirtyBitmap *action, ++ Transaction *tran, Error **errp) ++{ ++ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); ++ ++ tran_add(tran, &block_dirty_bitmap_remove_drv, state); ++ ++ ++ state->bitmap = block_dirty_bitmap_remove(action->node, action->name, ++ false, &state->bs, errp); ++ if (state->bitmap) { ++ bdrv_dirty_bitmap_skip_store(state->bitmap, true); ++ bdrv_dirty_bitmap_set_busy(state->bitmap, true); ++ } ++} ++ ++static void block_dirty_bitmap_remove_abort(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ if (state->bitmap) { ++ bdrv_dirty_bitmap_skip_store(state->bitmap, false); ++ bdrv_dirty_bitmap_set_busy(state->bitmap, false); ++ } ++} ++ ++static void block_dirty_bitmap_remove_commit(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ bdrv_dirty_bitmap_set_busy(state->bitmap, false); ++ bdrv_release_dirty_bitmap(state->bitmap); ++} ++ ++static void abort_commit(void *opaque); ++TransactionActionDrv abort_drv = { ++ .commit = abort_commit, ++}; ++ ++static void abort_action(Transaction *tran, Error **errp) ++{ ++ tran_add(tran, &abort_drv, NULL); ++ error_setg(errp, "Transaction aborted using Abort action"); ++} ++ ++static void abort_commit(void *opaque) ++{ ++ g_assert_not_reached(); /* this action never succeeds */ ++} ++ ++static void transaction_action(TransactionAction *act, JobTxn *block_job_txn, ++ Transaction *tran, Error **errp) ++{ ++ switch (act->type) { ++ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT: ++ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC: ++ external_snapshot_action(act, tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_DRIVE_BACKUP: ++ drive_backup_action(act->u.drive_backup.data, ++ block_job_txn, tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP: ++ blockdev_backup_action(act->u.blockdev_backup.data, ++ block_job_txn, tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_ABORT: ++ abort_action(tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC: ++ internal_snapshot_action(act->u.blockdev_snapshot_internal_sync.data, ++ tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ADD: ++ block_dirty_bitmap_add_action(act->u.block_dirty_bitmap_add.data, ++ tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR: ++ block_dirty_bitmap_clear_action(act->u.block_dirty_bitmap_clear.data, ++ tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE: ++ block_dirty_bitmap_enable_action(act->u.block_dirty_bitmap_enable.data, ++ tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE: ++ block_dirty_bitmap_disable_action( ++ act->u.block_dirty_bitmap_disable.data, tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_MERGE: ++ block_dirty_bitmap_merge_action(act->u.block_dirty_bitmap_merge.data, ++ tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_REMOVE: ++ block_dirty_bitmap_remove_action(act->u.block_dirty_bitmap_remove.data, ++ tran, errp); ++ return; ++ /* ++ * Where are transactions for MIRROR, COMMIT and STREAM? ++ * Although these blockjobs use transaction callbacks like the backup job, ++ * these jobs do not necessarily adhere to transaction semantics. ++ * These jobs may not fully undo all of their actions on abort, nor do they ++ * necessarily work in transactions with more than one job in them. ++ */ ++ case TRANSACTION_ACTION_KIND__MAX: ++ default: ++ g_assert_not_reached(); ++ }; ++} ++ ++ ++/* ++ * 'Atomic' group operations. The operations are performed as a set, and if ++ * any fail then we roll back all operations in the group. ++ * ++ * Always run under BQL. ++ */ ++void qmp_transaction(TransactionActionList *actions, ++ struct TransactionProperties *properties, ++ Error **errp) ++{ ++ TransactionActionList *act; ++ JobTxn *block_job_txn = NULL; ++ Error *local_err = NULL; ++ Transaction *tran; ++ ActionCompletionMode comp_mode = ++ properties ? properties->completion_mode : ++ ACTION_COMPLETION_MODE_INDIVIDUAL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* Does this transaction get canceled as a group on failure? ++ * If not, we don't really need to make a JobTxn. ++ */ ++ if (comp_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { ++ for (act = actions; act; act = act->next) { ++ TransactionActionKind type = act->value->type; ++ ++ if (type != TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP && ++ type != TRANSACTION_ACTION_KIND_DRIVE_BACKUP) ++ { ++ error_setg(errp, ++ "Action '%s' does not support transaction property " ++ "completion-mode = %s", ++ TransactionActionKind_str(type), ++ ActionCompletionMode_str(comp_mode)); ++ return; ++ } ++ } ++ ++ block_job_txn = job_txn_new(); ++ } ++ ++ /* drain all i/o before any operations */ ++ bdrv_drain_all(); ++ ++ tran = tran_new(); ++ ++ /* We don't do anything in this loop that commits us to the operations */ ++ for (act = actions; act; act = act->next) { ++ transaction_action(act->value, block_job_txn, tran, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ goto delete_and_fail; ++ } ++ } ++ ++ tran_commit(tran); ++ ++ /* success */ ++ goto exit; ++ ++delete_and_fail: ++ /* failure, and it is all-or-none; roll back all operations */ ++ tran_abort(tran); ++exit: ++ job_txn_unref(block_job_txn); ++} ++ ++BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, ++ const char *name, ++ Error **errp) ++{ ++ BdrvDirtyBitmap *bitmap; ++ BlockDriverState *bs; ++ BlockDirtyBitmapSha256 *ret = NULL; ++ char *sha256; ++ ++ bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); ++ if (!bitmap || !bs) { ++ return NULL; ++ } ++ ++ sha256 = bdrv_dirty_bitmap_sha256(bitmap, errp); ++ if (sha256 == NULL) { ++ return NULL; ++ } ++ ++ ret = g_new(BlockDirtyBitmapSha256, 1); ++ ret->sha256 = sha256; ++ ++ return ret; ++} ++ ++void coroutine_fn qmp_block_resize(const char *device, const char *node_name, ++ int64_t size, Error **errp) ++{ ++ Error *local_err = NULL; ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ AioContext *old_ctx; ++ ++ bs = bdrv_lookup_bs(device, node_name, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ if (size < 0) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size"); ++ return; ++ } ++ ++ bdrv_graph_co_rdlock(); ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, errp)) { ++ bdrv_graph_co_rdunlock(); ++ return; ++ } ++ bdrv_graph_co_rdunlock(); ++ ++ blk = blk_co_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp); ++ if (!blk) { ++ return; ++ } ++ ++ bdrv_drained_begin(bs); ++ ++ old_ctx = bdrv_co_enter(bs); ++ blk_co_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp); ++ bdrv_co_leave(bs, old_ctx); ++ ++ bdrv_drained_end(bs); ++ blk_co_unref(blk); ++} ++ ++void qmp_block_stream(const char *job_id, const char *device, ++ const char *base, ++ const char *base_node, ++ const char *backing_file, ++ bool has_backing_mask_protocol, ++ bool backing_mask_protocol, ++ const char *bottom, ++ bool has_speed, int64_t speed, ++ bool has_on_error, BlockdevOnError on_error, ++ const char *filter_node_name, ++ bool has_auto_finalize, bool auto_finalize, ++ bool has_auto_dismiss, bool auto_dismiss, ++ Error **errp) ++{ ++ BlockDriverState *bs, *iter, *iter_end; ++ BlockDriverState *base_bs = NULL; ++ BlockDriverState *bottom_bs = NULL; ++ AioContext *aio_context; ++ Error *local_err = NULL; ++ int job_flags = JOB_DEFAULT; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (base && base_node) { ++ error_setg(errp, "'base' and 'base-node' cannot be specified " ++ "at the same time"); ++ return; ++ } ++ ++ if (base && bottom) { ++ error_setg(errp, "'base' and 'bottom' cannot be specified " ++ "at the same time"); ++ return; ++ } ++ ++ if (bottom && base_node) { ++ error_setg(errp, "'bottom' and 'base-node' cannot be specified " ++ "at the same time"); ++ return; ++ } ++ ++ if (!has_backing_mask_protocol) { ++ backing_mask_protocol = false; ++ } ++ ++ if (!has_on_error) { ++ on_error = BLOCKDEV_ON_ERROR_REPORT; ++ } ++ ++ bs = bdrv_lookup_bs(device, device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ aio_context = bdrv_get_aio_context(bs); ++ ++ bdrv_graph_rdlock_main_loop(); ++ if (base) { ++ base_bs = bdrv_find_backing_image(bs, base); ++ if (base_bs == NULL) { ++ error_setg(errp, "Can't find '%s' in the backing chain", base); ++ goto out_rdlock; ++ } ++ assert(bdrv_get_aio_context(base_bs) == aio_context); ++ } ++ ++ if (base_node) { ++ base_bs = bdrv_lookup_bs(NULL, base_node, errp); ++ if (!base_bs) { ++ goto out_rdlock; ++ } ++ if (bs == base_bs || !bdrv_chain_contains(bs, base_bs)) { ++ error_setg(errp, "Node '%s' is not a backing image of '%s'", ++ base_node, device); ++ goto out_rdlock; ++ } ++ assert(bdrv_get_aio_context(base_bs) == aio_context); ++ ++ bdrv_refresh_filename(base_bs); ++ } ++ ++ if (bottom) { ++ bottom_bs = bdrv_lookup_bs(NULL, bottom, errp); ++ if (!bottom_bs) { ++ goto out_rdlock; ++ } ++ if (!bottom_bs->drv) { ++ error_setg(errp, "Node '%s' is not open", bottom); ++ goto out_rdlock; ++ } ++ if (bottom_bs->drv->is_filter) { ++ error_setg(errp, "Node '%s' is a filter, use a non-filter node " ++ "as 'bottom'", bottom); ++ goto out_rdlock; ++ } ++ if (!bdrv_chain_contains(bs, bottom_bs)) { ++ error_setg(errp, "Node '%s' is not in a chain starting from '%s'", ++ bottom, device); ++ goto out_rdlock; ++ } ++ assert(bdrv_get_aio_context(bottom_bs) == aio_context); ++ } ++ ++ /* ++ * Check for op blockers in the whole chain between bs and base (or bottom) ++ */ ++ iter_end = bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs; ++ for (iter = bs; iter && iter != iter_end; ++ iter = bdrv_filter_or_cow_bs(iter)) ++ { ++ if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) { ++ goto out_rdlock; ++ } ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ /* if we are streaming the entire chain, the result will have no backing ++ * file, and specifying one is therefore an error */ ++ if (!base_bs && backing_file) { ++ error_setg(errp, "backing file specified, but streaming the " ++ "entire chain"); ++ return; ++ } ++ ++ if (has_auto_finalize && !auto_finalize) { ++ job_flags |= JOB_MANUAL_FINALIZE; ++ } ++ if (has_auto_dismiss && !auto_dismiss) { ++ job_flags |= JOB_MANUAL_DISMISS; ++ } ++ ++ stream_start(job_id, bs, base_bs, backing_file, ++ backing_mask_protocol, ++ bottom_bs, job_flags, has_speed ? speed : 0, on_error, ++ filter_node_name, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ trace_qmp_block_stream(bs); ++ return; ++ ++out_rdlock: ++ bdrv_graph_rdunlock_main_loop(); ++} ++ ++void qmp_block_commit(const char *job_id, const char *device, ++ const char *base_node, ++ const char *base, ++ const char *top_node, ++ const char *top, ++ const char *backing_file, ++ bool has_backing_mask_protocol, ++ bool backing_mask_protocol, ++ bool has_speed, int64_t speed, ++ bool has_on_error, BlockdevOnError on_error, ++ const char *filter_node_name, ++ bool has_auto_finalize, bool auto_finalize, ++ bool has_auto_dismiss, bool auto_dismiss, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BlockDriverState *iter; ++ BlockDriverState *base_bs, *top_bs; ++ AioContext *aio_context; ++ Error *local_err = NULL; ++ int job_flags = JOB_DEFAULT; ++ uint64_t top_perm, top_shared; ++ ++ /* TODO We'll eventually have to take a writer lock in this function */ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!has_speed) { ++ speed = 0; ++ } ++ if (!has_on_error) { ++ on_error = BLOCKDEV_ON_ERROR_REPORT; ++ } ++ if (has_auto_finalize && !auto_finalize) { ++ job_flags |= JOB_MANUAL_FINALIZE; ++ } ++ if (has_auto_dismiss && !auto_dismiss) { ++ job_flags |= JOB_MANUAL_DISMISS; ++ } ++ if (!has_backing_mask_protocol) { ++ backing_mask_protocol = false; ++ } ++ ++ /* Important Note: ++ * libvirt relies on the DeviceNotFound error class in order to probe for ++ * live commit feature versions; for this to work, we must make sure to ++ * perform the device lookup before any generic errors that may occur in a ++ * scenario in which all optional arguments are omitted. */ ++ bs = qmp_get_root_bs(device, &local_err); ++ if (!bs) { ++ bs = bdrv_lookup_bs(device, device, NULL); ++ if (!bs) { ++ error_free(local_err); ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "Device '%s' not found", device); ++ } else { ++ error_propagate(errp, local_err); ++ } ++ return; ++ } ++ ++ aio_context = bdrv_get_aio_context(bs); ++ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, errp)) { ++ return; ++ } ++ ++ /* default top_bs is the active layer */ ++ top_bs = bs; ++ ++ if (top_node && top) { ++ error_setg(errp, "'top-node' and 'top' are mutually exclusive"); ++ return; ++ } else if (top_node) { ++ top_bs = bdrv_lookup_bs(NULL, top_node, errp); ++ if (top_bs == NULL) { ++ return; ++ } ++ if (!bdrv_chain_contains(bs, top_bs)) { ++ error_setg(errp, "'%s' is not in this backing file chain", ++ top_node); ++ return; ++ } ++ } else if (top) { ++ /* This strcmp() is just a shortcut, there is no need to ++ * refresh @bs's filename. If it mismatches, ++ * bdrv_find_backing_image() will do the refresh and may still ++ * return @bs. */ ++ if (strcmp(bs->filename, top) != 0) { ++ top_bs = bdrv_find_backing_image(bs, top); ++ } ++ } ++ ++ if (top_bs == NULL) { ++ error_setg(errp, "Top image file %s not found", top ? top : "NULL"); ++ return; ++ } ++ ++ assert(bdrv_get_aio_context(top_bs) == aio_context); ++ ++ if (base_node && base) { ++ error_setg(errp, "'base-node' and 'base' are mutually exclusive"); ++ return; ++ } else if (base_node) { ++ base_bs = bdrv_lookup_bs(NULL, base_node, errp); ++ if (base_bs == NULL) { ++ return; ++ } ++ if (!bdrv_chain_contains(top_bs, base_bs)) { ++ error_setg(errp, "'%s' is not in this backing file chain", ++ base_node); ++ return; ++ } ++ } else if (base) { ++ base_bs = bdrv_find_backing_image(top_bs, base); ++ if (base_bs == NULL) { ++ error_setg(errp, "Can't find '%s' in the backing chain", base); ++ return; ++ } ++ } else { ++ base_bs = bdrv_find_base(top_bs); ++ if (base_bs == NULL) { ++ error_setg(errp, "There is no backimg image"); ++ return; ++ } ++ } ++ ++ assert(bdrv_get_aio_context(base_bs) == aio_context); ++ ++ for (iter = top_bs; iter != bdrv_filter_or_cow_bs(base_bs); ++ iter = bdrv_filter_or_cow_bs(iter)) ++ { ++ if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { ++ return; ++ } ++ } ++ ++ /* Do not allow attempts to commit an image into itself */ ++ if (top_bs == base_bs) { ++ error_setg(errp, "cannot commit an image into itself"); ++ return; ++ } ++ ++ /* ++ * Active commit is required if and only if someone has taken a ++ * WRITE permission on the top node. Historically, we have always ++ * used active commit for top nodes, so continue that practice ++ * lest we possibly break clients that rely on this behavior, e.g. ++ * to later attach this node to a writing parent. ++ * (Active commit is never really wrong.) ++ */ ++ bdrv_get_cumulative_perm(top_bs, &top_perm, &top_shared); ++ if (top_perm & BLK_PERM_WRITE || ++ bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs)) ++ { ++ if (backing_file) { ++ if (bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs)) { ++ error_setg(errp, "'backing-file' specified," ++ " but 'top' is the active layer"); ++ } else { ++ error_setg(errp, "'backing-file' specified, but 'top' has a " ++ "writer on it"); ++ } ++ return; ++ } ++ if (!job_id) { ++ /* ++ * Emulate here what block_job_create() does, because it ++ * is possible that @bs != @top_bs (the block job should ++ * be named after @bs, even if @top_bs is the actual ++ * source) ++ */ ++ job_id = bdrv_get_device_name(bs); ++ } ++ commit_active_start(job_id, top_bs, base_bs, job_flags, speed, on_error, ++ filter_node_name, NULL, NULL, false, &local_err); ++ } else { ++ BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); ++ if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { ++ return; ++ } ++ commit_start(job_id, bs, base_bs, top_bs, job_flags, ++ speed, on_error, backing_file, ++ backing_mask_protocol, ++ filter_node_name, &local_err); ++ } ++ if (local_err != NULL) { ++ error_propagate(errp, local_err); ++ return; ++ } ++} ++ ++/* Common QMP interface for drive-backup and blockdev-backup */ ++static BlockJob *do_backup_common(BackupCommon *backup, ++ BlockDriverState *bs, ++ BlockDriverState *target_bs, ++ AioContext *aio_context, ++ JobTxn *txn, Error **errp) ++{ ++ BlockJob *job = NULL; ++ BdrvDirtyBitmap *bmap = NULL; ++ BackupPerf perf = { .max_workers = 64 }; ++ int job_flags = JOB_DEFAULT; ++ ++ if (!backup->has_speed) { ++ backup->speed = 0; ++ } ++ if (!backup->has_on_source_error) { ++ backup->on_source_error = BLOCKDEV_ON_ERROR_REPORT; ++ } ++ if (!backup->has_on_target_error) { ++ backup->on_target_error = BLOCKDEV_ON_ERROR_REPORT; ++ } ++ if (!backup->has_auto_finalize) { ++ backup->auto_finalize = true; ++ } ++ if (!backup->has_auto_dismiss) { ++ backup->auto_dismiss = true; ++ } ++ if (!backup->has_compress) { ++ backup->compress = false; ++ } ++ ++ if (backup->x_perf) { ++ if (backup->x_perf->has_use_copy_range) { ++ perf.use_copy_range = backup->x_perf->use_copy_range; ++ } ++ if (backup->x_perf->has_max_workers) { ++ perf.max_workers = backup->x_perf->max_workers; ++ } ++ if (backup->x_perf->has_max_chunk) { ++ perf.max_chunk = backup->x_perf->max_chunk; ++ } ++ } ++ ++ if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) || ++ (backup->sync == MIRROR_SYNC_MODE_INCREMENTAL)) { ++ /* done before desugaring 'incremental' to print the right message */ ++ if (!backup->bitmap) { ++ error_setg(errp, "must provide a valid bitmap name for " ++ "'%s' sync mode", MirrorSyncMode_str(backup->sync)); ++ return NULL; ++ } ++ } ++ ++ if (backup->sync == MIRROR_SYNC_MODE_INCREMENTAL) { ++ if (backup->has_bitmap_mode && ++ backup->bitmap_mode != BITMAP_SYNC_MODE_ON_SUCCESS) { ++ error_setg(errp, "Bitmap sync mode must be '%s' " ++ "when using sync mode '%s'", ++ BitmapSyncMode_str(BITMAP_SYNC_MODE_ON_SUCCESS), ++ MirrorSyncMode_str(backup->sync)); ++ return NULL; ++ } ++ backup->has_bitmap_mode = true; ++ backup->sync = MIRROR_SYNC_MODE_BITMAP; ++ backup->bitmap_mode = BITMAP_SYNC_MODE_ON_SUCCESS; ++ } ++ ++ if (backup->bitmap) { ++ bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap); ++ if (!bmap) { ++ error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap); ++ return NULL; ++ } ++ if (!backup->has_bitmap_mode) { ++ error_setg(errp, "Bitmap sync mode must be given " ++ "when providing a bitmap"); ++ return NULL; ++ } ++ if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_ALLOW_RO, errp)) { ++ return NULL; ++ } ++ ++ /* This does not produce a useful bitmap artifact: */ ++ if (backup->sync == MIRROR_SYNC_MODE_NONE) { ++ error_setg(errp, "sync mode '%s' does not produce meaningful bitmap" ++ " outputs", MirrorSyncMode_str(backup->sync)); ++ return NULL; ++ } ++ ++ /* If the bitmap isn't used for input or output, this is useless: */ ++ if (backup->bitmap_mode == BITMAP_SYNC_MODE_NEVER && ++ backup->sync != MIRROR_SYNC_MODE_BITMAP) { ++ error_setg(errp, "Bitmap sync mode '%s' has no meaningful effect" ++ " when combined with sync mode '%s'", ++ BitmapSyncMode_str(backup->bitmap_mode), ++ MirrorSyncMode_str(backup->sync)); ++ return NULL; ++ } ++ } ++ ++ if (!backup->bitmap && backup->has_bitmap_mode) { ++ error_setg(errp, "Cannot specify bitmap sync mode without a bitmap"); ++ return NULL; ++ } ++ ++ if (!backup->auto_finalize) { ++ job_flags |= JOB_MANUAL_FINALIZE; ++ } ++ if (!backup->auto_dismiss) { ++ job_flags |= JOB_MANUAL_DISMISS; ++ } ++ ++ job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, ++ backup->sync, bmap, backup->bitmap_mode, ++ backup->compress, backup->discard_source, ++ backup->filter_node_name, ++ &perf, ++ backup->on_source_error, ++ backup->on_target_error, ++ job_flags, NULL, NULL, txn, errp); ++ return job; ++} ++ ++void qmp_drive_backup(DriveBackup *backup, Error **errp) ++{ ++ TransactionAction action = { ++ .type = TRANSACTION_ACTION_KIND_DRIVE_BACKUP, ++ .u.drive_backup.data = backup, ++ }; ++ blockdev_do_action(&action, errp); ++} ++ ++BlockDeviceInfoList *qmp_query_named_block_nodes(bool has_flat, ++ bool flat, ++ Error **errp) ++{ ++ bool return_flat = has_flat && flat; ++ ++ return bdrv_named_nodes_list(return_flat, errp); ++} ++ ++XDbgBlockGraph *qmp_x_debug_query_block_graph(Error **errp) ++{ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ return bdrv_get_xdbg_block_graph(errp); ++} ++ ++void qmp_blockdev_backup(BlockdevBackup *backup, Error **errp) ++{ ++ TransactionAction action = { ++ .type = TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP, ++ .u.blockdev_backup.data = backup, ++ }; ++ blockdev_do_action(&action, errp); ++} ++ ++/* Parameter check and block job starting for drive mirroring. ++ * Caller should hold @device and @target's aio context (must be the same). ++ **/ ++static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *target, ++ const char *replaces, ++ enum MirrorSyncMode sync, ++ BlockMirrorBackingMode backing_mode, ++ bool zero_target, ++ bool has_speed, int64_t speed, ++ bool has_granularity, uint32_t granularity, ++ bool has_buf_size, int64_t buf_size, ++ bool has_on_source_error, ++ BlockdevOnError on_source_error, ++ bool has_on_target_error, ++ BlockdevOnError on_target_error, ++ bool has_unmap, bool unmap, ++ const char *filter_node_name, ++ bool has_copy_mode, MirrorCopyMode copy_mode, ++ bool has_auto_finalize, bool auto_finalize, ++ bool has_auto_dismiss, bool auto_dismiss, ++ Error **errp) ++{ ++ BlockDriverState *unfiltered_bs; ++ int job_flags = JOB_DEFAULT; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!has_speed) { ++ speed = 0; ++ } ++ if (!has_on_source_error) { ++ on_source_error = BLOCKDEV_ON_ERROR_REPORT; ++ } ++ if (!has_on_target_error) { ++ on_target_error = BLOCKDEV_ON_ERROR_REPORT; ++ } ++ if (!has_granularity) { ++ granularity = 0; ++ } ++ if (!has_buf_size) { ++ buf_size = 0; ++ } ++ if (!has_unmap) { ++ unmap = true; ++ } ++ if (!has_copy_mode) { ++ copy_mode = MIRROR_COPY_MODE_BACKGROUND; ++ } ++ if (has_auto_finalize && !auto_finalize) { ++ job_flags |= JOB_MANUAL_FINALIZE; ++ } ++ if (has_auto_dismiss && !auto_dismiss) { ++ job_flags |= JOB_MANUAL_DISMISS; ++ } ++ ++ if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", ++ "a value in range [512B, 64MB]"); ++ return; ++ } ++ if (granularity & (granularity - 1)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", ++ "a power of 2"); ++ return; ++ } ++ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) { ++ return; ++ } ++ if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_MIRROR_TARGET, errp)) { ++ return; ++ } ++ ++ if (!bdrv_backing_chain_next(bs) && sync == MIRROR_SYNC_MODE_TOP) { ++ sync = MIRROR_SYNC_MODE_FULL; ++ } ++ ++ if (!replaces) { ++ /* We want to mirror from @bs, but keep implicit filters on top */ ++ unfiltered_bs = bdrv_skip_implicit_filters(bs); ++ if (unfiltered_bs != bs) { ++ replaces = unfiltered_bs->node_name; ++ } ++ } ++ ++ if (replaces) { ++ BlockDriverState *to_replace_bs; ++ int64_t bs_size, replace_size; ++ ++ bs_size = bdrv_getlength(bs); ++ if (bs_size < 0) { ++ error_setg_errno(errp, -bs_size, "Failed to query device's size"); ++ return; ++ } ++ ++ to_replace_bs = check_to_replace_node(bs, replaces, errp); ++ if (!to_replace_bs) { ++ return; ++ } ++ ++ replace_size = bdrv_getlength(to_replace_bs); ++ ++ if (replace_size < 0) { ++ error_setg_errno(errp, -replace_size, ++ "Failed to query the replacement node's size"); ++ return; ++ } ++ if (bs_size != replace_size) { ++ error_setg(errp, "cannot replace image with a mirror image of " ++ "different size"); ++ return; ++ } ++ } ++ ++ /* pass the node name to replace to mirror start since it's loose coupling ++ * and will allow to check whether the node still exist at mirror completion ++ */ ++ mirror_start(job_id, bs, target, ++ replaces, job_flags, ++ speed, granularity, buf_size, sync, backing_mode, zero_target, ++ on_source_error, on_target_error, unmap, filter_node_name, ++ copy_mode, errp); ++} ++ ++void qmp_drive_mirror(DriveMirror *arg, Error **errp) ++{ ++ BlockDriverState *bs; ++ BlockDriverState *target_backing_bs, *target_bs; ++ AioContext *aio_context; ++ BlockMirrorBackingMode backing_mode; ++ Error *local_err = NULL; ++ QDict *options = NULL; ++ int flags; ++ int64_t size; ++ const char *format = arg->format; ++ bool zero_target; ++ int ret; ++ ++ bs = qmp_get_root_bs(arg->device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ /* Early check to avoid creating target */ ++ bdrv_graph_rdlock_main_loop(); ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) { ++ bdrv_graph_rdunlock_main_loop(); ++ return; ++ } ++ ++ aio_context = bdrv_get_aio_context(bs); ++ ++ if (!arg->has_mode) { ++ arg->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; ++ } ++ ++ if (!arg->format) { ++ format = (arg->mode == NEW_IMAGE_MODE_EXISTING ++ ? NULL : bs->drv->format_name); ++ } ++ ++ flags = bs->open_flags | BDRV_O_RDWR; ++ target_backing_bs = bdrv_cow_bs(bdrv_skip_filters(bs)); ++ if (!target_backing_bs && arg->sync == MIRROR_SYNC_MODE_TOP) { ++ arg->sync = MIRROR_SYNC_MODE_FULL; ++ } ++ if (arg->sync == MIRROR_SYNC_MODE_NONE) { ++ target_backing_bs = bs; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ size = bdrv_getlength(bs); ++ if (size < 0) { ++ error_setg_errno(errp, -size, "bdrv_getlength failed"); ++ return; ++ } ++ ++ if (arg->replaces) { ++ if (!arg->node_name) { ++ error_setg(errp, "a node-name must be provided when replacing a" ++ " named node of the graph"); ++ return; ++ } ++ } ++ ++ if (arg->mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) { ++ backing_mode = MIRROR_SOURCE_BACKING_CHAIN; ++ } else { ++ backing_mode = MIRROR_OPEN_BACKING_CHAIN; ++ } ++ ++ /* Don't open backing image in create() */ ++ flags |= BDRV_O_NO_BACKING; ++ ++ if ((arg->sync == MIRROR_SYNC_MODE_FULL || !target_backing_bs) ++ && arg->mode != NEW_IMAGE_MODE_EXISTING) ++ { ++ /* create new image w/o backing file */ ++ assert(format); ++ bdrv_img_create(arg->target, format, ++ NULL, NULL, NULL, size, flags, false, &local_err); ++ } else { ++ BlockDriverState *explicit_backing; ++ ++ switch (arg->mode) { ++ case NEW_IMAGE_MODE_EXISTING: ++ break; ++ case NEW_IMAGE_MODE_ABSOLUTE_PATHS: ++ /* ++ * Create new image with backing file. ++ * Implicit filters should not appear in the filename. ++ */ ++ bdrv_graph_rdlock_main_loop(); ++ explicit_backing = bdrv_skip_implicit_filters(target_backing_bs); ++ bdrv_refresh_filename(explicit_backing); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_img_create(arg->target, format, ++ explicit_backing->filename, ++ explicit_backing->drv->format_name, ++ NULL, size, flags, false, &local_err); ++ break; ++ default: ++ abort(); ++ } ++ } ++ ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ options = qdict_new(); ++ if (arg->node_name) { ++ qdict_put_str(options, "node-name", arg->node_name); ++ } ++ if (format) { ++ qdict_put_str(options, "driver", format); ++ } ++ ++ /* Mirroring takes care of copy-on-write using the source's backing ++ * file. ++ */ ++ target_bs = bdrv_open(arg->target, NULL, options, flags, errp); ++ if (!target_bs) { ++ return; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL && ++ (arg->mode == NEW_IMAGE_MODE_EXISTING || ++ !bdrv_has_zero_init(target_bs))); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ ++ ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); ++ if (ret < 0) { ++ bdrv_unref(target_bs); ++ return; ++ } ++ ++ blockdev_mirror_common(arg->job_id, bs, target_bs, ++ arg->replaces, arg->sync, ++ backing_mode, zero_target, ++ arg->has_speed, arg->speed, ++ arg->has_granularity, arg->granularity, ++ arg->has_buf_size, arg->buf_size, ++ arg->has_on_source_error, arg->on_source_error, ++ arg->has_on_target_error, arg->on_target_error, ++ arg->has_unmap, arg->unmap, ++ NULL, ++ arg->has_copy_mode, arg->copy_mode, ++ arg->has_auto_finalize, arg->auto_finalize, ++ arg->has_auto_dismiss, arg->auto_dismiss, ++ errp); ++ bdrv_unref(target_bs); ++} ++ ++void qmp_blockdev_mirror(const char *job_id, ++ const char *device, const char *target, ++ const char *replaces, ++ MirrorSyncMode sync, ++ bool has_speed, int64_t speed, ++ bool has_granularity, uint32_t granularity, ++ bool has_buf_size, int64_t buf_size, ++ bool has_on_source_error, ++ BlockdevOnError on_source_error, ++ bool has_on_target_error, ++ BlockdevOnError on_target_error, ++ const char *filter_node_name, ++ bool has_copy_mode, MirrorCopyMode copy_mode, ++ bool has_auto_finalize, bool auto_finalize, ++ bool has_auto_dismiss, bool auto_dismiss, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BlockDriverState *target_bs; ++ AioContext *aio_context; ++ BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN; ++ bool zero_target; ++ int ret; ++ ++ bs = qmp_get_root_bs(device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ target_bs = bdrv_lookup_bs(target, target, errp); ++ if (!target_bs) { ++ return; ++ } ++ ++ zero_target = (sync == MIRROR_SYNC_MODE_FULL); ++ ++ aio_context = bdrv_get_aio_context(bs); ++ ++ ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); ++ if (ret < 0) { ++ return; ++ } ++ ++ blockdev_mirror_common(job_id, bs, target_bs, ++ replaces, sync, backing_mode, ++ zero_target, has_speed, speed, ++ has_granularity, granularity, ++ has_buf_size, buf_size, ++ has_on_source_error, on_source_error, ++ has_on_target_error, on_target_error, ++ true, true, filter_node_name, ++ has_copy_mode, copy_mode, ++ has_auto_finalize, auto_finalize, ++ has_auto_dismiss, auto_dismiss, ++ errp); ++} ++ ++/* ++ * Get a block job using its ID. Called with job_mutex held. ++ */ ++static BlockJob *find_block_job_locked(const char *id, Error **errp) ++{ ++ BlockJob *job; ++ ++ assert(id != NULL); ++ ++ job = block_job_get_locked(id); ++ ++ if (!job) { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE, ++ "Block job '%s' not found", id); ++ return NULL; ++ } ++ ++ return job; ++} ++ ++void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(device, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ block_job_set_speed_locked(job, speed, errp); ++} ++ ++void qmp_block_job_cancel(const char *device, ++ bool has_force, bool force, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(device, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ if (!has_force) { ++ force = false; ++ } ++ ++ if (job_user_paused_locked(&job->job) && !force) { ++ error_setg(errp, "The block job for device '%s' is currently paused", ++ device); ++ return; ++ } ++ ++ trace_qmp_block_job_cancel(job); ++ job_user_cancel_locked(&job->job, force, errp); ++} ++ ++void qmp_block_job_pause(const char *device, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(device, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_block_job_pause(job); ++ job_user_pause_locked(&job->job, errp); ++} ++ ++void qmp_block_job_resume(const char *device, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(device, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_block_job_resume(job); ++ job_user_resume_locked(&job->job, errp); ++} ++ ++void qmp_block_job_complete(const char *device, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(device, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_block_job_complete(job); ++ job_complete_locked(&job->job, errp); ++} ++ ++void qmp_block_job_finalize(const char *id, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_block_job_finalize(job); ++ job_ref_locked(&job->job); ++ job_finalize_locked(&job->job, errp); ++ ++ job_unref_locked(&job->job); ++} ++ ++void qmp_block_job_dismiss(const char *id, Error **errp) ++{ ++ BlockJob *bjob; ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ bjob = find_block_job_locked(id, errp); ++ ++ if (!bjob) { ++ return; ++ } ++ ++ trace_qmp_block_job_dismiss(bjob); ++ job = &bjob->job; ++ job_dismiss_locked(&job, errp); ++} ++ ++void qmp_block_job_change(BlockJobChangeOptions *opts, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(opts->id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ block_job_change_locked(job, opts, errp); ++} ++ ++void qmp_change_backing_file(const char *device, ++ const char *image_node_name, ++ const char *backing_file, ++ Error **errp) ++{ ++ BlockDriverState *bs = NULL; ++ BlockDriverState *image_bs = NULL; ++ Error *local_err = NULL; ++ bool ro; ++ int ret; ++ ++ bs = qmp_get_root_bs(device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ ++ image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ goto out_rdlock; ++ } ++ ++ if (!image_bs) { ++ error_setg(errp, "image file not found"); ++ goto out_rdlock; ++ } ++ ++ if (bdrv_find_base(image_bs) == image_bs) { ++ error_setg(errp, "not allowing backing file change on an image " ++ "without a backing file"); ++ goto out_rdlock; ++ } ++ ++ /* even though we are not necessarily operating on bs, we need it to ++ * determine if block ops are currently prohibited on the chain */ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_CHANGE, errp)) { ++ goto out_rdlock; ++ } ++ ++ /* final sanity check */ ++ if (!bdrv_chain_contains(bs, image_bs)) { ++ error_setg(errp, "'%s' and image file are not in the same chain", ++ device); ++ goto out_rdlock; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ /* if not r/w, reopen to make r/w */ ++ ro = bdrv_is_read_only(image_bs); ++ ++ if (ro) { ++ if (bdrv_reopen_set_read_only(image_bs, false, errp) != 0) { ++ return; ++ } ++ } ++ ++ ret = bdrv_change_backing_file(image_bs, backing_file, ++ image_bs->drv ? image_bs->drv->format_name : "", ++ false); ++ ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not change backing file to '%s'", ++ backing_file); ++ /* don't exit here, so we can try to restore open flags if ++ * appropriate */ ++ } ++ ++ if (ro) { ++ bdrv_reopen_set_read_only(image_bs, true, errp); ++ } ++ return; ++ ++out_rdlock: ++ bdrv_graph_rdunlock_main_loop(); ++} ++ ++void qmp_blockdev_add(BlockdevOptions *options, Error **errp) ++{ ++ BlockDriverState *bs; ++ QObject *obj; ++ Visitor *v = qobject_output_visitor_new(&obj); ++ QDict *qdict; ++ ++ visit_type_BlockdevOptions(v, NULL, &options, &error_abort); ++ visit_complete(v, &obj); ++ qdict = qobject_to(QDict, obj); ++ ++ qdict_flatten(qdict); ++ ++ if (!qdict_get_try_str(qdict, "node-name")) { ++ error_setg(errp, "'node-name' must be specified for the root node"); ++ goto fail; ++ } ++ ++ bs = bds_tree_init(qdict, errp); ++ if (!bs) { ++ goto fail; ++ } ++ ++ bdrv_set_monitor_owned(bs); ++ ++fail: ++ visit_free(v); ++} ++ ++void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) ++{ ++ BlockReopenQueue *queue = NULL; ++ ++ /* Add each one of the BDS that we want to reopen to the queue */ ++ for (; reopen_list != NULL; reopen_list = reopen_list->next) { ++ BlockdevOptions *options = reopen_list->value; ++ BlockDriverState *bs; ++ QObject *obj; ++ Visitor *v; ++ QDict *qdict; ++ ++ /* Check for the selected node name */ ++ if (!options->node_name) { ++ error_setg(errp, "node-name not specified"); ++ goto fail; ++ } ++ ++ bs = bdrv_find_node(options->node_name); ++ if (!bs) { ++ error_setg(errp, "Failed to find node with node-name='%s'", ++ options->node_name); ++ goto fail; ++ } ++ ++ /* Put all options in a QDict and flatten it */ ++ v = qobject_output_visitor_new(&obj); ++ visit_type_BlockdevOptions(v, NULL, &options, &error_abort); ++ visit_complete(v, &obj); ++ visit_free(v); ++ ++ qdict = qobject_to(QDict, obj); ++ ++ qdict_flatten(qdict); ++ ++ queue = bdrv_reopen_queue(queue, bs, qdict, false); ++ } ++ ++ /* Perform the reopen operation */ ++ bdrv_reopen_multiple(queue, errp); ++ queue = NULL; ++ ++fail: ++ bdrv_reopen_queue_free(queue); ++} ++ ++void qmp_blockdev_del(const char *node_name, Error **errp) ++{ ++ BlockDriverState *bs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ bs = bdrv_find_node(node_name); ++ if (!bs) { ++ error_setg(errp, "Failed to find node with node-name='%s'", node_name); ++ return; ++ } ++ if (bdrv_has_blk(bs)) { ++ error_setg(errp, "Node %s is in use", node_name); ++ return; ++ } ++ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, errp)) { ++ return; ++ } ++ ++ if (!QTAILQ_IN_USE(bs, monitor_list)) { ++ error_setg(errp, "Node %s is not owned by the monitor", ++ bs->node_name); ++ return; ++ } ++ ++ if (bs->refcnt > 1) { ++ error_setg(errp, "Block device %s is in use", ++ bdrv_get_device_or_node_name(bs)); ++ return; ++ } ++ ++ QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list); ++ bdrv_unref(bs); ++} ++ ++static BdrvChild * GRAPH_RDLOCK ++bdrv_find_child(BlockDriverState *parent_bs, const char *child_name) ++{ ++ BdrvChild *child; ++ ++ QLIST_FOREACH(child, &parent_bs->children, next) { ++ if (strcmp(child->name, child_name) == 0) { ++ return child; ++ } ++ } ++ ++ return NULL; ++} ++ ++void qmp_x_blockdev_change(const char *parent, const char *child, ++ const char *node, Error **errp) ++{ ++ BlockDriverState *parent_bs, *new_bs = NULL; ++ BdrvChild *p_child; ++ ++ bdrv_graph_wrlock(); ++ ++ parent_bs = bdrv_lookup_bs(parent, parent, errp); ++ if (!parent_bs) { ++ goto out; ++ } ++ ++ if (!child == !node) { ++ if (child) { ++ error_setg(errp, "The parameters child and node are in conflict"); ++ } else { ++ error_setg(errp, "Either child or node must be specified"); ++ } ++ goto out; ++ } ++ ++ if (child) { ++ p_child = bdrv_find_child(parent_bs, child); ++ if (!p_child) { ++ error_setg(errp, "Node '%s' does not have child '%s'", ++ parent, child); ++ goto out; ++ } ++ bdrv_del_child(parent_bs, p_child, errp); ++ } ++ ++ if (node) { ++ new_bs = bdrv_find_node(node); ++ if (!new_bs) { ++ error_setg(errp, "Node '%s' not found", node); ++ goto out; ++ } ++ bdrv_add_child(parent_bs, new_bs, errp); ++ } ++ ++out: ++ bdrv_graph_wrunlock(); ++} ++ ++BlockJobInfoList *qmp_query_block_jobs(Error **errp) ++{ ++ BlockJobInfoList *head = NULL, **tail = &head; ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ ++ for (job = block_job_next_locked(NULL); job; ++ job = block_job_next_locked(job)) { ++ BlockJobInfo *value; ++ ++ if (block_job_is_internal(job)) { ++ continue; ++ } ++ value = block_job_query_locked(job, errp); ++ if (!value) { ++ qapi_free_BlockJobInfoList(head); ++ return NULL; ++ } ++ QAPI_LIST_APPEND(tail, value); ++ } ++ ++ return head; ++} ++ ++void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, ++ bool has_force, bool force, Error **errp) ++{ ++ AioContext *new_context; ++ BlockDriverState *bs; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ bs = bdrv_find_node(node_name); ++ if (!bs) { ++ error_setg(errp, "Failed to find node with node-name='%s'", node_name); ++ return; ++ } ++ ++ /* Protects against accidents. */ ++ if (!(has_force && force) && bdrv_has_blk(bs)) { ++ error_setg(errp, "Node %s is associated with a BlockBackend and could " ++ "be in use (use force=true to override this check)", ++ node_name); ++ return; ++ } ++ ++ if (iothread->type == QTYPE_QSTRING) { ++ IOThread *obj = iothread_by_id(iothread->u.s); ++ if (!obj) { ++ error_setg(errp, "Cannot find iothread %s", iothread->u.s); ++ return; ++ } ++ ++ new_context = iothread_get_aio_context(obj); ++ } else { ++ new_context = qemu_get_aio_context(); ++ } ++ ++ bdrv_try_change_aio_context(bs, new_context, NULL, errp); ++} ++ ++QemuOptsList qemu_common_drive_opts = { ++ .name = "drive", ++ .head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head), ++ .desc = { ++ { ++ .name = "snapshot", ++ .type = QEMU_OPT_BOOL, ++ .help = "enable/disable snapshot mode", ++ },{ ++ .name = "aio", ++ .type = QEMU_OPT_STRING, ++ .help = "host AIO implementation (threads, native, io_uring)", ++ },{ ++ .name = BDRV_OPT_CACHE_WB, ++ .type = QEMU_OPT_BOOL, ++ .help = "Enable writeback mode", ++ },{ ++ .name = "format", ++ .type = QEMU_OPT_STRING, ++ .help = "disk format (raw, qcow2, ...)", ++ },{ ++ .name = "rerror", ++ .type = QEMU_OPT_STRING, ++ .help = "read error action", ++ },{ ++ .name = "werror", ++ .type = QEMU_OPT_STRING, ++ .help = "write error action", ++ },{ ++ .name = BDRV_OPT_READ_ONLY, ++ .type = QEMU_OPT_BOOL, ++ .help = "open drive file as read-only", ++ }, ++ ++ THROTTLE_OPTS, ++ ++ { ++ .name = "throttling.group", ++ .type = QEMU_OPT_STRING, ++ .help = "name of the block throttling group", ++ },{ ++ .name = "copy-on-read", ++ .type = QEMU_OPT_BOOL, ++ .help = "copy read data from backing file into image file", ++ },{ ++ .name = "detect-zeroes", ++ .type = QEMU_OPT_STRING, ++ .help = "try to optimize zero writes (off, on, unmap)", ++ },{ ++ .name = "stats-account-invalid", ++ .type = QEMU_OPT_BOOL, ++ .help = "whether to account for invalid I/O operations " ++ "in the statistics", ++ },{ ++ .name = "stats-account-failed", ++ .type = QEMU_OPT_BOOL, ++ .help = "whether to account for failed I/O operations " ++ "in the statistics", ++ }, ++ { /* end of list */ } ++ }, ++}; ++ ++QemuOptsList qemu_drive_opts = { ++ .name = "drive", ++ .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head), ++ .desc = { ++ /* ++ * no elements => accept any params ++ * validation will happen later ++ */ ++ { /* end of list */ } ++ }, ++}; +diff --git a/qcow2/lib/blockjob.c b/qcow2/lib/blockjob.c +new file mode 100644 +index 00000000..d5f29e14 +--- /dev/null ++++ b/qcow2/lib/blockjob.c +@@ -0,0 +1,630 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/aio-wait.h" ++#include "block/block.h" ++#include "block/blockjob_int.h" ++#include "block/block_int.h" ++#include "block/trace.h" ++#include "sysemu/block-backend.h" ++#include "qapi/error.h" ++#include "qapi/qapi-events-block-core.h" ++#include "qapi/qmp/qerror.h" ++#include "qemu/main-loop.h" ++#include "qemu/timer.h" ++ ++static bool is_block_job(Job *job) ++{ ++ return job_type(job) == JOB_TYPE_BACKUP || ++ job_type(job) == JOB_TYPE_COMMIT || ++ job_type(job) == JOB_TYPE_MIRROR || ++ job_type(job) == JOB_TYPE_STREAM; ++} ++ ++BlockJob *block_job_next_locked(BlockJob *bjob) ++{ ++ Job *job = bjob ? &bjob->job : NULL; ++ GLOBAL_STATE_CODE(); ++ ++ do { ++ job = job_next_locked(job); ++ } while (job && !is_block_job(job)); ++ ++ return job ? container_of(job, BlockJob, job) : NULL; ++} ++ ++BlockJob *block_job_get_locked(const char *id) ++{ ++ Job *job = job_get_locked(id); ++ GLOBAL_STATE_CODE(); ++ ++ if (job && is_block_job(job)) { ++ return container_of(job, BlockJob, job); ++ } else { ++ return NULL; ++ } ++} ++ ++BlockJob *block_job_get(const char *id) ++{ ++ JOB_LOCK_GUARD(); ++ return block_job_get_locked(id); ++} ++ ++void block_job_free(Job *job) ++{ ++ BlockJob *bjob = container_of(job, BlockJob, job); ++ GLOBAL_STATE_CODE(); ++ ++ block_job_remove_all_bdrv(bjob); ++ ratelimit_destroy(&bjob->limit); ++ error_free(bjob->blocker); ++} ++ ++static char *child_job_get_parent_desc(BdrvChild *c) ++{ ++ BlockJob *job = c->opaque; ++ return g_strdup_printf("%s job '%s'", job_type_str(&job->job), job->job.id); ++} ++ ++static void child_job_drained_begin(BdrvChild *c) ++{ ++ BlockJob *job = c->opaque; ++ job_pause(&job->job); ++} ++ ++static bool child_job_drained_poll(BdrvChild *c) ++{ ++ BlockJob *bjob = c->opaque; ++ Job *job = &bjob->job; ++ const BlockJobDriver *drv = block_job_driver(bjob); ++ ++ /* An inactive or completed job doesn't have any pending requests. Jobs ++ * with !job->busy are either already paused or have a pause point after ++ * being reentered, so no job driver code will run before they pause. */ ++ WITH_JOB_LOCK_GUARD() { ++ if (!job->busy || job_is_completed_locked(job)) { ++ return false; ++ } ++ } ++ ++ /* Otherwise, assume that it isn't fully stopped yet, but allow the job to ++ * override this assumption. */ ++ if (drv->drained_poll) { ++ return drv->drained_poll(bjob); ++ } else { ++ return true; ++ } ++} ++ ++static void child_job_drained_end(BdrvChild *c) ++{ ++ BlockJob *job = c->opaque; ++ job_resume(&job->job); ++} ++ ++typedef struct BdrvStateChildJobContext { ++ AioContext *new_ctx; ++ BlockJob *job; ++} BdrvStateChildJobContext; ++ ++static void child_job_set_aio_ctx_commit(void *opaque) ++{ ++ BdrvStateChildJobContext *s = opaque; ++ BlockJob *job = s->job; ++ ++ job_set_aio_context(&job->job, s->new_ctx); ++} ++ ++static TransactionActionDrv change_child_job_context = { ++ .commit = child_job_set_aio_ctx_commit, ++ .clean = g_free, ++}; ++ ++static bool child_job_change_aio_ctx(BdrvChild *c, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp) ++{ ++ BlockJob *job = c->opaque; ++ BdrvStateChildJobContext *s; ++ GSList *l; ++ ++ for (l = job->nodes; l; l = l->next) { ++ BdrvChild *sibling = l->data; ++ if (!bdrv_child_change_aio_context(sibling, ctx, visited, ++ tran, errp)) { ++ return false; ++ } ++ } ++ ++ s = g_new(BdrvStateChildJobContext, 1); ++ *s = (BdrvStateChildJobContext) { ++ .new_ctx = ctx, ++ .job = job, ++ }; ++ ++ tran_add(tran, &change_child_job_context, s); ++ return true; ++} ++ ++static AioContext *child_job_get_parent_aio_context(BdrvChild *c) ++{ ++ BlockJob *job = c->opaque; ++ IO_CODE(); ++ JOB_LOCK_GUARD(); ++ ++ return job->job.aio_context; ++} ++ ++static const BdrvChildClass child_job = { ++ .get_parent_desc = child_job_get_parent_desc, ++ .drained_begin = child_job_drained_begin, ++ .drained_poll = child_job_drained_poll, ++ .drained_end = child_job_drained_end, ++ .change_aio_ctx = child_job_change_aio_ctx, ++ .stay_at_node = true, ++ .get_parent_aio_context = child_job_get_parent_aio_context, ++}; ++ ++void block_job_remove_all_bdrv(BlockJob *job) ++{ ++ GLOBAL_STATE_CODE(); ++ /* ++ * bdrv_root_unref_child() may reach child_job_[can_]set_aio_ctx(), ++ * which will also traverse job->nodes, so consume the list one by ++ * one to make sure that such a concurrent access does not attempt ++ * to process an already freed BdrvChild. ++ */ ++ bdrv_graph_wrlock(); ++ while (job->nodes) { ++ GSList *l = job->nodes; ++ BdrvChild *c = l->data; ++ ++ job->nodes = l->next; ++ ++ bdrv_op_unblock_all(c->bs, job->blocker); ++ bdrv_root_unref_child(c); ++ ++ g_slist_free_1(l); ++ } ++ bdrv_graph_wrunlock(); ++} ++ ++bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) ++{ ++ GSList *el; ++ GLOBAL_STATE_CODE(); ++ ++ for (el = job->nodes; el; el = el->next) { ++ BdrvChild *c = el->data; ++ if (c->bs == bs) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, ++ uint64_t perm, uint64_t shared_perm, Error **errp) ++{ ++ BdrvChild *c; ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_ref(bs); ++ ++ c = bdrv_root_attach_child(bs, name, &child_job, 0, perm, shared_perm, job, ++ errp); ++ if (c == NULL) { ++ return -EPERM; ++ } ++ ++ job->nodes = g_slist_prepend(job->nodes, c); ++ bdrv_op_block_all(bs, job->blocker); ++ ++ return 0; ++} ++ ++/* Called with job_mutex lock held. */ ++static void block_job_on_idle_locked(Notifier *n, void *opaque) ++{ ++ aio_wait_kick(); ++} ++ ++bool block_job_is_internal(BlockJob *job) ++{ ++ return (job->job.id == NULL); ++} ++ ++const BlockJobDriver *block_job_driver(BlockJob *job) ++{ ++ return container_of(job->job.driver, BlockJobDriver, job_driver); ++} ++ ++/* Assumes the job_mutex is held */ ++static bool job_timer_pending(Job *job) ++{ ++ return timer_pending(&job->sleep_timer); ++} ++ ++bool block_job_set_speed_locked(BlockJob *job, int64_t speed, Error **errp) ++{ ++ const BlockJobDriver *drv = block_job_driver(job); ++ int64_t old_speed = job->speed; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (job_apply_verb_locked(&job->job, JOB_VERB_SET_SPEED, errp) < 0) { ++ return false; ++ } ++ if (speed < 0) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "speed", ++ "a non-negative value"); ++ return false; ++ } ++ ++ ratelimit_set_speed(&job->limit, speed, BLOCK_JOB_SLICE_TIME); ++ ++ job->speed = speed; ++ ++ if (drv->set_speed) { ++ job_unlock(); ++ drv->set_speed(job, speed); ++ job_lock(); ++ } ++ ++ if (speed && speed <= old_speed) { ++ return true; ++ } ++ ++ /* kick only if a timer is pending */ ++ job_enter_cond_locked(&job->job, job_timer_pending); ++ ++ return true; ++} ++ ++static bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) ++{ ++ JOB_LOCK_GUARD(); ++ return block_job_set_speed_locked(job, speed, errp); ++} ++ ++void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, ++ Error **errp) ++{ ++ const BlockJobDriver *drv = block_job_driver(job); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (job_apply_verb_locked(&job->job, JOB_VERB_CHANGE, errp)) { ++ return; ++ } ++ ++ if (drv->change) { ++ job_unlock(); ++ drv->change(job, opts, errp); ++ job_lock(); ++ } else { ++ error_setg(errp, "Job type does not support change"); ++ } ++} ++ ++void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n) ++{ ++ IO_CODE(); ++ ratelimit_calculate_delay(&job->limit, n); ++} ++ ++void block_job_ratelimit_sleep(BlockJob *job) ++{ ++ uint64_t delay_ns; ++ ++ /* ++ * Sleep at least once. If the job is reentered early, keep waiting until ++ * we've waited for the full time that is necessary to keep the job at the ++ * right speed. ++ * ++ * Make sure to recalculate the delay after each (possibly interrupted) ++ * sleep because the speed can change while the job has yielded. ++ */ ++ do { ++ delay_ns = ratelimit_calculate_delay(&job->limit, 0); ++ job_sleep_ns(&job->job, delay_ns); ++ } while (delay_ns && !job_is_cancelled(&job->job)); ++} ++ ++BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp) ++{ ++ BlockJobInfo *info; ++ uint64_t progress_current, progress_total; ++ const BlockJobDriver *drv = block_job_driver(job); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (block_job_is_internal(job)) { ++ error_setg(errp, "Cannot query QEMU internal jobs"); ++ return NULL; ++ } ++ ++ progress_get_snapshot(&job->job.progress, &progress_current, ++ &progress_total); ++ ++ info = g_new0(BlockJobInfo, 1); ++ info->type = job_type(&job->job); ++ info->device = g_strdup(job->job.id); ++ info->busy = job->job.busy; ++ info->paused = job->job.pause_count > 0; ++ info->offset = progress_current; ++ info->len = progress_total; ++ info->speed = job->speed; ++ info->io_status = job->iostatus; ++ info->ready = job_is_ready_locked(&job->job), ++ info->status = job->job.status; ++ info->auto_finalize = job->job.auto_finalize; ++ info->auto_dismiss = job->job.auto_dismiss; ++ if (job->job.ret) { ++ info->error = job->job.err ? ++ g_strdup(error_get_pretty(job->job.err)) : ++ g_strdup(strerror(-job->job.ret)); ++ } ++ if (drv->query) { ++ job_unlock(); ++ drv->query(job, info); ++ job_lock(); ++ } ++ return info; ++} ++ ++/* Called with job lock held */ ++static void block_job_iostatus_set_err_locked(BlockJob *job, int error) ++{ ++ if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { ++ job->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : ++ BLOCK_DEVICE_IO_STATUS_FAILED; ++ } ++} ++ ++/* Called with job_mutex lock held. */ ++static void block_job_event_cancelled_locked(Notifier *n, void *opaque) ++{ ++ BlockJob *job = opaque; ++ uint64_t progress_current, progress_total; ++ ++ if (block_job_is_internal(job)) { ++ return; ++ } ++ ++ progress_get_snapshot(&job->job.progress, &progress_current, ++ &progress_total); ++ ++ qapi_event_send_block_job_cancelled(job_type(&job->job), ++ job->job.id, ++ progress_total, ++ progress_current, ++ job->speed); ++} ++ ++/* Called with job_mutex lock held. */ ++static void block_job_event_completed_locked(Notifier *n, void *opaque) ++{ ++ BlockJob *job = opaque; ++ const char *msg = NULL; ++ uint64_t progress_current, progress_total; ++ ++ if (block_job_is_internal(job)) { ++ return; ++ } ++ ++ if (job->job.ret < 0) { ++ msg = error_get_pretty(job->job.err); ++ } ++ ++ progress_get_snapshot(&job->job.progress, &progress_current, ++ &progress_total); ++ ++ qapi_event_send_block_job_completed(job_type(&job->job), ++ job->job.id, ++ progress_total, ++ progress_current, ++ job->speed, ++ msg); ++} ++ ++/* Called with job_mutex lock held. */ ++static void block_job_event_pending_locked(Notifier *n, void *opaque) ++{ ++ BlockJob *job = opaque; ++ ++ if (block_job_is_internal(job)) { ++ return; ++ } ++ ++ qapi_event_send_block_job_pending(job_type(&job->job), ++ job->job.id); ++} ++ ++/* Called with job_mutex lock held. */ ++static void block_job_event_ready_locked(Notifier *n, void *opaque) ++{ ++ BlockJob *job = opaque; ++ uint64_t progress_current, progress_total; ++ ++ if (block_job_is_internal(job)) { ++ return; ++ } ++ ++ progress_get_snapshot(&job->job.progress, &progress_current, ++ &progress_total); ++ ++ qapi_event_send_block_job_ready(job_type(&job->job), ++ job->job.id, ++ progress_total, ++ progress_current, ++ job->speed); ++} ++ ++ ++void *block_job_create(const char *job_id, const BlockJobDriver *driver, ++ JobTxn *txn, BlockDriverState *bs, uint64_t perm, ++ uint64_t shared_perm, int64_t speed, int flags, ++ BlockCompletionFunc *cb, void *opaque, Error **errp) ++{ ++ BlockJob *job; ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_graph_wrlock(); ++ ++ if (job_id == NULL && !(flags & JOB_INTERNAL)) { ++ job_id = bdrv_get_device_name(bs); ++ } ++ ++ job = job_create(job_id, &driver->job_driver, txn, bdrv_get_aio_context(bs), ++ flags, cb, opaque, errp); ++ if (job == NULL) { ++ bdrv_graph_wrunlock(); ++ return NULL; ++ } ++ ++ assert(is_block_job(&job->job)); ++ assert(job->job.driver->free == &block_job_free); ++ assert(job->job.driver->user_resume == &block_job_user_resume); ++ ++ ratelimit_init(&job->limit); ++ ++ job->finalize_cancelled_notifier.notify = block_job_event_cancelled_locked; ++ job->finalize_completed_notifier.notify = block_job_event_completed_locked; ++ job->pending_notifier.notify = block_job_event_pending_locked; ++ job->ready_notifier.notify = block_job_event_ready_locked; ++ job->idle_notifier.notify = block_job_on_idle_locked; ++ ++ WITH_JOB_LOCK_GUARD() { ++ notifier_list_add(&job->job.on_finalize_cancelled, ++ &job->finalize_cancelled_notifier); ++ notifier_list_add(&job->job.on_finalize_completed, ++ &job->finalize_completed_notifier); ++ notifier_list_add(&job->job.on_pending, &job->pending_notifier); ++ notifier_list_add(&job->job.on_ready, &job->ready_notifier); ++ notifier_list_add(&job->job.on_idle, &job->idle_notifier); ++ } ++ ++ error_setg(&job->blocker, "block device is in use by block job: %s", ++ job_type_str(&job->job)); ++ ++ ret = block_job_add_bdrv(job, "main node", bs, perm, shared_perm, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); ++ ++ if (!block_job_set_speed(job, speed, errp)) { ++ goto fail; ++ } ++ ++ bdrv_graph_wrunlock(); ++ return job; ++ ++fail: ++ bdrv_graph_wrunlock(); ++ job_early_fail(&job->job); ++ return NULL; ++} ++ ++void block_job_iostatus_reset_locked(BlockJob *job) ++{ ++ GLOBAL_STATE_CODE(); ++ if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { ++ return; ++ } ++ assert(job->job.user_paused && job->job.pause_count > 0); ++ job->iostatus = BLOCK_DEVICE_IO_STATUS_OK; ++} ++ ++static void block_job_iostatus_reset(BlockJob *job) ++{ ++ JOB_LOCK_GUARD(); ++ block_job_iostatus_reset_locked(job); ++} ++ ++void block_job_user_resume(Job *job) ++{ ++ BlockJob *bjob = container_of(job, BlockJob, job); ++ GLOBAL_STATE_CODE(); ++ block_job_iostatus_reset(bjob); ++} ++ ++BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, ++ int is_read, int error) ++{ ++ BlockErrorAction action; ++ IO_CODE(); ++ ++ switch (on_err) { ++ case BLOCKDEV_ON_ERROR_ENOSPC: ++ case BLOCKDEV_ON_ERROR_AUTO: ++ action = (error == ENOSPC) ? ++ BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT; ++ break; ++ case BLOCKDEV_ON_ERROR_STOP: ++ action = BLOCK_ERROR_ACTION_STOP; ++ break; ++ case BLOCKDEV_ON_ERROR_REPORT: ++ action = BLOCK_ERROR_ACTION_REPORT; ++ break; ++ case BLOCKDEV_ON_ERROR_IGNORE: ++ action = BLOCK_ERROR_ACTION_IGNORE; ++ break; ++ default: ++ abort(); ++ } ++ if (!block_job_is_internal(job)) { ++ qapi_event_send_block_job_error(job->job.id, ++ is_read ? IO_OPERATION_TYPE_READ : ++ IO_OPERATION_TYPE_WRITE, ++ action); ++ } ++ if (action == BLOCK_ERROR_ACTION_STOP) { ++ WITH_JOB_LOCK_GUARD() { ++ if (!job->job.user_paused) { ++ job_pause_locked(&job->job); ++ /* ++ * make the pause user visible, which will be ++ * resumed from QMP. ++ */ ++ job->job.user_paused = true; ++ } ++ block_job_iostatus_set_err_locked(job, error); ++ } ++ } ++ return action; ++} ++ ++AioContext *block_job_get_aio_context(BlockJob *job) ++{ ++ GLOBAL_STATE_CODE(); ++ return job->job.aio_context; ++} +diff --git a/qcow2/lib/hw/block/block.c b/qcow2/lib/hw/block/block.c +new file mode 100644 +index 00000000..3ceca7dc +--- /dev/null ++++ b/qcow2/lib/hw/block/block.c +@@ -0,0 +1,278 @@ ++/* ++ * Common code for block device models ++ * ++ * Copyright (C) 2012 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/block_int-common.h" ++#include "sysemu/blockdev.h" ++#include "sysemu/block-backend.h" ++#include "hw/block/block.h" ++#include "qapi/error.h" ++#include "qapi/qapi-types-block.h" ++ ++/* ++ * Read the non-zeroes parts of @blk into @buf ++ * Reading all of the @blk is expensive if the zeroes parts of @blk ++ * is large enough. Therefore check the block status and only write ++ * the non-zeroes block into @buf. ++ * ++ * Return 0 on success, non-zero on error. ++ */ ++static int blk_pread_nonzeroes(BlockBackend *blk, hwaddr size, void *buf) ++{ ++ int ret; ++ int64_t bytes, offset = 0; ++ BlockDriverState *bs = blk_bs(blk); ++ ++ for (;;) { ++ bytes = MIN(size - offset, BDRV_REQUEST_MAX_BYTES); ++ if (bytes <= 0) { ++ return 0; ++ } ++ ret = bdrv_block_status(bs, offset, bytes, &bytes, NULL, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ if (!(ret & BDRV_BLOCK_ZERO)) { ++ ret = blk_pread(blk, offset, bytes, (uint8_t *) buf + offset, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ offset += bytes; ++ } ++} ++ ++/* ++ * Read the entire contents of @blk into @buf. ++ * @blk's contents must be @size bytes, and @size must be at most ++ * BDRV_REQUEST_MAX_BYTES. ++ * On success, return true. ++ * On failure, store an error through @errp and return false. ++ * ++ * This function not intended for actual block devices, which read on ++ * demand. It's for things like memory devices that (ab)use a block ++ * backend to provide persistence. ++ */ ++bool blk_check_size_and_read_all(BlockBackend *blk, DeviceState *dev, ++ void *buf, hwaddr size, Error **errp) ++{ ++ int64_t blk_len; ++ int ret; ++ g_autofree char *dev_id = NULL; ++ ++ blk_len = blk_getlength(blk); ++ if (blk_len < 0) { ++ error_setg_errno(errp, -blk_len, ++ "can't get size of %s block backend", blk_name(blk)); ++ return false; ++ } ++ if (blk_len != size) { ++ dev_id = qdev_get_human_name(dev); ++ error_setg(errp, "%s device '%s' requires %" HWADDR_PRIu ++ " bytes, %s block backend provides %" PRIu64 " bytes", ++ object_get_typename(OBJECT(dev)), dev_id, size, ++ blk_name(blk), blk_len); ++ return false; ++ } ++ ++ /* ++ * We could loop for @size > BDRV_REQUEST_MAX_BYTES, but if we ++ * ever get to the point we want to read *gigabytes* here, we ++ * should probably rework the device to be more like an actual ++ * block device and read only on demand. ++ */ ++ assert(size <= BDRV_REQUEST_MAX_BYTES); ++ ret = blk_pread_nonzeroes(blk, size, buf); ++ if (ret < 0) { ++ dev_id = qdev_get_human_name(dev); ++ error_setg_errno(errp, -ret, "can't read %s block backend" ++ " for %s device '%s'", ++ blk_name(blk), object_get_typename(OBJECT(dev)), ++ dev_id); ++ return false; ++ } ++ return true; ++} ++ ++bool blkconf_blocksizes(BlockConf *conf, Error **errp) ++{ ++ BlockBackend *blk = conf->blk; ++ BlockSizes blocksizes; ++ BlockDriverState *bs; ++ bool use_blocksizes; ++ bool use_bs; ++ ++ switch (conf->backend_defaults) { ++ case ON_OFF_AUTO_AUTO: ++ use_blocksizes = !blk_probe_blocksizes(blk, &blocksizes); ++ use_bs = false; ++ break; ++ ++ case ON_OFF_AUTO_ON: ++ use_blocksizes = !blk_probe_blocksizes(blk, &blocksizes); ++ bs = blk_bs(blk); ++ use_bs = bs; ++ break; ++ ++ case ON_OFF_AUTO_OFF: ++ use_blocksizes = false; ++ use_bs = false; ++ break; ++ ++ default: ++ abort(); ++ } ++ ++ /* fill in detected values if they are not defined via qemu command line */ ++ if (!conf->physical_block_size) { ++ if (use_blocksizes) { ++ conf->physical_block_size = blocksizes.phys; ++ } else { ++ conf->physical_block_size = BDRV_SECTOR_SIZE; ++ } ++ } ++ if (!conf->logical_block_size) { ++ if (use_blocksizes) { ++ conf->logical_block_size = blocksizes.log; ++ } else { ++ conf->logical_block_size = BDRV_SECTOR_SIZE; ++ } ++ } ++ if (use_bs) { ++ if (!conf->opt_io_size) { ++ conf->opt_io_size = bs->bl.opt_transfer; ++ } ++ if (conf->discard_granularity == -1) { ++ if (bs->bl.pdiscard_alignment) { ++ conf->discard_granularity = bs->bl.pdiscard_alignment; ++ } else if (bs->bl.request_alignment != 1) { ++ conf->discard_granularity = bs->bl.request_alignment; ++ } ++ } ++ } ++ ++ if (conf->logical_block_size > conf->physical_block_size) { ++ error_setg(errp, ++ "logical_block_size > physical_block_size not supported"); ++ return false; ++ } ++ ++ if (!QEMU_IS_ALIGNED(conf->min_io_size, conf->logical_block_size)) { ++ error_setg(errp, ++ "min_io_size must be a multiple of logical_block_size"); ++ return false; ++ } ++ ++ /* ++ * all devices which support min_io_size (scsi and virtio-blk) expose it to ++ * the guest as a uint16_t in units of logical blocks ++ */ ++ if (conf->min_io_size / conf->logical_block_size > UINT16_MAX) { ++ error_setg(errp, "min_io_size must not exceed %u logical blocks", ++ UINT16_MAX); ++ return false; ++ } ++ ++ if (!QEMU_IS_ALIGNED(conf->opt_io_size, conf->logical_block_size)) { ++ error_setg(errp, ++ "opt_io_size must be a multiple of logical_block_size"); ++ return false; ++ } ++ ++ if (conf->discard_granularity != -1 && ++ !QEMU_IS_ALIGNED(conf->discard_granularity, ++ conf->logical_block_size)) { ++ error_setg(errp, "discard_granularity must be " ++ "a multiple of logical_block_size"); ++ return false; ++ } ++ ++ return true; ++} ++ ++bool blkconf_apply_backend_options(BlockConf *conf, bool readonly, ++ bool resizable, Error **errp) ++{ ++ BlockBackend *blk = conf->blk; ++ BlockdevOnError rerror, werror; ++ uint64_t perm, shared_perm; ++ bool wce; ++ int ret; ++ ++ perm = BLK_PERM_CONSISTENT_READ; ++ if (!readonly) { ++ perm |= BLK_PERM_WRITE; ++ } ++ ++ shared_perm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED; ++ if (resizable) { ++ shared_perm |= BLK_PERM_RESIZE; ++ } ++ if (conf->share_rw) { ++ shared_perm |= BLK_PERM_WRITE; ++ } ++ ++ ret = blk_set_perm(blk, perm, shared_perm, errp); ++ if (ret < 0) { ++ return false; ++ } ++ ++ switch (conf->wce) { ++ case ON_OFF_AUTO_ON: wce = true; break; ++ case ON_OFF_AUTO_OFF: wce = false; break; ++ case ON_OFF_AUTO_AUTO: wce = blk_enable_write_cache(blk); break; ++ default: ++ abort(); ++ } ++ ++ rerror = conf->rerror; ++ if (rerror == BLOCKDEV_ON_ERROR_AUTO) { ++ rerror = blk_get_on_error(blk, true); ++ } ++ ++ werror = conf->werror; ++ if (werror == BLOCKDEV_ON_ERROR_AUTO) { ++ werror = blk_get_on_error(blk, false); ++ } ++ ++ blk_set_enable_write_cache(blk, wce); ++ blk_set_on_error(blk, rerror, werror); ++ ++ block_acct_setup(blk_get_stats(blk), conf->account_invalid, ++ conf->account_failed); ++ return true; ++} ++ ++bool blkconf_geometry(BlockConf *conf, int *ptrans, ++ unsigned cyls_max, unsigned heads_max, unsigned secs_max, ++ Error **errp) ++{ ++ if (!conf->cyls && !conf->heads && !conf->secs) { ++ hd_geometry_guess(conf->blk, ++ &conf->cyls, &conf->heads, &conf->secs, ++ ptrans); ++ } else if (ptrans && *ptrans == BIOS_ATA_TRANSLATION_AUTO) { ++ *ptrans = hd_bios_chs_auto_trans(conf->cyls, conf->heads, conf->secs); ++ } ++ if (conf->cyls || conf->heads || conf->secs) { ++ if (conf->cyls < 1 || conf->cyls > cyls_max) { ++ error_setg(errp, "cyls must be between 1 and %u", cyls_max); ++ return false; ++ } ++ if (conf->heads < 1 || conf->heads > heads_max) { ++ error_setg(errp, "heads must be between 1 and %u", heads_max); ++ return false; ++ } ++ if (conf->secs < 1 || conf->secs > secs_max) { ++ error_setg(errp, "secs must be between 1 and %u", secs_max); ++ return false; ++ } ++ } ++ return true; ++} +diff --git a/qcow2/lib/hw/block/hd-geometry.c b/qcow2/lib/hw/block/hd-geometry.c +new file mode 100644 +index 00000000..2b0af443 +--- /dev/null ++++ b/qcow2/lib/hw/block/hd-geometry.c +@@ -0,0 +1,168 @@ ++/* ++ * Hard disk geometry utilities ++ * ++ * Copyright (C) 2012 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ * This file incorporates work covered by the following copyright and ++ * permission notice: ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "sysemu/block-backend.h" ++#include "qapi/qapi-types-block.h" ++#include "qemu/bswap.h" ++#include "hw/block/block.h" ++#include "trace.h" ++ ++struct partition { ++ uint8_t boot_ind; /* 0x80 - active */ ++ uint8_t head; /* starting head */ ++ uint8_t sector; /* starting sector */ ++ uint8_t cyl; /* starting cylinder */ ++ uint8_t sys_ind; /* What partition type */ ++ uint8_t end_head; /* end head */ ++ uint8_t end_sector; /* end sector */ ++ uint8_t end_cyl; /* end cylinder */ ++ uint32_t start_sect; /* starting sector counting from 0 */ ++ uint32_t nr_sects; /* nr of sectors in partition */ ++} QEMU_PACKED; ++ ++/* try to guess the disk logical geometry from the MS-DOS partition table. ++ Return 0 if OK, -1 if could not guess */ ++static int guess_disk_lchs(BlockBackend *blk, ++ int *pcylinders, int *pheads, int *psectors) ++{ ++ uint8_t buf[BDRV_SECTOR_SIZE]; ++ int i, heads, sectors, cylinders; ++ struct partition *p; ++ uint32_t nr_sects; ++ uint64_t nb_sectors; ++ ++ blk_get_geometry(blk, &nb_sectors); ++ ++ if (blk_pread(blk, 0, BDRV_SECTOR_SIZE, buf, 0) < 0) { ++ return -1; ++ } ++ /* test MS-DOS magic */ ++ if (buf[510] != 0x55 || buf[511] != 0xaa) { ++ return -1; ++ } ++ for (i = 0; i < 4; i++) { ++ p = ((struct partition *)(buf + 0x1be)) + i; ++ nr_sects = le32_to_cpu(p->nr_sects); ++ if (nr_sects && p->end_head) { ++ /* We make the assumption that the partition terminates on ++ a cylinder boundary */ ++ heads = p->end_head + 1; ++ sectors = p->end_sector & 63; ++ if (sectors == 0) { ++ continue; ++ } ++ cylinders = nb_sectors / (heads * sectors); ++ if (cylinders < 1 || cylinders > 16383) { ++ continue; ++ } ++ *pheads = heads; ++ *psectors = sectors; ++ *pcylinders = cylinders; ++ trace_hd_geometry_lchs_guess(blk, cylinders, heads, sectors); ++ return 0; ++ } ++ } ++ return -1; ++} ++ ++static void guess_chs_for_size(BlockBackend *blk, ++ uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs) ++{ ++ uint64_t nb_sectors; ++ int cylinders; ++ ++ blk_get_geometry(blk, &nb_sectors); ++ ++ cylinders = nb_sectors / (16 * 63); ++ if (cylinders > 16383) { ++ cylinders = 16383; ++ } else if (cylinders < 2) { ++ cylinders = 2; ++ } ++ *pcyls = cylinders; ++ *pheads = 16; ++ *psecs = 63; ++} ++ ++void hd_geometry_guess(BlockBackend *blk, ++ uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs, ++ int *ptrans) ++{ ++ int cylinders, heads, secs, translation; ++ HDGeometry geo; ++ ++ /* Try to probe the backing device geometry, otherwise fallback ++ to the old logic. (as of 12/2014 probing only succeeds on DASDs) */ ++ if (blk_probe_geometry(blk, &geo) == 0) { ++ *pcyls = geo.cylinders; ++ *psecs = geo.sectors; ++ *pheads = geo.heads; ++ translation = BIOS_ATA_TRANSLATION_NONE; ++ } else if (guess_disk_lchs(blk, &cylinders, &heads, &secs) < 0) { ++ /* no LCHS guess: use a standard physical disk geometry */ ++ guess_chs_for_size(blk, pcyls, pheads, psecs); ++ translation = hd_bios_chs_auto_trans(*pcyls, *pheads, *psecs); ++ } else if (heads > 16) { ++ /* LCHS guess with heads > 16 means that a BIOS LBA ++ translation was active, so a standard physical disk ++ geometry is OK */ ++ guess_chs_for_size(blk, pcyls, pheads, psecs); ++ translation = *pcyls * *pheads <= 131072 ++ ? BIOS_ATA_TRANSLATION_LARGE ++ : BIOS_ATA_TRANSLATION_LBA; ++ } else { ++ /* LCHS guess with heads <= 16: use as physical geometry */ ++ *pcyls = cylinders; ++ *pheads = heads; ++ *psecs = secs; ++ /* disable any translation to be in sync with ++ the logical geometry */ ++ translation = BIOS_ATA_TRANSLATION_NONE; ++ } ++ if (ptrans) { ++ if (*ptrans == BIOS_ATA_TRANSLATION_AUTO) { ++ *ptrans = translation; ++ } else { ++ /* Defer to the translation specified by the user. */ ++ translation = *ptrans; ++ } ++ } ++ trace_hd_geometry_guess(blk, *pcyls, *pheads, *psecs, translation); ++} ++ ++int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs) ++{ ++ return cyls <= 1024 && heads <= 16 && secs <= 63 ++ ? BIOS_ATA_TRANSLATION_NONE ++ : BIOS_ATA_TRANSLATION_LBA; ++} +diff --git a/qcow2/lib/io/channel-file.c b/qcow2/lib/io/channel-file.c +new file mode 100644 +index 00000000..2ea8d083 +--- /dev/null ++++ b/qcow2/lib/io/channel-file.c +@@ -0,0 +1,324 @@ ++/* ++ * QEMU I/O channels files driver ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "io/channel-file.h" ++#include "io/channel-util.h" ++#include "io/channel-watch.h" ++#include "qapi/error.h" ++#include "qemu/module.h" ++#include "qemu/sockets.h" ++#include "trace.h" ++ ++QIOChannelFile * ++qio_channel_file_new_fd(int fd) ++{ ++ QIOChannelFile *ioc; ++ ++ ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE)); ++ ++ ioc->fd = fd; ++ ++ if (lseek(fd, 0, SEEK_CUR) != (off_t)-1) { ++ qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE); ++ } ++ ++ trace_qio_channel_file_new_fd(ioc, fd); ++ ++ return ioc; ++} ++ ++QIOChannelFile * ++qio_channel_file_new_dupfd(int fd, Error **errp) ++{ ++ int newfd = dup(fd); ++ ++ if (newfd < 0) { ++ error_setg_errno(errp, errno, "Could not dup FD %d", fd); ++ return NULL; ++ } ++ ++ return qio_channel_file_new_fd(newfd); ++} ++ ++QIOChannelFile * ++qio_channel_file_new_path(const char *path, ++ int flags, ++ mode_t mode, ++ Error **errp) ++{ ++ QIOChannelFile *ioc; ++ ++ ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE)); ++ ++ if (flags & O_CREAT) { ++ ioc->fd = qemu_create(path, flags & ~O_CREAT, mode, errp); ++ } else { ++ ioc->fd = qemu_open(path, flags, errp); ++ } ++ if (ioc->fd < 0) { ++ object_unref(OBJECT(ioc)); ++ return NULL; ++ } ++ ++ if (lseek(ioc->fd, 0, SEEK_CUR) != (off_t)-1) { ++ qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE); ++ } ++ ++ trace_qio_channel_file_new_path(ioc, path, flags, mode, ioc->fd); ++ ++ return ioc; ++} ++ ++ ++static void qio_channel_file_init(Object *obj) ++{ ++ QIOChannelFile *ioc = QIO_CHANNEL_FILE(obj); ++ ioc->fd = -1; ++} ++ ++static void qio_channel_file_finalize(Object *obj) ++{ ++ QIOChannelFile *ioc = QIO_CHANNEL_FILE(obj); ++ if (ioc->fd != -1) { ++ qemu_close(ioc->fd); ++ ioc->fd = -1; ++ } ++} ++ ++ ++static ssize_t qio_channel_file_readv(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, ++ size_t *nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ssize_t ret; ++ ++ retry: ++ ret = readv(fioc->fd, iov, niov); ++ if (ret < 0) { ++ if (errno == EAGAIN) { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ if (errno == EINTR) { ++ goto retry; ++ } ++ ++ error_setg_errno(errp, errno, ++ "Unable to read from file"); ++ return -1; ++ } ++ ++ return ret; ++} ++ ++static ssize_t qio_channel_file_writev(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, ++ size_t nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ssize_t ret; ++ ++ retry: ++ ret = writev(fioc->fd, iov, niov); ++ if (ret <= 0) { ++ if (errno == EAGAIN) { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ if (errno == EINTR) { ++ goto retry; ++ } ++ error_setg_errno(errp, errno, ++ "Unable to write to file"); ++ return -1; ++ } ++ return ret; ++} ++ ++#ifdef CONFIG_PREADV ++static ssize_t qio_channel_file_preadv(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ off_t offset, ++ Error **errp) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ssize_t ret; ++ ++ retry: ++ ret = preadv(fioc->fd, iov, niov, offset); ++ if (ret < 0) { ++ if (errno == EAGAIN) { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ if (errno == EINTR) { ++ goto retry; ++ } ++ ++ error_setg_errno(errp, errno, "Unable to read from file"); ++ return -1; ++ } ++ ++ return ret; ++} ++ ++static ssize_t qio_channel_file_pwritev(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ off_t offset, ++ Error **errp) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ssize_t ret; ++ ++ retry: ++ ret = pwritev(fioc->fd, iov, niov, offset); ++ if (ret <= 0) { ++ if (errno == EAGAIN) { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ if (errno == EINTR) { ++ goto retry; ++ } ++ error_setg_errno(errp, errno, "Unable to write to file"); ++ return -1; ++ } ++ return ret; ++} ++#endif /* CONFIG_PREADV */ ++ ++static int qio_channel_file_set_blocking(QIOChannel *ioc, ++ bool enabled, ++ Error **errp) ++{ ++#ifdef WIN32 ++ /* not implemented */ ++ error_setg_errno(errp, errno, "Failed to set FD nonblocking"); ++ return -1; ++#else ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ++ if (!g_unix_set_fd_nonblocking(fioc->fd, !enabled, NULL)) { ++ error_setg_errno(errp, errno, "Failed to set FD nonblocking"); ++ return -1; ++ } ++ return 0; ++#endif ++} ++ ++ ++static off_t qio_channel_file_seek(QIOChannel *ioc, ++ off_t offset, ++ int whence, ++ Error **errp) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ off_t ret; ++ ++ ret = lseek(fioc->fd, offset, whence); ++ if (ret == (off_t)-1) { ++ error_setg_errno(errp, errno, ++ "Unable to seek to offset %lld whence %d in file", ++ (long long int)offset, whence); ++ return -1; ++ } ++ return ret; ++} ++ ++ ++static int qio_channel_file_close(QIOChannel *ioc, ++ Error **errp) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ++ if (qemu_close(fioc->fd) < 0) { ++ error_setg_errno(errp, errno, ++ "Unable to close file"); ++ return -1; ++ } ++ fioc->fd = -1; ++ return 0; ++} ++ ++ ++static void qio_channel_file_set_aio_fd_handler(QIOChannel *ioc, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ++ qio_channel_util_set_aio_fd_handler(fioc->fd, read_ctx, io_read, ++ fioc->fd, write_ctx, io_write, ++ opaque); ++} ++ ++static GSource *qio_channel_file_create_watch(QIOChannel *ioc, ++ GIOCondition condition) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ return qio_channel_create_fd_watch(ioc, ++ fioc->fd, ++ condition); ++} ++ ++static void qio_channel_file_class_init(ObjectClass *klass, ++ void *class_data G_GNUC_UNUSED) ++{ ++ QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); ++ ++ ioc_klass->io_writev = qio_channel_file_writev; ++ ioc_klass->io_readv = qio_channel_file_readv; ++ ioc_klass->io_set_blocking = qio_channel_file_set_blocking; ++#ifdef CONFIG_PREADV ++ ioc_klass->io_pwritev = qio_channel_file_pwritev; ++ ioc_klass->io_preadv = qio_channel_file_preadv; ++#endif ++ ioc_klass->io_seek = qio_channel_file_seek; ++ ioc_klass->io_close = qio_channel_file_close; ++ ioc_klass->io_create_watch = qio_channel_file_create_watch; ++ ioc_klass->io_set_aio_fd_handler = qio_channel_file_set_aio_fd_handler; ++} ++ ++static const TypeInfo qio_channel_file_info = { ++ .parent = TYPE_QIO_CHANNEL, ++ .name = TYPE_QIO_CHANNEL_FILE, ++ .instance_size = sizeof(QIOChannelFile), ++ .instance_init = qio_channel_file_init, ++ .instance_finalize = qio_channel_file_finalize, ++ .class_init = qio_channel_file_class_init, ++}; ++ ++static void qio_channel_file_register_types(void) ++{ ++ type_register_static(&qio_channel_file_info); ++} ++ ++type_init(qio_channel_file_register_types); +diff --git a/qcow2/lib/io/channel-socket.c b/qcow2/lib/io/channel-socket.c +new file mode 100644 +index 00000000..608bcf06 +--- /dev/null ++++ b/qcow2/lib/io/channel-socket.c +@@ -0,0 +1,985 @@ ++/* ++ * QEMU I/O channels sockets driver ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi/qapi-visit-sockets.h" ++#include "qemu/module.h" ++#include "io/channel-socket.h" ++#include "io/channel-util.h" ++#include "io/channel-watch.h" ++#include "trace.h" ++#include "qapi/clone-visitor.h" ++#ifdef CONFIG_LINUX ++#include ++#include ++ ++#if (defined(MSG_ZEROCOPY) && defined(SO_ZEROCOPY)) ++#define QEMU_MSG_ZEROCOPY ++#endif ++#endif ++ ++#define SOCKET_MAX_FDS 16 ++ ++SocketAddress * ++qio_channel_socket_get_local_address(QIOChannelSocket *ioc, ++ Error **errp) ++{ ++ return socket_sockaddr_to_address(&ioc->localAddr, ++ ioc->localAddrLen, ++ errp); ++} ++ ++SocketAddress * ++qio_channel_socket_get_remote_address(QIOChannelSocket *ioc, ++ Error **errp) ++{ ++ return socket_sockaddr_to_address(&ioc->remoteAddr, ++ ioc->remoteAddrLen, ++ errp); ++} ++ ++QIOChannelSocket * ++qio_channel_socket_new(void) ++{ ++ QIOChannelSocket *sioc; ++ QIOChannel *ioc; ++ ++ sioc = QIO_CHANNEL_SOCKET(object_new(TYPE_QIO_CHANNEL_SOCKET)); ++ sioc->fd = -1; ++ sioc->zero_copy_queued = 0; ++ sioc->zero_copy_sent = 0; ++ ++ ioc = QIO_CHANNEL(sioc); ++ qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); ++ ++#ifdef WIN32 ++ ioc->event = CreateEvent(NULL, FALSE, FALSE, NULL); ++#endif ++ ++ trace_qio_channel_socket_new(sioc); ++ ++ return sioc; ++} ++ ++ ++static int ++qio_channel_socket_set_fd(QIOChannelSocket *sioc, ++ int fd, ++ Error **errp) ++{ ++ if (sioc->fd != -1) { ++ error_setg(errp, "Socket is already open"); ++ return -1; ++ } ++ ++ sioc->fd = fd; ++ sioc->remoteAddrLen = sizeof(sioc->remoteAddr); ++ sioc->localAddrLen = sizeof(sioc->localAddr); ++ ++ ++ if (getpeername(fd, (struct sockaddr *)&sioc->remoteAddr, ++ &sioc->remoteAddrLen) < 0) { ++ if (errno == ENOTCONN) { ++ memset(&sioc->remoteAddr, 0, sizeof(sioc->remoteAddr)); ++ sioc->remoteAddrLen = sizeof(sioc->remoteAddr); ++ } else { ++ error_setg_errno(errp, errno, ++ "Unable to query remote socket address"); ++ goto error; ++ } ++ } ++ ++ if (getsockname(fd, (struct sockaddr *)&sioc->localAddr, ++ &sioc->localAddrLen) < 0) { ++ error_setg_errno(errp, errno, ++ "Unable to query local socket address"); ++ goto error; ++ } ++ ++#ifndef WIN32 ++ if (sioc->localAddr.ss_family == AF_UNIX) { ++ QIOChannel *ioc = QIO_CHANNEL(sioc); ++ qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS); ++ } ++#endif /* WIN32 */ ++ ++ return 0; ++ ++ error: ++ sioc->fd = -1; /* Let the caller close FD on failure */ ++ return -1; ++} ++ ++QIOChannelSocket * ++qio_channel_socket_new_fd(int fd, ++ Error **errp) ++{ ++ QIOChannelSocket *ioc; ++ ++ ioc = qio_channel_socket_new(); ++ if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { ++ object_unref(OBJECT(ioc)); ++ return NULL; ++ } ++ ++ trace_qio_channel_socket_new_fd(ioc, fd); ++ ++ return ioc; ++} ++ ++ ++int qio_channel_socket_connect_sync(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ Error **errp) ++{ ++ int fd; ++ ++ trace_qio_channel_socket_connect_sync(ioc, addr); ++ fd = socket_connect(addr, errp); ++ if (fd < 0) { ++ trace_qio_channel_socket_connect_fail(ioc); ++ return -1; ++ } ++ ++ trace_qio_channel_socket_connect_complete(ioc, fd); ++ if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { ++ close(fd); ++ return -1; ++ } ++ ++#ifdef QEMU_MSG_ZEROCOPY ++ int ret, v = 1; ++ ret = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &v, sizeof(v)); ++ if (ret == 0) { ++ /* Zero copy available on host */ ++ qio_channel_set_feature(QIO_CHANNEL(ioc), ++ QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY); ++ } ++#endif ++ ++ qio_channel_set_feature(QIO_CHANNEL(ioc), ++ QIO_CHANNEL_FEATURE_READ_MSG_PEEK); ++ ++ return 0; ++} ++ ++ ++static void qio_channel_socket_connect_worker(QIOTask *task, ++ gpointer opaque) ++{ ++ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); ++ SocketAddress *addr = opaque; ++ Error *err = NULL; ++ ++ qio_channel_socket_connect_sync(ioc, addr, &err); ++ ++ qio_task_set_error(task, err); ++} ++ ++ ++void qio_channel_socket_connect_async(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ QIOTaskFunc callback, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context) ++{ ++ QIOTask *task = qio_task_new( ++ OBJECT(ioc), callback, opaque, destroy); ++ SocketAddress *addrCopy; ++ ++ addrCopy = QAPI_CLONE(SocketAddress, addr); ++ ++ /* socket_connect() does a non-blocking connect(), but it ++ * still blocks in DNS lookups, so we must use a thread */ ++ trace_qio_channel_socket_connect_async(ioc, addr); ++ qio_task_run_in_thread(task, ++ qio_channel_socket_connect_worker, ++ addrCopy, ++ (GDestroyNotify)qapi_free_SocketAddress, ++ context); ++} ++ ++ ++int qio_channel_socket_listen_sync(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ int num, ++ Error **errp) ++{ ++ int fd; ++ ++ trace_qio_channel_socket_listen_sync(ioc, addr, num); ++ fd = socket_listen(addr, num, errp); ++ if (fd < 0) { ++ trace_qio_channel_socket_listen_fail(ioc); ++ return -1; ++ } ++ ++ trace_qio_channel_socket_listen_complete(ioc, fd); ++ if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { ++ close(fd); ++ return -1; ++ } ++ qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_LISTEN); ++ ++ return 0; ++} ++ ++ ++struct QIOChannelListenWorkerData { ++ SocketAddress *addr; ++ int num; /* amount of expected connections */ ++}; ++ ++static void qio_channel_listen_worker_free(gpointer opaque) ++{ ++ struct QIOChannelListenWorkerData *data = opaque; ++ ++ qapi_free_SocketAddress(data->addr); ++ g_free(data); ++} ++ ++static void qio_channel_socket_listen_worker(QIOTask *task, ++ gpointer opaque) ++{ ++ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); ++ struct QIOChannelListenWorkerData *data = opaque; ++ Error *err = NULL; ++ ++ qio_channel_socket_listen_sync(ioc, data->addr, data->num, &err); ++ ++ qio_task_set_error(task, err); ++} ++ ++ ++void qio_channel_socket_listen_async(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ int num, ++ QIOTaskFunc callback, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context) ++{ ++ QIOTask *task = qio_task_new( ++ OBJECT(ioc), callback, opaque, destroy); ++ struct QIOChannelListenWorkerData *data; ++ ++ data = g_new0(struct QIOChannelListenWorkerData, 1); ++ data->addr = QAPI_CLONE(SocketAddress, addr); ++ data->num = num; ++ ++ /* socket_listen() blocks in DNS lookups, so we must use a thread */ ++ trace_qio_channel_socket_listen_async(ioc, addr, num); ++ qio_task_run_in_thread(task, ++ qio_channel_socket_listen_worker, ++ data, ++ qio_channel_listen_worker_free, ++ context); ++} ++ ++ ++int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc, ++ SocketAddress *localAddr, ++ SocketAddress *remoteAddr, ++ Error **errp) ++{ ++ int fd; ++ ++ trace_qio_channel_socket_dgram_sync(ioc, localAddr, remoteAddr); ++ fd = socket_dgram(remoteAddr, localAddr, errp); ++ if (fd < 0) { ++ trace_qio_channel_socket_dgram_fail(ioc); ++ return -1; ++ } ++ ++ trace_qio_channel_socket_dgram_complete(ioc, fd); ++ if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { ++ close(fd); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++struct QIOChannelSocketDGramWorkerData { ++ SocketAddress *localAddr; ++ SocketAddress *remoteAddr; ++}; ++ ++ ++static void qio_channel_socket_dgram_worker_free(gpointer opaque) ++{ ++ struct QIOChannelSocketDGramWorkerData *data = opaque; ++ qapi_free_SocketAddress(data->localAddr); ++ qapi_free_SocketAddress(data->remoteAddr); ++ g_free(data); ++} ++ ++static void qio_channel_socket_dgram_worker(QIOTask *task, ++ gpointer opaque) ++{ ++ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); ++ struct QIOChannelSocketDGramWorkerData *data = opaque; ++ Error *err = NULL; ++ ++ /* socket_dgram() blocks in DNS lookups, so we must use a thread */ ++ qio_channel_socket_dgram_sync(ioc, data->localAddr, ++ data->remoteAddr, &err); ++ ++ qio_task_set_error(task, err); ++} ++ ++ ++void qio_channel_socket_dgram_async(QIOChannelSocket *ioc, ++ SocketAddress *localAddr, ++ SocketAddress *remoteAddr, ++ QIOTaskFunc callback, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context) ++{ ++ QIOTask *task = qio_task_new( ++ OBJECT(ioc), callback, opaque, destroy); ++ struct QIOChannelSocketDGramWorkerData *data = g_new0( ++ struct QIOChannelSocketDGramWorkerData, 1); ++ ++ data->localAddr = QAPI_CLONE(SocketAddress, localAddr); ++ data->remoteAddr = QAPI_CLONE(SocketAddress, remoteAddr); ++ ++ trace_qio_channel_socket_dgram_async(ioc, localAddr, remoteAddr); ++ qio_task_run_in_thread(task, ++ qio_channel_socket_dgram_worker, ++ data, ++ qio_channel_socket_dgram_worker_free, ++ context); ++} ++ ++ ++QIOChannelSocket * ++qio_channel_socket_accept(QIOChannelSocket *ioc, ++ Error **errp) ++{ ++ QIOChannelSocket *cioc; ++ ++ cioc = qio_channel_socket_new(); ++ cioc->remoteAddrLen = sizeof(ioc->remoteAddr); ++ cioc->localAddrLen = sizeof(ioc->localAddr); ++ ++ retry: ++ trace_qio_channel_socket_accept(ioc); ++ cioc->fd = qemu_accept(ioc->fd, (struct sockaddr *)&cioc->remoteAddr, ++ &cioc->remoteAddrLen); ++ if (cioc->fd < 0) { ++ if (errno == EINTR) { ++ goto retry; ++ } ++ error_setg_errno(errp, errno, "Unable to accept connection"); ++ trace_qio_channel_socket_accept_fail(ioc); ++ goto error; ++ } ++ ++ if (getsockname(cioc->fd, (struct sockaddr *)&cioc->localAddr, ++ &cioc->localAddrLen) < 0) { ++ error_setg_errno(errp, errno, ++ "Unable to query local socket address"); ++ goto error; ++ } ++ ++#ifndef WIN32 ++ if (cioc->localAddr.ss_family == AF_UNIX) { ++ QIOChannel *ioc_local = QIO_CHANNEL(cioc); ++ qio_channel_set_feature(ioc_local, QIO_CHANNEL_FEATURE_FD_PASS); ++ } ++#endif /* WIN32 */ ++ ++ qio_channel_set_feature(QIO_CHANNEL(cioc), ++ QIO_CHANNEL_FEATURE_READ_MSG_PEEK); ++ ++ trace_qio_channel_socket_accept_complete(ioc, cioc, cioc->fd); ++ return cioc; ++ ++ error: ++ object_unref(OBJECT(cioc)); ++ return NULL; ++} ++ ++static void qio_channel_socket_init(Object *obj) ++{ ++ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(obj); ++ ioc->fd = -1; ++} ++ ++static void qio_channel_socket_finalize(Object *obj) ++{ ++ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(obj); ++ ++ if (ioc->fd != -1) { ++ QIOChannel *ioc_local = QIO_CHANNEL(ioc); ++ if (qio_channel_has_feature(ioc_local, QIO_CHANNEL_FEATURE_LISTEN)) { ++ Error *err = NULL; ++ ++ socket_listen_cleanup(ioc->fd, &err); ++ if (err) { ++ error_report_err(err); ++ err = NULL; ++ } ++ } ++#ifdef WIN32 ++ qemu_socket_unselect(ioc->fd, NULL); ++#endif ++ close(ioc->fd); ++ ioc->fd = -1; ++ } ++} ++ ++ ++#ifndef WIN32 ++static void qio_channel_socket_copy_fds(struct msghdr *msg, ++ int **fds, size_t *nfds) ++{ ++ struct cmsghdr *cmsg; ++ ++ *nfds = 0; ++ *fds = NULL; ++ ++ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { ++ int fd_size, i; ++ int gotfds; ++ ++ if (cmsg->cmsg_len < CMSG_LEN(sizeof(int)) || ++ cmsg->cmsg_level != SOL_SOCKET || ++ cmsg->cmsg_type != SCM_RIGHTS) { ++ continue; ++ } ++ ++ fd_size = cmsg->cmsg_len - CMSG_LEN(0); ++ ++ if (!fd_size) { ++ continue; ++ } ++ ++ gotfds = fd_size / sizeof(int); ++ *fds = g_renew(int, *fds, *nfds + gotfds); ++ memcpy(*fds + *nfds, CMSG_DATA(cmsg), fd_size); ++ ++ for (i = 0; i < gotfds; i++) { ++ int fd = (*fds)[*nfds + i]; ++ if (fd < 0) { ++ continue; ++ } ++ ++ /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ ++ qemu_socket_set_block(fd); ++ ++#ifndef MSG_CMSG_CLOEXEC ++ qemu_set_cloexec(fd); ++#endif ++ } ++ *nfds += gotfds; ++ } ++} ++ ++ ++static ssize_t qio_channel_socket_readv(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, ++ size_t *nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ ssize_t ret; ++ struct msghdr msg = { NULL, }; ++ char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)]; ++ int sflags = 0; ++ ++ memset(control, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)); ++ ++ msg.msg_iov = (struct iovec *)iov; ++ msg.msg_iovlen = niov; ++ if (fds && nfds) { ++ msg.msg_control = control; ++ msg.msg_controllen = sizeof(control); ++#ifdef MSG_CMSG_CLOEXEC ++ sflags |= MSG_CMSG_CLOEXEC; ++#endif ++ ++ } ++ ++ if (flags & QIO_CHANNEL_READ_FLAG_MSG_PEEK) { ++ sflags |= MSG_PEEK; ++ } ++ ++ retry: ++ ret = recvmsg(sioc->fd, &msg, sflags); ++ if (ret < 0) { ++ if (errno == EAGAIN) { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ if (errno == EINTR) { ++ goto retry; ++ } ++ ++ error_setg_errno(errp, errno, ++ "Unable to read from socket"); ++ return -1; ++ } ++ ++ if (fds && nfds) { ++ qio_channel_socket_copy_fds(&msg, fds, nfds); ++ } ++ ++ return ret; ++} ++ ++static ssize_t qio_channel_socket_writev(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, ++ size_t nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ ssize_t ret; ++ struct msghdr msg = { NULL, }; ++ char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)]; ++ size_t fdsize = sizeof(int) * nfds; ++ struct cmsghdr *cmsg; ++ int sflags = 0; ++ ++ memset(control, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)); ++ ++ msg.msg_iov = (struct iovec *)iov; ++ msg.msg_iovlen = niov; ++ ++ if (nfds) { ++ if (nfds > SOCKET_MAX_FDS) { ++ error_setg_errno(errp, EINVAL, ++ "Only %d FDs can be sent, got %zu", ++ SOCKET_MAX_FDS, nfds); ++ return -1; ++ } ++ ++ msg.msg_control = control; ++ msg.msg_controllen = CMSG_SPACE(sizeof(int) * nfds); ++ ++ cmsg = CMSG_FIRSTHDR(&msg); ++ cmsg->cmsg_len = CMSG_LEN(fdsize); ++ cmsg->cmsg_level = SOL_SOCKET; ++ cmsg->cmsg_type = SCM_RIGHTS; ++ memcpy(CMSG_DATA(cmsg), fds, fdsize); ++ } ++ ++ if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { ++#ifdef QEMU_MSG_ZEROCOPY ++ sflags = MSG_ZEROCOPY; ++#else ++ /* ++ * We expect QIOChannel class entry point to have ++ * blocked this code path already ++ */ ++ g_assert_not_reached(); ++#endif ++ } ++ ++ retry: ++ ret = sendmsg(sioc->fd, &msg, sflags); ++ if (ret <= 0) { ++ switch (errno) { ++ case EAGAIN: ++ return QIO_CHANNEL_ERR_BLOCK; ++ case EINTR: ++ goto retry; ++ case ENOBUFS: ++ if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { ++ error_setg_errno(errp, errno, ++ "Process can't lock enough memory for using MSG_ZEROCOPY"); ++ return -1; ++ } ++ break; ++ } ++ ++ error_setg_errno(errp, errno, ++ "Unable to write to socket"); ++ return -1; ++ } ++ ++ if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { ++ sioc->zero_copy_queued++; ++ } ++ ++ return ret; ++} ++#else /* WIN32 */ ++static ssize_t qio_channel_socket_readv(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, ++ size_t *nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ ssize_t done = 0; ++ ssize_t i; ++ int sflags = 0; ++ ++ if (flags & QIO_CHANNEL_READ_FLAG_MSG_PEEK) { ++ sflags |= MSG_PEEK; ++ } ++ ++ for (i = 0; i < niov; i++) { ++ ssize_t ret; ++ retry: ++ ret = recv(sioc->fd, ++ iov[i].iov_base, ++ iov[i].iov_len, ++ sflags); ++ if (ret < 0) { ++ if (errno == EAGAIN) { ++ if (done) { ++ return done; ++ } else { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ } else if (errno == EINTR) { ++ goto retry; ++ } else { ++ error_setg_errno(errp, errno, ++ "Unable to read from socket"); ++ return -1; ++ } ++ } ++ done += ret; ++ if (ret < iov[i].iov_len) { ++ return done; ++ } ++ } ++ ++ return done; ++} ++ ++static ssize_t qio_channel_socket_writev(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, ++ size_t nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ ssize_t done = 0; ++ ssize_t i; ++ ++ for (i = 0; i < niov; i++) { ++ ssize_t ret; ++ retry: ++ ret = send(sioc->fd, ++ iov[i].iov_base, ++ iov[i].iov_len, ++ 0); ++ if (ret < 0) { ++ if (errno == EAGAIN) { ++ if (done) { ++ return done; ++ } else { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ } else if (errno == EINTR) { ++ goto retry; ++ } else { ++ error_setg_errno(errp, errno, ++ "Unable to write to socket"); ++ return -1; ++ } ++ } ++ done += ret; ++ if (ret < iov[i].iov_len) { ++ return done; ++ } ++ } ++ ++ return done; ++} ++#endif /* WIN32 */ ++ ++ ++#ifdef QEMU_MSG_ZEROCOPY ++static int qio_channel_socket_flush(QIOChannel *ioc, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ struct msghdr msg = {}; ++ struct sock_extended_err *serr; ++ struct cmsghdr *cm; ++ char control[CMSG_SPACE(sizeof(*serr))]; ++ int received; ++ int ret; ++ ++ if (sioc->zero_copy_queued == sioc->zero_copy_sent) { ++ return 0; ++ } ++ ++ msg.msg_control = control; ++ msg.msg_controllen = sizeof(control); ++ memset(control, 0, sizeof(control)); ++ ++ ret = 1; ++ ++ while (sioc->zero_copy_sent < sioc->zero_copy_queued) { ++ received = recvmsg(sioc->fd, &msg, MSG_ERRQUEUE); ++ if (received < 0) { ++ switch (errno) { ++ case EAGAIN: ++ /* Nothing on errqueue, wait until something is available */ ++ qio_channel_wait(ioc, G_IO_ERR); ++ continue; ++ case EINTR: ++ continue; ++ default: ++ error_setg_errno(errp, errno, ++ "Unable to read errqueue"); ++ return -1; ++ } ++ } ++ ++ cm = CMSG_FIRSTHDR(&msg); ++ if (cm->cmsg_level != SOL_IP && cm->cmsg_type != IP_RECVERR && ++ cm->cmsg_level != SOL_IPV6 && cm->cmsg_type != IPV6_RECVERR) { ++ error_setg_errno(errp, EPROTOTYPE, ++ "Wrong cmsg in errqueue"); ++ return -1; ++ } ++ ++ serr = (void *) CMSG_DATA(cm); ++ if (serr->ee_errno != SO_EE_ORIGIN_NONE) { ++ error_setg_errno(errp, serr->ee_errno, ++ "Error on socket"); ++ return -1; ++ } ++ if (serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY) { ++ error_setg_errno(errp, serr->ee_origin, ++ "Error not from zero copy"); ++ return -1; ++ } ++ if (serr->ee_data < serr->ee_info) { ++ error_setg_errno(errp, serr->ee_origin, ++ "Wrong notification bounds"); ++ return -1; ++ } ++ ++ /* No errors, count successfully finished sendmsg()*/ ++ sioc->zero_copy_sent += serr->ee_data - serr->ee_info + 1; ++ ++ /* If any sendmsg() succeeded using zero copy, return 0 at the end */ ++ if (serr->ee_code != SO_EE_CODE_ZEROCOPY_COPIED) { ++ ret = 0; ++ } ++ } ++ ++ return ret; ++} ++ ++#endif /* QEMU_MSG_ZEROCOPY */ ++ ++static int ++qio_channel_socket_set_blocking(QIOChannel *ioc, ++ bool enabled, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ ++ if (enabled) { ++ qemu_socket_set_block(sioc->fd); ++ } else { ++ qemu_socket_set_nonblock(sioc->fd); ++ } ++ return 0; ++} ++ ++ ++static void ++qio_channel_socket_set_delay(QIOChannel *ioc, ++ bool enabled) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ int v = enabled ? 0 : 1; ++ ++ setsockopt(sioc->fd, ++ IPPROTO_TCP, TCP_NODELAY, ++ &v, sizeof(v)); ++} ++ ++ ++static void ++qio_channel_socket_set_cork(QIOChannel *ioc, ++ bool enabled) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ int v = enabled ? 1 : 0; ++ ++ socket_set_cork(sioc->fd, v); ++} ++ ++static int ++qio_channel_socket_get_peerpid(QIOChannel *ioc, ++ unsigned int *pid, ++ Error **errp) ++{ ++#ifdef CONFIG_LINUX ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ Error *err = NULL; ++ socklen_t len = sizeof(struct ucred); ++ ++ struct ucred cred; ++ if (getsockopt(sioc->fd, ++ SOL_SOCKET, SO_PEERCRED, ++ &cred, &len) == -1) { ++ error_setg_errno(&err, errno, "Unable to get peer credentials"); ++ error_propagate(errp, err); ++ *pid = -1; ++ return -1; ++ } ++ *pid = (unsigned int)cred.pid; ++ return 0; ++#else ++ error_setg(errp, "Unsupported feature"); ++ *pid = -1; ++ return -1; ++#endif ++} ++ ++static int ++qio_channel_socket_close(QIOChannel *ioc, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ int rc = 0; ++ Error *err = NULL; ++ ++ if (sioc->fd != -1) { ++#ifdef WIN32 ++ qemu_socket_unselect(sioc->fd, NULL); ++#endif ++ if (qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_LISTEN)) { ++ socket_listen_cleanup(sioc->fd, errp); ++ } ++ ++ if (close(sioc->fd) < 0) { ++ sioc->fd = -1; ++ error_setg_errno(&err, errno, "Unable to close socket"); ++ error_propagate(errp, err); ++ return -1; ++ } ++ sioc->fd = -1; ++ } ++ return rc; ++} ++ ++static int ++qio_channel_socket_shutdown(QIOChannel *ioc, ++ QIOChannelShutdown how, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ int sockhow; ++ ++ switch (how) { ++ case QIO_CHANNEL_SHUTDOWN_READ: ++ sockhow = SHUT_RD; ++ break; ++ case QIO_CHANNEL_SHUTDOWN_WRITE: ++ sockhow = SHUT_WR; ++ break; ++ case QIO_CHANNEL_SHUTDOWN_BOTH: ++ default: ++ sockhow = SHUT_RDWR; ++ break; ++ } ++ ++ if (shutdown(sioc->fd, sockhow) < 0) { ++ error_setg_errno(errp, errno, ++ "Unable to shutdown socket"); ++ return -1; ++ } ++ return 0; ++} ++ ++static void qio_channel_socket_set_aio_fd_handler(QIOChannel *ioc, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ ++ qio_channel_util_set_aio_fd_handler(sioc->fd, read_ctx, io_read, ++ sioc->fd, write_ctx, io_write, ++ opaque); ++} ++ ++static GSource *qio_channel_socket_create_watch(QIOChannel *ioc, ++ GIOCondition condition) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ return qio_channel_create_socket_watch(ioc, ++ sioc->fd, ++ condition); ++} ++ ++static void qio_channel_socket_class_init(ObjectClass *klass, ++ void *class_data G_GNUC_UNUSED) ++{ ++ QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); ++ ++ ioc_klass->io_writev = qio_channel_socket_writev; ++ ioc_klass->io_readv = qio_channel_socket_readv; ++ ioc_klass->io_set_blocking = qio_channel_socket_set_blocking; ++ ioc_klass->io_close = qio_channel_socket_close; ++ ioc_klass->io_shutdown = qio_channel_socket_shutdown; ++ ioc_klass->io_set_cork = qio_channel_socket_set_cork; ++ ioc_klass->io_set_delay = qio_channel_socket_set_delay; ++ ioc_klass->io_create_watch = qio_channel_socket_create_watch; ++ ioc_klass->io_set_aio_fd_handler = qio_channel_socket_set_aio_fd_handler; ++#ifdef QEMU_MSG_ZEROCOPY ++ ioc_klass->io_flush = qio_channel_socket_flush; ++#endif ++ ioc_klass->io_peerpid = qio_channel_socket_get_peerpid; ++} ++ ++static const TypeInfo qio_channel_socket_info = { ++ .parent = TYPE_QIO_CHANNEL, ++ .name = TYPE_QIO_CHANNEL_SOCKET, ++ .instance_size = sizeof(QIOChannelSocket), ++ .instance_init = qio_channel_socket_init, ++ .instance_finalize = qio_channel_socket_finalize, ++ .class_init = qio_channel_socket_class_init, ++}; ++ ++static void qio_channel_socket_register_types(void) ++{ ++ type_register_static(&qio_channel_socket_info); ++} ++ ++type_init(qio_channel_socket_register_types); +diff --git a/qcow2/lib/io/channel-util.c b/qcow2/lib/io/channel-util.c +new file mode 100644 +index 00000000..4b340d46 +--- /dev/null ++++ b/qcow2/lib/io/channel-util.c +@@ -0,0 +1,62 @@ ++/* ++ * QEMU I/O channels utility APIs ++ * ++ * Copyright (c) 2016 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "io/channel-util.h" ++#include "io/channel-file.h" ++#include "io/channel-socket.h" ++ ++ ++QIOChannel *qio_channel_new_fd(int fd, ++ Error **errp) ++{ ++ QIOChannel *ioc; ++ ++ if (fd_is_socket(fd)) { ++ ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fd, errp)); ++ } else { ++ ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd)); ++ } ++ return ioc; ++} ++ ++ ++void qio_channel_util_set_aio_fd_handler(int read_fd, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ int write_fd, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque) ++{ ++ if (read_fd == write_fd && read_ctx == write_ctx) { ++ aio_set_fd_handler(read_ctx, read_fd, io_read, io_write, ++ NULL, NULL, opaque); ++ } else { ++ if (read_ctx) { ++ aio_set_fd_handler(read_ctx, read_fd, io_read, NULL, ++ NULL, NULL, opaque); ++ } ++ if (write_ctx) { ++ aio_set_fd_handler(write_ctx, write_fd, NULL, io_write, ++ NULL, NULL, opaque); ++ } ++ } ++} +diff --git a/qcow2/lib/io/channel-watch.c b/qcow2/lib/io/channel-watch.c +new file mode 100644 +index 00000000..64b486e3 +--- /dev/null ++++ b/qcow2/lib/io/channel-watch.c +@@ -0,0 +1,347 @@ ++/* ++ * QEMU I/O channels watch helper APIs ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "io/channel-watch.h" ++ ++typedef struct QIOChannelFDSource QIOChannelFDSource; ++struct QIOChannelFDSource { ++ GSource parent; ++ GPollFD fd; ++ QIOChannel *ioc; ++ GIOCondition condition; ++}; ++ ++ ++#ifdef CONFIG_WIN32 ++typedef struct QIOChannelSocketSource QIOChannelSocketSource; ++struct QIOChannelSocketSource { ++ GSource parent; ++ GPollFD fd; ++ QIOChannel *ioc; ++ SOCKET socket; ++ int revents; ++ GIOCondition condition; ++}; ++ ++#endif ++ ++ ++typedef struct QIOChannelFDPairSource QIOChannelFDPairSource; ++struct QIOChannelFDPairSource { ++ GSource parent; ++ GPollFD fdread; ++ GPollFD fdwrite; ++ QIOChannel *ioc; ++ GIOCondition condition; ++}; ++ ++ ++static gboolean ++qio_channel_fd_source_prepare(GSource *source G_GNUC_UNUSED, ++ gint *timeout) ++{ ++ *timeout = -1; ++ ++ return FALSE; ++} ++ ++ ++static gboolean ++qio_channel_fd_source_check(GSource *source) ++{ ++ QIOChannelFDSource *ssource = (QIOChannelFDSource *)source; ++ ++ return ssource->fd.revents & ssource->condition; ++} ++ ++ ++static gboolean ++qio_channel_fd_source_dispatch(GSource *source, ++ GSourceFunc callback, ++ gpointer user_data) ++{ ++ QIOChannelFunc func = (QIOChannelFunc)callback; ++ QIOChannelFDSource *ssource = (QIOChannelFDSource *)source; ++ ++ return (*func)(ssource->ioc, ++ ssource->fd.revents & ssource->condition, ++ user_data); ++} ++ ++ ++static void ++qio_channel_fd_source_finalize(GSource *source) ++{ ++ QIOChannelFDSource *ssource = (QIOChannelFDSource *)source; ++ ++ object_unref(OBJECT(ssource->ioc)); ++} ++ ++ ++#ifdef CONFIG_WIN32 ++static gboolean ++qio_channel_socket_source_prepare(GSource *source G_GNUC_UNUSED, ++ gint *timeout) ++{ ++ *timeout = -1; ++ ++ return FALSE; ++} ++ ++ ++/* ++ * NB, this impl only works when the socket is in non-blocking ++ * mode on Win32 ++ */ ++static gboolean ++qio_channel_socket_source_check(GSource *source) ++{ ++ static struct timeval tv0; ++ QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source; ++ fd_set rfds, wfds, xfds; ++ ++ if (!ssource->condition) { ++ return 0; ++ } ++ ++ FD_ZERO(&rfds); ++ FD_ZERO(&wfds); ++ FD_ZERO(&xfds); ++ if (ssource->condition & G_IO_IN) { ++ FD_SET(ssource->socket, &rfds); ++ } ++ if (ssource->condition & G_IO_OUT) { ++ FD_SET(ssource->socket, &wfds); ++ } ++ if (ssource->condition & G_IO_PRI) { ++ FD_SET(ssource->socket, &xfds); ++ } ++ ssource->revents = 0; ++ if (select(0, &rfds, &wfds, &xfds, &tv0) == 0) { ++ return 0; ++ } ++ ++ if (FD_ISSET(ssource->socket, &rfds)) { ++ ssource->revents |= G_IO_IN; ++ } ++ if (FD_ISSET(ssource->socket, &wfds)) { ++ ssource->revents |= G_IO_OUT; ++ } ++ if (FD_ISSET(ssource->socket, &xfds)) { ++ ssource->revents |= G_IO_PRI; ++ } ++ ++ return ssource->revents; ++} ++ ++ ++static gboolean ++qio_channel_socket_source_dispatch(GSource *source, ++ GSourceFunc callback, ++ gpointer user_data) ++{ ++ QIOChannelFunc func = (QIOChannelFunc)callback; ++ QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source; ++ ++ return (*func)(ssource->ioc, ssource->revents, user_data); ++} ++ ++ ++static void ++qio_channel_socket_source_finalize(GSource *source) ++{ ++ QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source; ++ ++ object_unref(OBJECT(ssource->ioc)); ++} ++ ++ ++GSourceFuncs qio_channel_socket_source_funcs = { ++ qio_channel_socket_source_prepare, ++ qio_channel_socket_source_check, ++ qio_channel_socket_source_dispatch, ++ qio_channel_socket_source_finalize ++}; ++#endif ++ ++ ++static gboolean ++qio_channel_fd_pair_source_prepare(GSource *source G_GNUC_UNUSED, ++ gint *timeout) ++{ ++ *timeout = -1; ++ ++ return FALSE; ++} ++ ++ ++static gboolean ++qio_channel_fd_pair_source_check(GSource *source) ++{ ++ QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source; ++ GIOCondition poll_condition = ssource->fdread.revents | ++ ssource->fdwrite.revents; ++ ++ return poll_condition & ssource->condition; ++} ++ ++ ++static gboolean ++qio_channel_fd_pair_source_dispatch(GSource *source, ++ GSourceFunc callback, ++ gpointer user_data) ++{ ++ QIOChannelFunc func = (QIOChannelFunc)callback; ++ QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source; ++ GIOCondition poll_condition = ssource->fdread.revents | ++ ssource->fdwrite.revents; ++ ++ return (*func)(ssource->ioc, ++ poll_condition & ssource->condition, ++ user_data); ++} ++ ++ ++static void ++qio_channel_fd_pair_source_finalize(GSource *source) ++{ ++ QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source; ++ ++ object_unref(OBJECT(ssource->ioc)); ++} ++ ++ ++GSourceFuncs qio_channel_fd_source_funcs = { ++ qio_channel_fd_source_prepare, ++ qio_channel_fd_source_check, ++ qio_channel_fd_source_dispatch, ++ qio_channel_fd_source_finalize ++}; ++ ++ ++GSourceFuncs qio_channel_fd_pair_source_funcs = { ++ qio_channel_fd_pair_source_prepare, ++ qio_channel_fd_pair_source_check, ++ qio_channel_fd_pair_source_dispatch, ++ qio_channel_fd_pair_source_finalize ++}; ++ ++ ++GSource *qio_channel_create_fd_watch(QIOChannel *ioc, ++ int fd, ++ GIOCondition condition) ++{ ++ GSource *source; ++ QIOChannelFDSource *ssource; ++ ++ source = g_source_new(&qio_channel_fd_source_funcs, ++ sizeof(QIOChannelFDSource)); ++ ssource = (QIOChannelFDSource *)source; ++ ++ ssource->ioc = ioc; ++ object_ref(OBJECT(ioc)); ++ ++ ssource->condition = condition; ++ ++#ifdef CONFIG_WIN32 ++ ssource->fd.fd = (gint64)_get_osfhandle(fd); ++#else ++ ssource->fd.fd = fd; ++#endif ++ ssource->fd.events = condition; ++ ++ g_source_add_poll(source, &ssource->fd); ++ ++ return source; ++} ++ ++#ifdef CONFIG_WIN32 ++GSource *qio_channel_create_socket_watch(QIOChannel *ioc, ++ int sockfd, ++ GIOCondition condition) ++{ ++ GSource *source; ++ QIOChannelSocketSource *ssource; ++ ++ qemu_socket_select(sockfd, ioc->event, ++ FD_READ | FD_ACCEPT | FD_CLOSE | ++ FD_CONNECT | FD_WRITE | FD_OOB, NULL); ++ ++ source = g_source_new(&qio_channel_socket_source_funcs, ++ sizeof(QIOChannelSocketSource)); ++ ssource = (QIOChannelSocketSource *)source; ++ ++ ssource->ioc = ioc; ++ object_ref(OBJECT(ioc)); ++ ++ ssource->condition = condition; ++ ssource->socket = _get_osfhandle(sockfd); ++ ssource->revents = 0; ++ ++ ssource->fd.fd = (gintptr)ioc->event; ++ ssource->fd.events = G_IO_IN; ++ ++ g_source_add_poll(source, &ssource->fd); ++ ++ return source; ++} ++#else ++GSource *qio_channel_create_socket_watch(QIOChannel *ioc, ++ int socket, ++ GIOCondition condition) ++{ ++ return qio_channel_create_fd_watch(ioc, socket, condition); ++} ++#endif ++ ++GSource *qio_channel_create_fd_pair_watch(QIOChannel *ioc, ++ int fdread, ++ int fdwrite, ++ GIOCondition condition) ++{ ++ GSource *source; ++ QIOChannelFDPairSource *ssource; ++ ++ source = g_source_new(&qio_channel_fd_pair_source_funcs, ++ sizeof(QIOChannelFDPairSource)); ++ ssource = (QIOChannelFDPairSource *)source; ++ ++ ssource->ioc = ioc; ++ object_ref(OBJECT(ioc)); ++ ++ ssource->condition = condition; ++ ++#ifdef CONFIG_WIN32 ++ ssource->fdread.fd = (gint64)_get_osfhandle(fdread); ++ ssource->fdwrite.fd = (gint64)_get_osfhandle(fdwrite); ++#else ++ ssource->fdread.fd = fdread; ++ ssource->fdwrite.fd = fdwrite; ++#endif ++ ++ ssource->fdread.events = condition & G_IO_IN; ++ ssource->fdwrite.events = condition & G_IO_OUT; ++ ++ g_source_add_poll(source, &ssource->fdread); ++ g_source_add_poll(source, &ssource->fdwrite); ++ ++ return source; ++} +diff --git a/qcow2/lib/io/channel.c b/qcow2/lib/io/channel.c +new file mode 100644 +index 00000000..e3f17c24 +--- /dev/null ++++ b/qcow2/lib/io/channel.c +@@ -0,0 +1,808 @@ ++/* ++ * QEMU I/O channels ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/aio-wait.h" ++#include "io/channel.h" ++#include "qapi/error.h" ++#include "qemu/main-loop.h" ++#include "qemu/module.h" ++#include "qemu/iov.h" ++ ++bool qio_channel_has_feature(QIOChannel *ioc, ++ QIOChannelFeature feature) ++{ ++ return ioc->features & (1 << feature); ++} ++ ++ ++void qio_channel_set_feature(QIOChannel *ioc, ++ QIOChannelFeature feature) ++{ ++ ioc->features |= (1 << feature); ++} ++ ++ ++void qio_channel_set_name(QIOChannel *ioc, ++ const char *name) ++{ ++ g_free(ioc->name); ++ ioc->name = g_strdup(name); ++} ++ ++ ++ssize_t qio_channel_readv_full(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, ++ size_t *nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if ((fds || nfds) && ++ !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) { ++ error_setg_errno(errp, EINVAL, ++ "Channel does not support file descriptor passing"); ++ return -1; ++ } ++ ++ if ((flags & QIO_CHANNEL_READ_FLAG_MSG_PEEK) && ++ !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_READ_MSG_PEEK)) { ++ error_setg_errno(errp, EINVAL, ++ "Channel does not support peek read"); ++ return -1; ++ } ++ ++ return klass->io_readv(ioc, iov, niov, fds, nfds, flags, errp); ++} ++ ++ ++ssize_t qio_channel_writev_full(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, ++ size_t nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (fds || nfds) { ++ if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) { ++ error_setg_errno(errp, EINVAL, ++ "Channel does not support file descriptor passing"); ++ return -1; ++ } ++ if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { ++ error_setg_errno(errp, EINVAL, ++ "Zero Copy does not support file descriptor passing"); ++ return -1; ++ } ++ } ++ ++ if ((flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) && ++ !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY)) { ++ error_setg_errno(errp, EINVAL, ++ "Requested Zero Copy feature is not available"); ++ return -1; ++ } ++ ++ return klass->io_writev(ioc, iov, niov, fds, nfds, flags, errp); ++} ++ ++ ++int coroutine_mixed_fn qio_channel_readv_all_eof(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp) ++{ ++ return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, errp); ++} ++ ++int coroutine_mixed_fn qio_channel_readv_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp) ++{ ++ return qio_channel_readv_full_all(ioc, iov, niov, NULL, NULL, errp); ++} ++ ++int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, size_t *nfds, ++ Error **errp) ++{ ++ int ret = -1; ++ struct iovec *local_iov = g_new(struct iovec, niov); ++ struct iovec *local_iov_head = local_iov; ++ unsigned int nlocal_iov = niov; ++ int **local_fds = fds; ++ size_t *local_nfds = nfds; ++ bool partial = false; ++ ++ if (nfds) { ++ *nfds = 0; ++ } ++ ++ if (fds) { ++ *fds = NULL; ++ } ++ ++ nlocal_iov = iov_copy(local_iov, nlocal_iov, ++ iov, niov, ++ 0, iov_size(iov, niov)); ++ ++ while ((nlocal_iov > 0) || local_fds) { ++ ssize_t len; ++ len = qio_channel_readv_full(ioc, local_iov, nlocal_iov, local_fds, ++ local_nfds, 0, errp); ++ if (len == QIO_CHANNEL_ERR_BLOCK) { ++ if (qemu_in_coroutine()) { ++ qio_channel_yield(ioc, G_IO_IN); ++ } else { ++ qio_channel_wait(ioc, G_IO_IN); ++ } ++ continue; ++ } ++ ++ if (len == 0) { ++ if (local_nfds && *local_nfds) { ++ /* ++ * Got some FDs, but no data yet. This isn't an EOF ++ * scenario (yet), so carry on to try to read data ++ * on next loop iteration ++ */ ++ goto next_iter; ++ } else if (!partial) { ++ /* No fds and no data - EOF before any data read */ ++ ret = 0; ++ goto cleanup; ++ } else { ++ len = -1; ++ error_setg(errp, ++ "Unexpected end-of-file before all data were read"); ++ /* Fallthrough into len < 0 handling */ ++ } ++ } ++ ++ if (len < 0) { ++ /* Close any FDs we previously received */ ++ if (nfds && fds) { ++ size_t i; ++ for (i = 0; i < (*nfds); i++) { ++ close((*fds)[i]); ++ } ++ g_free(*fds); ++ *fds = NULL; ++ *nfds = 0; ++ } ++ goto cleanup; ++ } ++ ++ if (nlocal_iov) { ++ iov_discard_front(&local_iov, &nlocal_iov, len); ++ } ++ ++next_iter: ++ partial = true; ++ local_fds = NULL; ++ local_nfds = NULL; ++ } ++ ++ ret = 1; ++ ++ cleanup: ++ g_free(local_iov_head); ++ return ret; ++} ++ ++int coroutine_mixed_fn qio_channel_readv_full_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, size_t *nfds, ++ Error **errp) ++{ ++ int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, errp); ++ ++ if (ret == 0) { ++ error_setg(errp, "Unexpected end-of-file before all data were read"); ++ return -1; ++ } ++ if (ret == 1) { ++ return 0; ++ } ++ ++ return ret; ++} ++ ++int coroutine_mixed_fn qio_channel_writev_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp) ++{ ++ return qio_channel_writev_full_all(ioc, iov, niov, NULL, 0, 0, errp); ++} ++ ++int coroutine_mixed_fn qio_channel_writev_full_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, size_t nfds, ++ int flags, Error **errp) ++{ ++ int ret = -1; ++ struct iovec *local_iov = g_new(struct iovec, niov); ++ struct iovec *local_iov_head = local_iov; ++ unsigned int nlocal_iov = niov; ++ ++ nlocal_iov = iov_copy(local_iov, nlocal_iov, ++ iov, niov, ++ 0, iov_size(iov, niov)); ++ ++ while (nlocal_iov > 0) { ++ ssize_t len; ++ ++ len = qio_channel_writev_full(ioc, local_iov, nlocal_iov, fds, ++ nfds, flags, errp); ++ ++ if (len == QIO_CHANNEL_ERR_BLOCK) { ++ if (qemu_in_coroutine()) { ++ qio_channel_yield(ioc, G_IO_OUT); ++ } else { ++ qio_channel_wait(ioc, G_IO_OUT); ++ } ++ continue; ++ } ++ if (len < 0) { ++ goto cleanup; ++ } ++ ++ iov_discard_front(&local_iov, &nlocal_iov, len); ++ ++ fds = NULL; ++ nfds = 0; ++ } ++ ++ ret = 0; ++ cleanup: ++ g_free(local_iov_head); ++ return ret; ++} ++ ++ssize_t qio_channel_readv(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp) ++{ ++ return qio_channel_readv_full(ioc, iov, niov, NULL, NULL, 0, errp); ++} ++ ++ ++ssize_t qio_channel_writev(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp) ++{ ++ return qio_channel_writev_full(ioc, iov, niov, NULL, 0, 0, errp); ++} ++ ++ ++ssize_t qio_channel_read(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = buf, .iov_len = buflen }; ++ return qio_channel_readv_full(ioc, &iov, 1, NULL, NULL, 0, errp); ++} ++ ++ ++ssize_t qio_channel_write(QIOChannel *ioc, ++ const char *buf, ++ size_t buflen, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen }; ++ return qio_channel_writev_full(ioc, &iov, 1, NULL, 0, 0, errp); ++} ++ ++ ++int coroutine_mixed_fn qio_channel_read_all_eof(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = buf, .iov_len = buflen }; ++ return qio_channel_readv_all_eof(ioc, &iov, 1, errp); ++} ++ ++ ++int coroutine_mixed_fn qio_channel_read_all(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = buf, .iov_len = buflen }; ++ return qio_channel_readv_all(ioc, &iov, 1, errp); ++} ++ ++ ++int coroutine_mixed_fn qio_channel_write_all(QIOChannel *ioc, ++ const char *buf, ++ size_t buflen, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen }; ++ return qio_channel_writev_all(ioc, &iov, 1, errp); ++} ++ ++ ++int qio_channel_set_blocking(QIOChannel *ioc, ++ bool enabled, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ return klass->io_set_blocking(ioc, enabled, errp); ++} ++ ++ ++void qio_channel_set_follow_coroutine_ctx(QIOChannel *ioc, bool enabled) ++{ ++ ioc->follow_coroutine_ctx = enabled; ++} ++ ++ ++int qio_channel_close(QIOChannel *ioc, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ return klass->io_close(ioc, errp); ++} ++ ++ ++GSource *qio_channel_create_watch(QIOChannel *ioc, ++ GIOCondition condition) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ GSource *ret = klass->io_create_watch(ioc, condition); ++ ++ if (ioc->name) { ++ g_source_set_name(ret, ioc->name); ++ } ++ ++ return ret; ++} ++ ++ ++void qio_channel_set_aio_fd_handler(QIOChannel *ioc, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ klass->io_set_aio_fd_handler(ioc, read_ctx, io_read, write_ctx, io_write, ++ opaque); ++} ++ ++guint qio_channel_add_watch_full(QIOChannel *ioc, ++ GIOCondition condition, ++ QIOChannelFunc func, ++ gpointer user_data, ++ GDestroyNotify notify, ++ GMainContext *context) ++{ ++ GSource *source; ++ guint id; ++ ++ source = qio_channel_create_watch(ioc, condition); ++ ++ g_source_set_callback(source, (GSourceFunc)func, user_data, notify); ++ ++ id = g_source_attach(source, context); ++ g_source_unref(source); ++ ++ return id; ++} ++ ++guint qio_channel_add_watch(QIOChannel *ioc, ++ GIOCondition condition, ++ QIOChannelFunc func, ++ gpointer user_data, ++ GDestroyNotify notify) ++{ ++ return qio_channel_add_watch_full(ioc, condition, func, ++ user_data, notify, NULL); ++} ++ ++GSource *qio_channel_add_watch_source(QIOChannel *ioc, ++ GIOCondition condition, ++ QIOChannelFunc func, ++ gpointer user_data, ++ GDestroyNotify notify, ++ GMainContext *context) ++{ ++ GSource *source; ++ guint id; ++ ++ id = qio_channel_add_watch_full(ioc, condition, func, ++ user_data, notify, context); ++ source = g_main_context_find_source_by_id(context, id); ++ g_source_ref(source); ++ return source; ++} ++ ++ ++ssize_t qio_channel_pwritev(QIOChannel *ioc, const struct iovec *iov, ++ size_t niov, off_t offset, Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (!klass->io_pwritev) { ++ error_setg(errp, "Channel does not support pwritev"); ++ return -1; ++ } ++ ++ if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_SEEKABLE)) { ++ error_setg_errno(errp, EINVAL, "Requested channel is not seekable"); ++ return -1; ++ } ++ ++ return klass->io_pwritev(ioc, iov, niov, offset, errp); ++} ++ ++ssize_t qio_channel_pwrite(QIOChannel *ioc, char *buf, size_t buflen, ++ off_t offset, Error **errp) ++{ ++ struct iovec iov = { ++ .iov_base = buf, ++ .iov_len = buflen ++ }; ++ ++ return qio_channel_pwritev(ioc, &iov, 1, offset, errp); ++} ++ ++ssize_t qio_channel_preadv(QIOChannel *ioc, const struct iovec *iov, ++ size_t niov, off_t offset, Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (!klass->io_preadv) { ++ error_setg(errp, "Channel does not support preadv"); ++ return -1; ++ } ++ ++ if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_SEEKABLE)) { ++ error_setg_errno(errp, EINVAL, "Requested channel is not seekable"); ++ return -1; ++ } ++ ++ return klass->io_preadv(ioc, iov, niov, offset, errp); ++} ++ ++ssize_t qio_channel_pread(QIOChannel *ioc, char *buf, size_t buflen, ++ off_t offset, Error **errp) ++{ ++ struct iovec iov = { ++ .iov_base = buf, ++ .iov_len = buflen ++ }; ++ ++ return qio_channel_preadv(ioc, &iov, 1, offset, errp); ++} ++ ++int qio_channel_shutdown(QIOChannel *ioc, ++ QIOChannelShutdown how, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (!klass->io_shutdown) { ++ error_setg(errp, "Data path shutdown not supported"); ++ return -1; ++ } ++ ++ return klass->io_shutdown(ioc, how, errp); ++} ++ ++ ++void qio_channel_set_delay(QIOChannel *ioc, ++ bool enabled) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (klass->io_set_delay) { ++ klass->io_set_delay(ioc, enabled); ++ } ++} ++ ++ ++void qio_channel_set_cork(QIOChannel *ioc, ++ bool enabled) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (klass->io_set_cork) { ++ klass->io_set_cork(ioc, enabled); ++ } ++} ++ ++int qio_channel_get_peerpid(QIOChannel *ioc, ++ unsigned int *pid, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (!klass->io_peerpid) { ++ error_setg(errp, "Channel does not support peer pid"); ++ return -1; ++ } ++ klass->io_peerpid(ioc, pid, errp); ++ return 0; ++} ++ ++off_t qio_channel_io_seek(QIOChannel *ioc, ++ off_t offset, ++ int whence, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (!klass->io_seek) { ++ error_setg(errp, "Channel does not support random access"); ++ return -1; ++ } ++ ++ return klass->io_seek(ioc, offset, whence, errp); ++} ++ ++int qio_channel_flush(QIOChannel *ioc, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (!klass->io_flush || ++ !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY)) { ++ return 0; ++ } ++ ++ return klass->io_flush(ioc, errp); ++} ++ ++ ++static void qio_channel_restart_read(void *opaque) ++{ ++ QIOChannel *ioc = opaque; ++ Coroutine *co = qatomic_xchg(&ioc->read_coroutine, NULL); ++ ++ if (!co) { ++ return; ++ } ++ ++ /* Assert that aio_co_wake() reenters the coroutine directly */ ++ assert(qemu_get_current_aio_context() == ++ qemu_coroutine_get_aio_context(co)); ++ aio_co_wake(co); ++} ++ ++static void qio_channel_restart_write(void *opaque) ++{ ++ QIOChannel *ioc = opaque; ++ Coroutine *co = qatomic_xchg(&ioc->write_coroutine, NULL); ++ ++ if (!co) { ++ return; ++ } ++ ++ /* Assert that aio_co_wake() reenters the coroutine directly */ ++ assert(qemu_get_current_aio_context() == ++ qemu_coroutine_get_aio_context(co)); ++ aio_co_wake(co); ++} ++ ++static void coroutine_fn ++qio_channel_set_fd_handlers(QIOChannel *ioc, GIOCondition condition) ++{ ++ AioContext *ctx = ioc->follow_coroutine_ctx ? ++ qemu_coroutine_get_aio_context(qemu_coroutine_self()) : ++ iohandler_get_aio_context(); ++ AioContext *read_ctx = NULL; ++ IOHandler *io_read = NULL; ++ AioContext *write_ctx = NULL; ++ IOHandler *io_write = NULL; ++ ++ if (condition == G_IO_IN) { ++ ioc->read_coroutine = qemu_coroutine_self(); ++ ioc->read_ctx = ctx; ++ read_ctx = ctx; ++ io_read = qio_channel_restart_read; ++ ++ /* ++ * Thread safety: if the other coroutine is set and its AioContext ++ * matches ours, then there is mutual exclusion between read and write ++ * because they share a single thread and it's safe to set both read ++ * and write fd handlers here. If the AioContext does not match ours, ++ * then both threads may run in parallel but there is no shared state ++ * to worry about. ++ */ ++ if (ioc->write_coroutine && ioc->write_ctx == ctx) { ++ write_ctx = ctx; ++ io_write = qio_channel_restart_write; ++ } ++ } else if (condition == G_IO_OUT) { ++ ioc->write_coroutine = qemu_coroutine_self(); ++ ioc->write_ctx = ctx; ++ write_ctx = ctx; ++ io_write = qio_channel_restart_write; ++ if (ioc->read_coroutine && ioc->read_ctx == ctx) { ++ read_ctx = ctx; ++ io_read = qio_channel_restart_read; ++ } ++ } else { ++ abort(); ++ } ++ ++ qio_channel_set_aio_fd_handler(ioc, read_ctx, io_read, ++ write_ctx, io_write, ioc); ++} ++ ++static void coroutine_fn ++qio_channel_clear_fd_handlers(QIOChannel *ioc, GIOCondition condition) ++{ ++ AioContext *read_ctx = NULL; ++ IOHandler *io_read = NULL; ++ AioContext *write_ctx = NULL; ++ IOHandler *io_write = NULL; ++ AioContext *ctx; ++ ++ if (condition == G_IO_IN) { ++ ctx = ioc->read_ctx; ++ read_ctx = ctx; ++ io_read = NULL; ++ if (ioc->write_coroutine && ioc->write_ctx == ctx) { ++ write_ctx = ctx; ++ io_write = qio_channel_restart_write; ++ } ++ } else if (condition == G_IO_OUT) { ++ ctx = ioc->write_ctx; ++ write_ctx = ctx; ++ io_write = NULL; ++ if (ioc->read_coroutine && ioc->read_ctx == ctx) { ++ read_ctx = ctx; ++ io_read = qio_channel_restart_read; ++ } ++ } else { ++ abort(); ++ } ++ ++ qio_channel_set_aio_fd_handler(ioc, read_ctx, io_read, ++ write_ctx, io_write, ioc); ++} ++ ++void coroutine_fn qio_channel_yield(QIOChannel *ioc, ++ GIOCondition condition) ++{ ++ AioContext *ioc_ctx; ++ ++ assert(qemu_in_coroutine()); ++ ioc_ctx = qemu_coroutine_get_aio_context(qemu_coroutine_self()); ++ ++ if (condition == G_IO_IN) { ++ assert(!ioc->read_coroutine); ++ } else if (condition == G_IO_OUT) { ++ assert(!ioc->write_coroutine); ++ } else { ++ abort(); ++ } ++ qio_channel_set_fd_handlers(ioc, condition); ++ qemu_coroutine_yield(); ++ assert(in_aio_context_home_thread(ioc_ctx)); ++ ++ /* Allow interrupting the operation by reentering the coroutine other than ++ * through the aio_fd_handlers. */ ++ if (condition == G_IO_IN) { ++ assert(ioc->read_coroutine == NULL); ++ } else if (condition == G_IO_OUT) { ++ assert(ioc->write_coroutine == NULL); ++ } ++ qio_channel_clear_fd_handlers(ioc, condition); ++} ++ ++void qio_channel_wake_read(QIOChannel *ioc) ++{ ++ Coroutine *co = qatomic_xchg(&ioc->read_coroutine, NULL); ++ if (co) { ++ aio_co_wake(co); ++ } ++} ++ ++static gboolean qio_channel_wait_complete(QIOChannel *ioc, ++ GIOCondition condition, ++ gpointer opaque) ++{ ++ GMainLoop *loop = opaque; ++ ++ g_main_loop_quit(loop); ++ return FALSE; ++} ++ ++ ++void qio_channel_wait(QIOChannel *ioc, ++ GIOCondition condition) ++{ ++ GMainContext *ctxt = g_main_context_new(); ++ GMainLoop *loop = g_main_loop_new(ctxt, TRUE); ++ GSource *source; ++ ++ source = qio_channel_create_watch(ioc, condition); ++ ++ g_source_set_callback(source, ++ (GSourceFunc)qio_channel_wait_complete, ++ loop, ++ NULL); ++ ++ g_source_attach(source, ctxt); ++ ++ g_main_loop_run(loop); ++ ++ g_source_unref(source); ++ g_main_loop_unref(loop); ++ g_main_context_unref(ctxt); ++} ++ ++ ++static void qio_channel_finalize(Object *obj) ++{ ++ QIOChannel *ioc = QIO_CHANNEL(obj); ++ ++ /* Must not have coroutines in qio_channel_yield() */ ++ assert(!ioc->read_coroutine); ++ assert(!ioc->write_coroutine); ++ ++ g_free(ioc->name); ++ ++#ifdef _WIN32 ++ if (ioc->event) { ++ CloseHandle(ioc->event); ++ } ++#endif ++} ++ ++static const TypeInfo qio_channel_info = { ++ .parent = TYPE_OBJECT, ++ .name = TYPE_QIO_CHANNEL, ++ .instance_size = sizeof(QIOChannel), ++ .instance_finalize = qio_channel_finalize, ++ .abstract = true, ++ .class_size = sizeof(QIOChannelClass), ++}; ++ ++ ++static void qio_channel_register_types(void) ++{ ++ type_register_static(&qio_channel_info); ++} ++ ++ ++type_init(qio_channel_register_types); +diff --git a/qcow2/lib/io/task.c b/qcow2/lib/io/task.c +new file mode 100644 +index 00000000..451f26f8 +--- /dev/null ++++ b/qcow2/lib/io/task.c +@@ -0,0 +1,241 @@ ++/* ++ * QEMU I/O task ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "io/task.h" ++#include "qapi/error.h" ++#include "qemu/thread.h" ++#include "qom/object.h" ++#include "trace.h" ++ ++struct QIOTaskThreadData { ++ QIOTaskWorker worker; ++ gpointer opaque; ++ GDestroyNotify destroy; ++ GMainContext *context; ++ GSource *completion; ++}; ++ ++ ++struct QIOTask { ++ Object *source; ++ QIOTaskFunc func; ++ gpointer opaque; ++ GDestroyNotify destroy; ++ Error *err; ++ gpointer result; ++ GDestroyNotify destroyResult; ++ QemuMutex thread_lock; ++ QemuCond thread_cond; ++ struct QIOTaskThreadData *thread; ++}; ++ ++ ++QIOTask *qio_task_new(Object *source, ++ QIOTaskFunc func, ++ gpointer opaque, ++ GDestroyNotify destroy) ++{ ++ QIOTask *task; ++ ++ task = g_new0(QIOTask, 1); ++ ++ task->source = source; ++ object_ref(source); ++ task->func = func; ++ task->opaque = opaque; ++ task->destroy = destroy; ++ qemu_mutex_init(&task->thread_lock); ++ qemu_cond_init(&task->thread_cond); ++ ++ trace_qio_task_new(task, source, func, opaque); ++ ++ return task; ++} ++ ++static void qio_task_free(QIOTask *task) ++{ ++ qemu_mutex_lock(&task->thread_lock); ++ if (task->thread) { ++ if (task->thread->destroy) { ++ task->thread->destroy(task->thread->opaque); ++ } ++ ++ if (task->thread->context) { ++ g_main_context_unref(task->thread->context); ++ } ++ ++ g_free(task->thread); ++ } ++ ++ if (task->destroy) { ++ task->destroy(task->opaque); ++ } ++ if (task->destroyResult) { ++ task->destroyResult(task->result); ++ } ++ if (task->err) { ++ error_free(task->err); ++ } ++ object_unref(task->source); ++ ++ qemu_mutex_unlock(&task->thread_lock); ++ qemu_mutex_destroy(&task->thread_lock); ++ qemu_cond_destroy(&task->thread_cond); ++ ++ g_free(task); ++} ++ ++ ++static gboolean qio_task_thread_result(gpointer opaque) ++{ ++ QIOTask *task = opaque; ++ ++ trace_qio_task_thread_result(task); ++ qio_task_complete(task); ++ ++ return FALSE; ++} ++ ++ ++static gpointer qio_task_thread_worker(gpointer opaque) ++{ ++ QIOTask *task = opaque; ++ ++ trace_qio_task_thread_run(task); ++ ++ task->thread->worker(task, task->thread->opaque); ++ ++ /* We're running in the background thread, and must only ++ * ever report the task results in the main event loop ++ * thread. So we schedule an idle callback to report ++ * the worker results ++ */ ++ trace_qio_task_thread_exit(task); ++ ++ qemu_mutex_lock(&task->thread_lock); ++ ++ task->thread->completion = g_idle_source_new(); ++ g_source_set_callback(task->thread->completion, ++ qio_task_thread_result, task, NULL); ++ g_source_attach(task->thread->completion, ++ task->thread->context); ++ g_source_unref(task->thread->completion); ++ trace_qio_task_thread_source_attach(task, task->thread->completion); ++ ++ qemu_cond_signal(&task->thread_cond); ++ qemu_mutex_unlock(&task->thread_lock); ++ ++ return NULL; ++} ++ ++ ++void qio_task_run_in_thread(QIOTask *task, ++ QIOTaskWorker worker, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context) ++{ ++ struct QIOTaskThreadData *data = g_new0(struct QIOTaskThreadData, 1); ++ QemuThread thread; ++ ++ if (context) { ++ g_main_context_ref(context); ++ } ++ ++ data->worker = worker; ++ data->opaque = opaque; ++ data->destroy = destroy; ++ data->context = context; ++ ++ task->thread = data; ++ ++ trace_qio_task_thread_start(task, worker, opaque); ++ qemu_thread_create(&thread, ++ "io-task-worker", ++ qio_task_thread_worker, ++ task, ++ QEMU_THREAD_DETACHED); ++} ++ ++ ++void qio_task_wait_thread(QIOTask *task) ++{ ++ qemu_mutex_lock(&task->thread_lock); ++ g_assert(task->thread != NULL); ++ while (task->thread->completion == NULL) { ++ qemu_cond_wait(&task->thread_cond, &task->thread_lock); ++ } ++ ++ trace_qio_task_thread_source_cancel(task, task->thread->completion); ++ g_source_destroy(task->thread->completion); ++ qemu_mutex_unlock(&task->thread_lock); ++ ++ qio_task_thread_result(task); ++} ++ ++ ++void qio_task_complete(QIOTask *task) ++{ ++ task->func(task, task->opaque); ++ trace_qio_task_complete(task); ++ qio_task_free(task); ++} ++ ++ ++void qio_task_set_error(QIOTask *task, ++ Error *err) ++{ ++ error_propagate(&task->err, err); ++} ++ ++ ++bool qio_task_propagate_error(QIOTask *task, ++ Error **errp) ++{ ++ if (task->err) { ++ error_propagate(errp, task->err); ++ task->err = NULL; ++ return true; ++ } ++ ++ return false; ++} ++ ++ ++void qio_task_set_result_pointer(QIOTask *task, ++ gpointer result, ++ GDestroyNotify destroy) ++{ ++ task->result = result; ++ task->destroyResult = destroy; ++} ++ ++ ++gpointer qio_task_get_result_pointer(QIOTask *task) ++{ ++ return task->result; ++} ++ ++ ++Object *qio_task_get_source(QIOTask *task) ++{ ++ return task->source; ++} +diff --git a/qcow2/lib/job-qmp.c b/qcow2/lib/job-qmp.c +new file mode 100644 +index 00000000..9e26fa89 +--- /dev/null ++++ b/qcow2/lib/job-qmp.c +@@ -0,0 +1,188 @@ ++/* ++ * QMP interface for background jobs ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012, 2018 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/job.h" ++#include "qapi/qapi-commands-job.h" ++#include "qapi/error.h" ++#include "trace/trace-root.h" ++ ++/* ++ * Get a job using its ID. Called with job_mutex held. ++ */ ++static Job *find_job_locked(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ job = job_get_locked(id); ++ if (!job) { ++ error_setg(errp, "Job not found"); ++ return NULL; ++ } ++ ++ return job; ++} ++ ++void qmp_job_cancel(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_cancel(job); ++ job_user_cancel_locked(job, true, errp); ++} ++ ++void qmp_job_pause(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_pause(job); ++ job_user_pause_locked(job, errp); ++} ++ ++void qmp_job_resume(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_resume(job); ++ job_user_resume_locked(job, errp); ++} ++ ++void qmp_job_complete(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_complete(job); ++ job_complete_locked(job, errp); ++} ++ ++void qmp_job_finalize(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_finalize(job); ++ job_ref_locked(job); ++ job_finalize_locked(job, errp); ++ ++ job_unref_locked(job); ++} ++ ++void qmp_job_dismiss(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_dismiss(job); ++ job_dismiss_locked(&job, errp); ++} ++ ++/* Called with job_mutex held. */ ++static JobInfo *job_query_single_locked(Job *job, Error **errp) ++{ ++ JobInfo *info; ++ uint64_t progress_current; ++ uint64_t progress_total; ++ ++ assert(!job_is_internal(job)); ++ progress_get_snapshot(&job->progress, &progress_current, ++ &progress_total); ++ ++ info = g_new(JobInfo, 1); ++ *info = (JobInfo) { ++ .id = g_strdup(job->id), ++ .type = job_type(job), ++ .status = job->status, ++ .current_progress = progress_current, ++ .total_progress = progress_total, ++ .error = job->err ? ++ g_strdup(error_get_pretty(job->err)) : NULL, ++ }; ++ ++ return info; ++} ++ ++JobInfoList *qmp_query_jobs(Error **errp) ++{ ++ JobInfoList *head = NULL, **tail = &head; ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ ++ for (job = job_next_locked(NULL); job; job = job_next_locked(job)) { ++ JobInfo *value; ++ ++ if (job_is_internal(job)) { ++ continue; ++ } ++ value = job_query_single_locked(job, errp); ++ if (!value) { ++ qapi_free_JobInfoList(head); ++ return NULL; ++ } ++ QAPI_LIST_APPEND(tail, value); ++ } ++ ++ return head; ++} +diff --git a/qcow2/lib/job.c b/qcow2/lib/job.c +new file mode 100644 +index 00000000..660ce22c +--- /dev/null ++++ b/qcow2/lib/job.c +@@ -0,0 +1,1264 @@ ++/* ++ * Background jobs (long-running operations) ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012, 2018 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/job.h" ++#include "qemu/id.h" ++#include "qemu/main-loop.h" ++#include "block/aio-wait.h" ++#include "trace/trace-root.h" ++#include "qapi/qapi-events-job.h" ++ ++/* ++ * The job API is composed of two categories of functions. ++ * ++ * The first includes functions used by the monitor. The monitor is ++ * peculiar in that it accesses the job list with job_get, and ++ * therefore needs consistency across job_get and the actual operation ++ * (e.g. job_user_cancel). To achieve this consistency, the caller ++ * calls job_lock/job_unlock itself around the whole operation. ++ * ++ * ++ * The second includes functions used by the job drivers and sometimes ++ * by the core block layer. These delegate the locking to the callee instead. ++ */ ++ ++/* ++ * job_mutex protects the jobs list, but also makes the ++ * struct job fields thread-safe. ++ */ ++QemuMutex job_mutex; ++ ++/* Protected by job_mutex */ ++static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); ++ ++/* Job State Transition Table */ ++bool JobSTT[JOB_STATUS__MAX][JOB_STATUS__MAX] = { ++ /* U, C, R, P, Y, S, W, D, X, E, N */ ++ /* U: */ [JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ /* C: */ [JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, ++ /* R: */ [JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0}, ++ /* P: */ [JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, ++ /* Y: */ [JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0}, ++ /* S: */ [JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, ++ /* W: */ [JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, ++ /* D: */ [JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, ++ /* X: */ [JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, ++ /* E: */ [JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, ++ /* N: */ [JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++}; ++ ++bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { ++ /* U, C, R, P, Y, S, W, D, X, E, N */ ++ [JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, ++ [JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, ++ [JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, ++ [JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, ++ [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0}, ++ [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, ++ [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, ++ [JOB_VERB_CHANGE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, ++}; ++ ++/* Transactional group of jobs */ ++struct JobTxn { ++ ++ /* Is this txn being cancelled? */ ++ bool aborting; ++ ++ /* List of jobs */ ++ QLIST_HEAD(, Job) jobs; ++ ++ /* Reference count */ ++ int refcnt; ++}; ++ ++void job_lock(void) ++{ ++ qemu_mutex_lock(&job_mutex); ++} ++ ++void job_unlock(void) ++{ ++ qemu_mutex_unlock(&job_mutex); ++} ++ ++static void __attribute__((__constructor__)) job_init(void) ++{ ++ qemu_mutex_init(&job_mutex); ++} ++ ++JobTxn *job_txn_new(void) ++{ ++ JobTxn *txn = g_new0(JobTxn, 1); ++ QLIST_INIT(&txn->jobs); ++ txn->refcnt = 1; ++ return txn; ++} ++ ++/* Called with job_mutex held. */ ++static void job_txn_ref_locked(JobTxn *txn) ++{ ++ txn->refcnt++; ++} ++ ++void job_txn_unref_locked(JobTxn *txn) ++{ ++ if (txn && --txn->refcnt == 0) { ++ g_free(txn); ++ } ++} ++ ++void job_txn_unref(JobTxn *txn) ++{ ++ JOB_LOCK_GUARD(); ++ job_txn_unref_locked(txn); ++} ++ ++/** ++ * @txn: The transaction (may be NULL) ++ * @job: Job to add to the transaction ++ * ++ * Add @job to the transaction. The @job must not already be in a transaction. ++ * The caller must call either job_txn_unref() or job_completed() to release ++ * the reference that is automatically grabbed here. ++ * ++ * If @txn is NULL, the function does nothing. ++ * ++ * Called with job_mutex held. ++ */ ++static void job_txn_add_job_locked(JobTxn *txn, Job *job) ++{ ++ if (!txn) { ++ return; ++ } ++ ++ assert(!job->txn); ++ job->txn = txn; ++ ++ QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); ++ job_txn_ref_locked(txn); ++} ++ ++/* Called with job_mutex held. */ ++static void job_txn_del_job_locked(Job *job) ++{ ++ if (job->txn) { ++ QLIST_REMOVE(job, txn_list); ++ job_txn_unref_locked(job->txn); ++ job->txn = NULL; ++ } ++} ++ ++/* Called with job_mutex held, but releases it temporarily. */ ++static int job_txn_apply_locked(Job *job, int fn(Job *)) ++{ ++ Job *other_job, *next; ++ JobTxn *txn = job->txn; ++ int rc = 0; ++ ++ /* ++ * Similar to job_completed_txn_abort, we take each job's lock before ++ * applying fn, but since we assume that outer_ctx is held by the caller, ++ * we need to release it here to avoid holding the lock twice - which would ++ * break AIO_WAIT_WHILE from within fn. ++ */ ++ job_ref_locked(job); ++ ++ QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) { ++ rc = fn(other_job); ++ if (rc) { ++ break; ++ } ++ } ++ ++ job_unref_locked(job); ++ return rc; ++} ++ ++bool job_is_internal(Job *job) ++{ ++ return (job->id == NULL); ++} ++ ++/* Called with job_mutex held. */ ++static void job_state_transition_locked(Job *job, JobStatus s1) ++{ ++ JobStatus s0 = job->status; ++ assert(s1 >= 0 && s1 < JOB_STATUS__MAX); ++ trace_job_state_transition(job, job->ret, ++ JobSTT[s0][s1] ? "allowed" : "disallowed", ++ JobStatus_str(s0), JobStatus_str(s1)); ++ assert(JobSTT[s0][s1]); ++ job->status = s1; ++ ++ if (!job_is_internal(job) && s1 != s0) { ++ qapi_event_send_job_status_change(job->id, job->status); ++ } ++} ++ ++int job_apply_verb_locked(Job *job, JobVerb verb, Error **errp) ++{ ++ JobStatus s0 = job->status; ++ assert(verb >= 0 && verb < JOB_VERB__MAX); ++ trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), ++ JobVerbTable[verb][s0] ? "allowed" : "prohibited"); ++ if (JobVerbTable[verb][s0]) { ++ return 0; ++ } ++ error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", ++ job->id, JobStatus_str(s0), JobVerb_str(verb)); ++ return -EPERM; ++} ++ ++JobType job_type(const Job *job) ++{ ++ return job->driver->job_type; ++} ++ ++const char *job_type_str(const Job *job) ++{ ++ return JobType_str(job_type(job)); ++} ++ ++bool job_is_cancelled_locked(Job *job) ++{ ++ /* force_cancel may be true only if cancelled is true, too */ ++ assert(job->cancelled || !job->force_cancel); ++ return job->force_cancel; ++} ++ ++bool job_is_cancelled(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ return job_is_cancelled_locked(job); ++} ++ ++/* Called with job_mutex held. */ ++static bool job_cancel_requested_locked(Job *job) ++{ ++ return job->cancelled; ++} ++ ++bool job_cancel_requested(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ return job_cancel_requested_locked(job); ++} ++ ++bool job_is_ready_locked(Job *job) ++{ ++ switch (job->status) { ++ case JOB_STATUS_UNDEFINED: ++ case JOB_STATUS_CREATED: ++ case JOB_STATUS_RUNNING: ++ case JOB_STATUS_PAUSED: ++ case JOB_STATUS_WAITING: ++ case JOB_STATUS_PENDING: ++ case JOB_STATUS_ABORTING: ++ case JOB_STATUS_CONCLUDED: ++ case JOB_STATUS_NULL: ++ return false; ++ case JOB_STATUS_READY: ++ case JOB_STATUS_STANDBY: ++ return true; ++ default: ++ g_assert_not_reached(); ++ } ++ return false; ++} ++ ++bool job_is_ready(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ return job_is_ready_locked(job); ++} ++ ++bool job_is_completed_locked(Job *job) ++{ ++ switch (job->status) { ++ case JOB_STATUS_UNDEFINED: ++ case JOB_STATUS_CREATED: ++ case JOB_STATUS_RUNNING: ++ case JOB_STATUS_PAUSED: ++ case JOB_STATUS_READY: ++ case JOB_STATUS_STANDBY: ++ return false; ++ case JOB_STATUS_WAITING: ++ case JOB_STATUS_PENDING: ++ case JOB_STATUS_ABORTING: ++ case JOB_STATUS_CONCLUDED: ++ case JOB_STATUS_NULL: ++ return true; ++ default: ++ g_assert_not_reached(); ++ } ++ return false; ++} ++ ++static bool job_is_completed(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ return job_is_completed_locked(job); ++} ++ ++static bool job_started_locked(Job *job) ++{ ++ return job->co; ++} ++ ++/* Called with job_mutex held. */ ++static bool job_should_pause_locked(Job *job) ++{ ++ return job->pause_count > 0; ++} ++ ++Job *job_next_locked(Job *job) ++{ ++ if (!job) { ++ return QLIST_FIRST(&jobs); ++ } ++ return QLIST_NEXT(job, job_list); ++} ++ ++Job *job_next(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ return job_next_locked(job); ++} ++ ++Job *job_get_locked(const char *id) ++{ ++ Job *job; ++ ++ QLIST_FOREACH(job, &jobs, job_list) { ++ if (job->id && !strcmp(id, job->id)) { ++ return job; ++ } ++ } ++ ++ return NULL; ++} ++ ++void job_set_aio_context(Job *job, AioContext *ctx) ++{ ++ /* protect against read in job_finish_sync_locked and job_start */ ++ GLOBAL_STATE_CODE(); ++ /* protect against read in job_do_yield_locked */ ++ JOB_LOCK_GUARD(); ++ /* ensure the job is quiescent while the AioContext is changed */ ++ assert(job->paused || job_is_completed_locked(job)); ++ job->aio_context = ctx; ++} ++ ++/* Called with job_mutex *not* held. */ ++static void job_sleep_timer_cb(void *opaque) ++{ ++ Job *job = opaque; ++ ++ job_enter(job); ++} ++ ++void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, ++ AioContext *ctx, int flags, BlockCompletionFunc *cb, ++ void *opaque, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ ++ if (job_id) { ++ if (flags & JOB_INTERNAL) { ++ error_setg(errp, "Cannot specify job ID for internal job"); ++ return NULL; ++ } ++ if (!id_wellformed(job_id)) { ++ error_setg(errp, "Invalid job ID '%s'", job_id); ++ return NULL; ++ } ++ if (job_get_locked(job_id)) { ++ error_setg(errp, "Job ID '%s' already in use", job_id); ++ return NULL; ++ } ++ } else if (!(flags & JOB_INTERNAL)) { ++ error_setg(errp, "An explicit job ID is required"); ++ return NULL; ++ } ++ ++ job = g_malloc0(driver->instance_size); ++ job->driver = driver; ++ job->id = g_strdup(job_id); ++ job->refcnt = 1; ++ job->aio_context = ctx; ++ job->busy = false; ++ job->paused = true; ++ job->pause_count = 1; ++ job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE); ++ job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS); ++ job->cb = cb; ++ job->opaque = opaque; ++ ++ progress_init(&job->progress); ++ ++ notifier_list_init(&job->on_finalize_cancelled); ++ notifier_list_init(&job->on_finalize_completed); ++ notifier_list_init(&job->on_pending); ++ notifier_list_init(&job->on_ready); ++ notifier_list_init(&job->on_idle); ++ ++ job_state_transition_locked(job, JOB_STATUS_CREATED); ++ aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, ++ QEMU_CLOCK_REALTIME, SCALE_NS, ++ job_sleep_timer_cb, job); ++ ++ QLIST_INSERT_HEAD(&jobs, job, job_list); ++ ++ /* Single jobs are modeled as single-job transactions for sake of ++ * consolidating the job management logic */ ++ if (!txn) { ++ txn = job_txn_new(); ++ job_txn_add_job_locked(txn, job); ++ job_txn_unref_locked(txn); ++ } else { ++ job_txn_add_job_locked(txn, job); ++ } ++ ++ return job; ++} ++ ++void job_ref_locked(Job *job) ++{ ++ ++job->refcnt; ++} ++ ++void job_unref_locked(Job *job) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ if (--job->refcnt == 0) { ++ assert(job->status == JOB_STATUS_NULL); ++ assert(!timer_pending(&job->sleep_timer)); ++ assert(!job->txn); ++ ++ if (job->driver->free) { ++ job_unlock(); ++ job->driver->free(job); ++ job_lock(); ++ } ++ ++ QLIST_REMOVE(job, job_list); ++ ++ progress_destroy(&job->progress); ++ error_free(job->err); ++ g_free(job->id); ++ g_free(job); ++ } ++} ++ ++void job_progress_update(Job *job, uint64_t done) ++{ ++ progress_work_done(&job->progress, done); ++} ++ ++void job_progress_set_remaining(Job *job, uint64_t remaining) ++{ ++ progress_set_remaining(&job->progress, remaining); ++} ++ ++void job_progress_increase_remaining(Job *job, uint64_t delta) ++{ ++ progress_increase_remaining(&job->progress, delta); ++} ++ ++/** ++ * To be called when a cancelled job is finalised. ++ * Called with job_mutex held. ++ */ ++static void job_event_cancelled_locked(Job *job) ++{ ++ notifier_list_notify(&job->on_finalize_cancelled, job); ++} ++ ++/** ++ * To be called when a successfully completed job is finalised. ++ * Called with job_mutex held. ++ */ ++static void job_event_completed_locked(Job *job) ++{ ++ notifier_list_notify(&job->on_finalize_completed, job); ++} ++ ++/* Called with job_mutex held. */ ++static void job_event_pending_locked(Job *job) ++{ ++ notifier_list_notify(&job->on_pending, job); ++} ++ ++/* Called with job_mutex held. */ ++static void job_event_ready_locked(Job *job) ++{ ++ notifier_list_notify(&job->on_ready, job); ++} ++ ++/* Called with job_mutex held. */ ++static void job_event_idle_locked(Job *job) ++{ ++ notifier_list_notify(&job->on_idle, job); ++} ++ ++void job_enter_cond_locked(Job *job, bool(*fn)(Job *job)) ++{ ++ if (!job_started_locked(job)) { ++ return; ++ } ++ if (job->deferred_to_main_loop) { ++ return; ++ } ++ ++ if (job->busy) { ++ return; ++ } ++ ++ if (fn && !fn(job)) { ++ return; ++ } ++ ++ assert(!job->deferred_to_main_loop); ++ timer_del(&job->sleep_timer); ++ job->busy = true; ++ job_unlock(); ++ aio_co_wake(job->co); ++ job_lock(); ++} ++ ++void job_enter(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ job_enter_cond_locked(job, NULL); ++} ++ ++/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. ++ * Reentering the job coroutine with job_enter() before the timer has expired ++ * is allowed and cancels the timer. ++ * ++ * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be ++ * called explicitly. ++ * ++ * Called with job_mutex held, but releases it temporarily. ++ */ ++static void coroutine_fn job_do_yield_locked(Job *job, uint64_t ns) ++{ ++ AioContext *next_aio_context; ++ ++ if (ns != -1) { ++ timer_mod(&job->sleep_timer, ns); ++ } ++ job->busy = false; ++ job_event_idle_locked(job); ++ job_unlock(); ++ qemu_coroutine_yield(); ++ job_lock(); ++ ++ next_aio_context = job->aio_context; ++ /* ++ * Coroutine has resumed, but in the meanwhile the job AioContext ++ * might have changed via bdrv_try_change_aio_context(), so we need to move ++ * the coroutine too in the new aiocontext. ++ */ ++ while (qemu_get_current_aio_context() != next_aio_context) { ++ job_unlock(); ++ aio_co_reschedule_self(next_aio_context); ++ job_lock(); ++ next_aio_context = job->aio_context; ++ } ++ ++ /* Set by job_enter_cond_locked() before re-entering the coroutine. */ ++ assert(job->busy); ++} ++ ++/* Called with job_mutex held, but releases it temporarily. */ ++static void coroutine_fn job_pause_point_locked(Job *job) ++{ ++ assert(job && job_started_locked(job)); ++ ++ if (!job_should_pause_locked(job)) { ++ return; ++ } ++ if (job_is_cancelled_locked(job)) { ++ return; ++ } ++ ++ if (job->driver->pause) { ++ job_unlock(); ++ job->driver->pause(job); ++ job_lock(); ++ } ++ ++ if (job_should_pause_locked(job) && !job_is_cancelled_locked(job)) { ++ JobStatus status = job->status; ++ job_state_transition_locked(job, status == JOB_STATUS_READY ++ ? JOB_STATUS_STANDBY ++ : JOB_STATUS_PAUSED); ++ job->paused = true; ++ job_do_yield_locked(job, -1); ++ job->paused = false; ++ job_state_transition_locked(job, status); ++ } ++ ++ if (job->driver->resume) { ++ job_unlock(); ++ job->driver->resume(job); ++ job_lock(); ++ } ++} ++ ++void coroutine_fn job_pause_point(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ job_pause_point_locked(job); ++} ++ ++void coroutine_fn job_yield(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ assert(job->busy); ++ ++ /* Check cancellation *before* setting busy = false, too! */ ++ if (job_is_cancelled_locked(job)) { ++ return; ++ } ++ ++ if (!job_should_pause_locked(job)) { ++ job_do_yield_locked(job, -1); ++ } ++ ++ job_pause_point_locked(job); ++} ++ ++void coroutine_fn job_sleep_ns(Job *job, int64_t ns) ++{ ++ JOB_LOCK_GUARD(); ++ assert(job->busy); ++ ++ /* Check cancellation *before* setting busy = false, too! */ ++ if (job_is_cancelled_locked(job)) { ++ return; ++ } ++ ++ if (!job_should_pause_locked(job)) { ++ job_do_yield_locked(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); ++ } ++ ++ job_pause_point_locked(job); ++} ++ ++/* Assumes the job_mutex is held */ ++static bool job_timer_not_pending_locked(Job *job) ++{ ++ return !timer_pending(&job->sleep_timer); ++} ++ ++void job_pause_locked(Job *job) ++{ ++ job->pause_count++; ++ if (!job->paused) { ++ job_enter_cond_locked(job, NULL); ++ } ++} ++ ++void job_pause(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ job_pause_locked(job); ++} ++ ++void job_resume_locked(Job *job) ++{ ++ assert(job->pause_count > 0); ++ job->pause_count--; ++ if (job->pause_count) { ++ return; ++ } ++ ++ /* kick only if no timer is pending */ ++ job_enter_cond_locked(job, job_timer_not_pending_locked); ++} ++ ++void job_resume(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ job_resume_locked(job); ++} ++ ++void job_user_pause_locked(Job *job, Error **errp) ++{ ++ if (job_apply_verb_locked(job, JOB_VERB_PAUSE, errp)) { ++ return; ++ } ++ if (job->user_paused) { ++ error_setg(errp, "Job is already paused"); ++ return; ++ } ++ job->user_paused = true; ++ job_pause_locked(job); ++} ++ ++bool job_user_paused_locked(Job *job) ++{ ++ return job->user_paused; ++} ++ ++void job_user_resume_locked(Job *job, Error **errp) ++{ ++ assert(job); ++ GLOBAL_STATE_CODE(); ++ if (!job->user_paused || job->pause_count <= 0) { ++ error_setg(errp, "Can't resume a job that was not paused"); ++ return; ++ } ++ if (job_apply_verb_locked(job, JOB_VERB_RESUME, errp)) { ++ return; ++ } ++ if (job->driver->user_resume) { ++ job_unlock(); ++ job->driver->user_resume(job); ++ job_lock(); ++ } ++ job->user_paused = false; ++ job_resume_locked(job); ++} ++ ++/* Called with job_mutex held, but releases it temporarily. */ ++static void job_do_dismiss_locked(Job *job) ++{ ++ assert(job); ++ job->busy = false; ++ job->paused = false; ++ job->deferred_to_main_loop = true; ++ ++ job_txn_del_job_locked(job); ++ ++ job_state_transition_locked(job, JOB_STATUS_NULL); ++ job_unref_locked(job); ++} ++ ++void job_dismiss_locked(Job **jobptr, Error **errp) ++{ ++ Job *job = *jobptr; ++ /* similarly to _complete, this is QMP-interface only. */ ++ assert(job->id); ++ if (job_apply_verb_locked(job, JOB_VERB_DISMISS, errp)) { ++ return; ++ } ++ ++ job_do_dismiss_locked(job); ++ *jobptr = NULL; ++} ++ ++void job_early_fail(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ assert(job->status == JOB_STATUS_CREATED); ++ job_do_dismiss_locked(job); ++} ++ ++/* Called with job_mutex held. */ ++static void job_conclude_locked(Job *job) ++{ ++ job_state_transition_locked(job, JOB_STATUS_CONCLUDED); ++ if (job->auto_dismiss || !job_started_locked(job)) { ++ job_do_dismiss_locked(job); ++ } ++} ++ ++/* Called with job_mutex held. */ ++static void job_update_rc_locked(Job *job) ++{ ++ if (!job->ret && job_is_cancelled_locked(job)) { ++ job->ret = -ECANCELED; ++ } ++ if (job->ret) { ++ if (!job->err) { ++ error_setg(&job->err, "%s", strerror(-job->ret)); ++ } ++ job_state_transition_locked(job, JOB_STATUS_ABORTING); ++ } ++} ++ ++static void job_commit(Job *job) ++{ ++ assert(!job->ret); ++ GLOBAL_STATE_CODE(); ++ if (job->driver->commit) { ++ job->driver->commit(job); ++ } ++} ++ ++static void job_abort(Job *job) ++{ ++ assert(job->ret); ++ GLOBAL_STATE_CODE(); ++ if (job->driver->abort) { ++ job->driver->abort(job); ++ } ++} ++ ++static void job_clean(Job *job) ++{ ++ GLOBAL_STATE_CODE(); ++ if (job->driver->clean) { ++ job->driver->clean(job); ++ } ++} ++ ++/* ++ * Called with job_mutex held, but releases it temporarily. ++ */ ++static int job_finalize_single_locked(Job *job) ++{ ++ int job_ret; ++ ++ assert(job_is_completed_locked(job)); ++ ++ /* Ensure abort is called for late-transactional failures */ ++ job_update_rc_locked(job); ++ ++ job_ret = job->ret; ++ job_unlock(); ++ ++ if (!job_ret) { ++ job_commit(job); ++ } else { ++ job_abort(job); ++ } ++ job_clean(job); ++ ++ if (job->cb) { ++ job->cb(job->opaque, job_ret); ++ } ++ ++ job_lock(); ++ ++ /* Emit events only if we actually started */ ++ if (job_started_locked(job)) { ++ if (job_is_cancelled_locked(job)) { ++ job_event_cancelled_locked(job); ++ } else { ++ job_event_completed_locked(job); ++ } ++ } ++ ++ job_txn_del_job_locked(job); ++ job_conclude_locked(job); ++ return 0; ++} ++ ++/* ++ * Called with job_mutex held, but releases it temporarily. ++ */ ++static void job_cancel_async_locked(Job *job, bool force) ++{ ++ GLOBAL_STATE_CODE(); ++ if (job->driver->cancel) { ++ job_unlock(); ++ force = job->driver->cancel(job, force); ++ job_lock(); ++ } else { ++ /* No .cancel() means the job will behave as if force-cancelled */ ++ force = true; ++ } ++ ++ if (job->user_paused) { ++ /* Do not call job_enter here, the caller will handle it. */ ++ if (job->driver->user_resume) { ++ job_unlock(); ++ job->driver->user_resume(job); ++ job_lock(); ++ } ++ job->user_paused = false; ++ assert(job->pause_count > 0); ++ job->pause_count--; ++ } ++ ++ /* ++ * Ignore soft cancel requests after the job is already done ++ * (We will still invoke job->driver->cancel() above, but if the ++ * job driver supports soft cancelling and the job is done, that ++ * should be a no-op, too. We still call it so it can override ++ * @force.) ++ */ ++ if (force || !job->deferred_to_main_loop) { ++ job->cancelled = true; ++ /* To prevent 'force == false' overriding a previous 'force == true' */ ++ job->force_cancel |= force; ++ } ++} ++ ++/* ++ * Called with job_mutex held, but releases it temporarily. ++ */ ++static void job_completed_txn_abort_locked(Job *job) ++{ ++ JobTxn *txn = job->txn; ++ Job *other_job; ++ ++ if (txn->aborting) { ++ /* ++ * We are cancelled by another job, which will handle everything. ++ */ ++ return; ++ } ++ txn->aborting = true; ++ job_txn_ref_locked(txn); ++ ++ job_ref_locked(job); ++ ++ /* Other jobs are effectively cancelled by us, set the status for ++ * them; this job, however, may or may not be cancelled, depending ++ * on the caller, so leave it. */ ++ QLIST_FOREACH(other_job, &txn->jobs, txn_list) { ++ if (other_job != job) { ++ /* ++ * This is a transaction: If one job failed, no result will matter. ++ * Therefore, pass force=true to terminate all other jobs as quickly ++ * as possible. ++ */ ++ job_cancel_async_locked(other_job, true); ++ } ++ } ++ while (!QLIST_EMPTY(&txn->jobs)) { ++ other_job = QLIST_FIRST(&txn->jobs); ++ if (!job_is_completed_locked(other_job)) { ++ assert(job_cancel_requested_locked(other_job)); ++ job_finish_sync_locked(other_job, NULL, NULL); ++ } ++ job_finalize_single_locked(other_job); ++ } ++ ++ job_unref_locked(job); ++ job_txn_unref_locked(txn); ++} ++ ++/* Called with job_mutex held, but releases it temporarily */ ++static int job_prepare_locked(Job *job) ++{ ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (job->ret == 0 && job->driver->prepare) { ++ job_unlock(); ++ ret = job->driver->prepare(job); ++ job_lock(); ++ job->ret = ret; ++ job_update_rc_locked(job); ++ } ++ ++ return job->ret; ++} ++ ++/* Called with job_mutex held */ ++static int job_needs_finalize_locked(Job *job) ++{ ++ return !job->auto_finalize; ++} ++ ++/* Called with job_mutex held */ ++static void job_do_finalize_locked(Job *job) ++{ ++ int rc; ++ assert(job && job->txn); ++ ++ /* prepare the transaction to complete */ ++ rc = job_txn_apply_locked(job, job_prepare_locked); ++ if (rc) { ++ job_completed_txn_abort_locked(job); ++ } else { ++ job_txn_apply_locked(job, job_finalize_single_locked); ++ } ++} ++ ++void job_finalize_locked(Job *job, Error **errp) ++{ ++ assert(job && job->id); ++ if (job_apply_verb_locked(job, JOB_VERB_FINALIZE, errp)) { ++ return; ++ } ++ job_do_finalize_locked(job); ++} ++ ++/* Called with job_mutex held. */ ++static int job_transition_to_pending_locked(Job *job) ++{ ++ job_state_transition_locked(job, JOB_STATUS_PENDING); ++ if (!job->auto_finalize) { ++ job_event_pending_locked(job); ++ } ++ return 0; ++} ++ ++void job_transition_to_ready(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ job_state_transition_locked(job, JOB_STATUS_READY); ++ job_event_ready_locked(job); ++} ++ ++/* Called with job_mutex held. */ ++static void job_completed_txn_success_locked(Job *job) ++{ ++ JobTxn *txn = job->txn; ++ Job *other_job; ++ ++ job_state_transition_locked(job, JOB_STATUS_WAITING); ++ ++ /* ++ * Successful completion, see if there are other running jobs in this ++ * txn. ++ */ ++ QLIST_FOREACH(other_job, &txn->jobs, txn_list) { ++ if (!job_is_completed_locked(other_job)) { ++ return; ++ } ++ assert(other_job->ret == 0); ++ } ++ ++ job_txn_apply_locked(job, job_transition_to_pending_locked); ++ ++ /* If no jobs need manual finalization, automatically do so */ ++ if (job_txn_apply_locked(job, job_needs_finalize_locked) == 0) { ++ job_do_finalize_locked(job); ++ } ++} ++ ++/* Called with job_mutex held. */ ++static void job_completed_locked(Job *job) ++{ ++ assert(job && job->txn && !job_is_completed_locked(job)); ++ ++ job_update_rc_locked(job); ++ trace_job_completed(job, job->ret); ++ if (job->ret) { ++ job_completed_txn_abort_locked(job); ++ } else { ++ job_completed_txn_success_locked(job); ++ } ++} ++ ++/** ++ * Useful only as a type shim for aio_bh_schedule_oneshot. ++ * Called with job_mutex *not* held. ++ */ ++static void job_exit(void *opaque) ++{ ++ Job *job = (Job *)opaque; ++ JOB_LOCK_GUARD(); ++ job_ref_locked(job); ++ ++ /* This is a lie, we're not quiescent, but still doing the completion ++ * callbacks. However, completion callbacks tend to involve operations that ++ * drain block nodes, and if .drained_poll still returned true, we would ++ * deadlock. */ ++ job->busy = false; ++ job_event_idle_locked(job); ++ ++ job_completed_locked(job); ++ job_unref_locked(job); ++} ++ ++/** ++ * All jobs must allow a pause point before entering their job proper. This ++ * ensures that jobs can be paused prior to being started, then resumed later. ++ */ ++static void coroutine_fn job_co_entry(void *opaque) ++{ ++ Job *job = opaque; ++ int ret; ++ ++ assert(job && job->driver && job->driver->run); ++ WITH_JOB_LOCK_GUARD() { ++ assert(job->aio_context == qemu_get_current_aio_context()); ++ job_pause_point_locked(job); ++ } ++ ret = job->driver->run(job, &job->err); ++ WITH_JOB_LOCK_GUARD() { ++ job->ret = ret; ++ job->deferred_to_main_loop = true; ++ job->busy = true; ++ } ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); ++} ++ ++void job_start(Job *job) ++{ ++ assert(qemu_in_main_thread()); ++ ++ WITH_JOB_LOCK_GUARD() { ++ assert(job && !job_started_locked(job) && job->paused && ++ job->driver && job->driver->run); ++ job->co = qemu_coroutine_create(job_co_entry, job); ++ job->pause_count--; ++ job->busy = true; ++ job->paused = false; ++ job_state_transition_locked(job, JOB_STATUS_RUNNING); ++ } ++ aio_co_enter(job->aio_context, job->co); ++} ++ ++void job_cancel_locked(Job *job, bool force) ++{ ++ if (job->status == JOB_STATUS_CONCLUDED) { ++ job_do_dismiss_locked(job); ++ return; ++ } ++ job_cancel_async_locked(job, force); ++ if (!job_started_locked(job)) { ++ job_completed_locked(job); ++ } else if (job->deferred_to_main_loop) { ++ /* ++ * job_cancel_async() ignores soft-cancel requests for jobs ++ * that are already done (i.e. deferred to the main loop). We ++ * have to check again whether the job is really cancelled. ++ * (job_cancel_requested() and job_is_cancelled() are equivalent ++ * here, because job_cancel_async() will make soft-cancel ++ * requests no-ops when deferred_to_main_loop is true. We ++ * choose to call job_is_cancelled() to show that we invoke ++ * job_completed_txn_abort() only for force-cancelled jobs.) ++ */ ++ if (job_is_cancelled_locked(job)) { ++ job_completed_txn_abort_locked(job); ++ } ++ } else { ++ job_enter_cond_locked(job, NULL); ++ } ++} ++ ++void job_user_cancel_locked(Job *job, bool force, Error **errp) ++{ ++ if (job_apply_verb_locked(job, JOB_VERB_CANCEL, errp)) { ++ return; ++ } ++ job_cancel_locked(job, force); ++} ++ ++/* A wrapper around job_cancel_locked() taking an Error ** parameter so it may ++ * be used with job_finish_sync_locked() without the need for (rather nasty) ++ * function pointer casts there. ++ * ++ * Called with job_mutex held. ++ */ ++static void job_cancel_err_locked(Job *job, Error **errp) ++{ ++ job_cancel_locked(job, false); ++} ++ ++/** ++ * Same as job_cancel_err(), but force-cancel. ++ * Called with job_mutex held. ++ */ ++static void job_force_cancel_err_locked(Job *job, Error **errp) ++{ ++ job_cancel_locked(job, true); ++} ++ ++int job_cancel_sync_locked(Job *job, bool force) ++{ ++ if (force) { ++ return job_finish_sync_locked(job, &job_force_cancel_err_locked, NULL); ++ } else { ++ return job_finish_sync_locked(job, &job_cancel_err_locked, NULL); ++ } ++} ++ ++int job_cancel_sync(Job *job, bool force) ++{ ++ JOB_LOCK_GUARD(); ++ return job_cancel_sync_locked(job, force); ++} ++ ++void job_cancel_sync_all(void) ++{ ++ Job *job; ++ JOB_LOCK_GUARD(); ++ ++ while ((job = job_next_locked(NULL))) { ++ job_cancel_sync_locked(job, true); ++ } ++} ++ ++int job_complete_sync_locked(Job *job, Error **errp) ++{ ++ return job_finish_sync_locked(job, job_complete_locked, errp); ++} ++ ++void job_complete_locked(Job *job, Error **errp) ++{ ++ /* Should not be reachable via external interface for internal jobs */ ++ assert(job->id); ++ GLOBAL_STATE_CODE(); ++ if (job_apply_verb_locked(job, JOB_VERB_COMPLETE, errp)) { ++ return; ++ } ++ if (job_cancel_requested_locked(job) || !job->driver->complete) { ++ error_setg(errp, "The active block job '%s' cannot be completed", ++ job->id); ++ return; ++ } ++ ++ job_unlock(); ++ job->driver->complete(job, errp); ++ job_lock(); ++} ++ ++int job_finish_sync_locked(Job *job, ++ void (*finish)(Job *, Error **errp), ++ Error **errp) ++{ ++ Error *local_err = NULL; ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ job_ref_locked(job); ++ ++ if (finish) { ++ finish(job, &local_err); ++ } ++ if (local_err) { ++ error_propagate(errp, local_err); ++ job_unref_locked(job); ++ return -EBUSY; ++ } ++ ++ job_unlock(); ++ AIO_WAIT_WHILE_UNLOCKED(job->aio_context, ++ (job_enter(job), !job_is_completed(job))); ++ job_lock(); ++ ++ ret = (job_is_cancelled_locked(job) && job->ret == 0) ++ ? -ECANCELED : job->ret; ++ job_unref_locked(job); ++ return ret; ++} +diff --git a/qcow2/lib/qapi/qapi-clone-visitor.c b/qcow2/lib/qapi/qapi-clone-visitor.c +new file mode 100644 +index 00000000..bbf95369 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-clone-visitor.c +@@ -0,0 +1,182 @@ ++/* ++ * Copy one QAPI object to another ++ * ++ * Copyright (C) 2016 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/clone-visitor.h" ++#include "qapi/visitor-impl.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qnull.h" ++ ++struct QapiCloneVisitor { ++ Visitor visitor; ++ size_t depth; ++}; ++ ++static QapiCloneVisitor *to_qcv(Visitor *v) ++{ ++ return container_of(v, QapiCloneVisitor, visitor); ++} ++ ++static bool qapi_clone_start_struct(Visitor *v, const char *name, void **obj, ++ size_t size, Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ if (!obj) { ++ assert(qcv->depth); ++ /* Only possible when visiting an alternate's object ++ * branch. Nothing further to do here, since the earlier ++ * visit_start_alternate() already copied memory. */ ++ return true; ++ } ++ ++ *obj = g_memdup(*obj, size); ++ qcv->depth++; ++ return true; ++} ++ ++static void qapi_clone_end(Visitor *v, void **obj) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ if (obj) { ++ qcv->depth--; ++ } ++} ++ ++static bool qapi_clone_start_list(Visitor *v, const char *name, ++ GenericList **listp, size_t size, ++ Error **errp) ++{ ++ return qapi_clone_start_struct(v, name, (void **)listp, size, errp); ++} ++ ++static GenericList *qapi_clone_next_list(Visitor *v, GenericList *tail, ++ size_t size) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ /* Unshare the tail of the list cloned by g_memdup() */ ++ tail->next = g_memdup(tail->next, size); ++ return tail->next; ++} ++ ++static bool qapi_clone_start_alternate(Visitor *v, const char *name, ++ GenericAlternate **obj, size_t size, ++ Error **errp) ++{ ++ return qapi_clone_start_struct(v, name, (void **)obj, size, errp); ++} ++ ++static bool qapi_clone_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ /* Value was already cloned by g_memdup() */ ++ return true; ++} ++ ++static bool qapi_clone_type_uint64(Visitor *v, const char *name, ++ uint64_t *obj, Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ /* Value was already cloned by g_memdup() */ ++ return true; ++} ++ ++static bool qapi_clone_type_bool(Visitor *v, const char *name, bool *obj, ++ Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ /* Value was already cloned by g_memdup() */ ++ return true; ++} ++ ++static bool qapi_clone_type_str(Visitor *v, const char *name, char **obj, ++ Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ /* ++ * Pointer was already cloned by g_memdup; create fresh copy. ++ * Note that as long as qobject-output-visitor accepts NULL instead of ++ * "", then we must do likewise. However, we want to obey the ++ * input visitor semantics of never producing NULL when the empty ++ * string is intended. ++ */ ++ *obj = g_strdup(*obj ?: ""); ++ return true; ++} ++ ++static bool qapi_clone_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ /* Value was already cloned by g_memdup() */ ++ return true; ++} ++ ++static bool qapi_clone_type_null(Visitor *v, const char *name, QNull **obj, ++ Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ *obj = qnull(); ++ return true; ++} ++ ++static void qapi_clone_free(Visitor *v) ++{ ++ g_free(v); ++} ++ ++Visitor *qapi_clone_visitor_new(void) ++{ ++ QapiCloneVisitor *v; ++ ++ v = g_malloc0(sizeof(*v)); ++ ++ v->visitor.type = VISITOR_CLONE; ++ v->visitor.start_struct = qapi_clone_start_struct; ++ v->visitor.end_struct = qapi_clone_end; ++ v->visitor.start_list = qapi_clone_start_list; ++ v->visitor.next_list = qapi_clone_next_list; ++ v->visitor.end_list = qapi_clone_end; ++ v->visitor.start_alternate = qapi_clone_start_alternate; ++ v->visitor.end_alternate = qapi_clone_end; ++ v->visitor.type_int64 = qapi_clone_type_int64; ++ v->visitor.type_uint64 = qapi_clone_type_uint64; ++ v->visitor.type_bool = qapi_clone_type_bool; ++ v->visitor.type_str = qapi_clone_type_str; ++ v->visitor.type_number = qapi_clone_type_number; ++ v->visitor.type_null = qapi_clone_type_null; ++ v->visitor.free = qapi_clone_free; ++ ++ return &v->visitor; ++} ++ ++Visitor *qapi_clone_members_visitor_new(void) ++{ ++ Visitor *v = qapi_clone_visitor_new(); ++ to_qcv(v)->depth++; ++ return v; ++} +diff --git a/qcow2/lib/qapi/qapi-dealloc-visitor.c b/qcow2/lib/qapi/qapi-dealloc-visitor.c +new file mode 100644 +index 00000000..ef283f29 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-dealloc-visitor.c +@@ -0,0 +1,143 @@ ++/* ++ * Dealloc Visitor ++ * ++ * Copyright (C) 2012-2016 Red Hat, Inc. ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Michael Roth ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/visitor-impl.h" ++ ++struct QapiDeallocVisitor ++{ ++ Visitor visitor; ++}; ++ ++static bool qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj, ++ size_t unused, Error **errp) ++{ ++ return true; ++} ++ ++static void qapi_dealloc_end_struct(Visitor *v, void **obj) ++{ ++ if (obj) { ++ g_free(*obj); ++ } ++} ++ ++static void qapi_dealloc_end_alternate(Visitor *v, void **obj) ++{ ++ if (obj) { ++ g_free(*obj); ++ } ++} ++ ++static bool qapi_dealloc_start_list(Visitor *v, const char *name, ++ GenericList **list, size_t size, ++ Error **errp) ++{ ++ return true; ++} ++ ++static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *tail, ++ size_t size) ++{ ++ GenericList *next = tail->next; ++ g_free(tail); ++ return next; ++} ++ ++static void qapi_dealloc_end_list(Visitor *v, void **obj) ++{ ++} ++ ++static bool qapi_dealloc_type_str(Visitor *v, const char *name, char **obj, ++ Error **errp) ++{ ++ if (obj) { ++ g_free(*obj); ++ } ++ return true; ++} ++ ++static bool qapi_dealloc_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp) ++{ ++ return true; ++} ++ ++static bool qapi_dealloc_type_uint64(Visitor *v, const char *name, ++ uint64_t *obj, Error **errp) ++{ ++ return true; ++} ++ ++static bool qapi_dealloc_type_bool(Visitor *v, const char *name, bool *obj, ++ Error **errp) ++{ ++ return true; ++} ++ ++static bool qapi_dealloc_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp) ++{ ++ return true; ++} ++ ++static bool qapi_dealloc_type_anything(Visitor *v, const char *name, ++ QObject **obj, Error **errp) ++{ ++ if (obj) { ++ qobject_unref(*obj); ++ } ++ return true; ++} ++ ++static bool qapi_dealloc_type_null(Visitor *v, const char *name, ++ QNull **obj, Error **errp) ++{ ++ if (obj) { ++ qobject_unref(*obj); ++ } ++ return true; ++} ++ ++static void qapi_dealloc_free(Visitor *v) ++{ ++ g_free(container_of(v, QapiDeallocVisitor, visitor)); ++} ++ ++Visitor *qapi_dealloc_visitor_new(void) ++{ ++ QapiDeallocVisitor *v; ++ ++ v = g_malloc0(sizeof(*v)); ++ ++ v->visitor.type = VISITOR_DEALLOC; ++ v->visitor.start_struct = qapi_dealloc_start_struct; ++ v->visitor.end_struct = qapi_dealloc_end_struct; ++ v->visitor.end_alternate = qapi_dealloc_end_alternate; ++ v->visitor.start_list = qapi_dealloc_start_list; ++ v->visitor.next_list = qapi_dealloc_next_list; ++ v->visitor.end_list = qapi_dealloc_end_list; ++ v->visitor.type_int64 = qapi_dealloc_type_int64; ++ v->visitor.type_uint64 = qapi_dealloc_type_uint64; ++ v->visitor.type_bool = qapi_dealloc_type_bool; ++ v->visitor.type_str = qapi_dealloc_type_str; ++ v->visitor.type_number = qapi_dealloc_type_number; ++ v->visitor.type_any = qapi_dealloc_type_anything; ++ v->visitor.type_null = qapi_dealloc_type_null; ++ v->visitor.free = qapi_dealloc_free; ++ ++ return &v->visitor; ++} +diff --git a/qcow2/lib/qapi/qapi-emit-events.h b/qcow2/lib/qapi/qapi-emit-events.h +new file mode 100644 +index 00000000..725da1d4 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-emit-events.h +@@ -0,0 +1,83 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * QAPI Events emission ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * Copyright (c) 2015-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_EMIT_EVENTS_H ++#define QAPI_EMIT_EVENTS_H ++ ++#include "qapi/util.h" ++ ++typedef enum QAPIEvent { ++ QAPI_EVENT_SHUTDOWN, ++ QAPI_EVENT_POWERDOWN, ++ QAPI_EVENT_RESET, ++ QAPI_EVENT_STOP, ++ QAPI_EVENT_RESUME, ++ QAPI_EVENT_SUSPEND, ++ QAPI_EVENT_SUSPEND_DISK, ++ QAPI_EVENT_WAKEUP, ++ QAPI_EVENT_WATCHDOG, ++ QAPI_EVENT_GUEST_PANICKED, ++ QAPI_EVENT_GUEST_CRASHLOADED, ++ QAPI_EVENT_GUEST_PVSHUTDOWN, ++ QAPI_EVENT_MEMORY_FAILURE, ++ QAPI_EVENT_JOB_STATUS_CHANGE, ++ QAPI_EVENT_DEVICE_TRAY_MOVED, ++ QAPI_EVENT_PR_MANAGER_STATUS_CHANGED, ++ QAPI_EVENT_BLOCK_IMAGE_CORRUPTED, ++ QAPI_EVENT_BLOCK_IO_ERROR, ++ QAPI_EVENT_BLOCK_JOB_COMPLETED, ++ QAPI_EVENT_BLOCK_JOB_CANCELLED, ++ QAPI_EVENT_BLOCK_JOB_ERROR, ++ QAPI_EVENT_BLOCK_JOB_READY, ++ QAPI_EVENT_BLOCK_JOB_PENDING, ++ QAPI_EVENT_BLOCK_WRITE_THRESHOLD, ++ QAPI_EVENT_QUORUM_FAILURE, ++ QAPI_EVENT_QUORUM_REPORT_BAD, ++ QAPI_EVENT_BLOCK_EXPORT_DELETED, ++ QAPI_EVENT_VSERPORT_CHANGE, ++ QAPI_EVENT_DUMP_COMPLETED, ++ QAPI_EVENT_NIC_RX_FILTER_CHANGED, ++ QAPI_EVENT_FAILOVER_NEGOTIATED, ++ QAPI_EVENT_NETDEV_STREAM_CONNECTED, ++ QAPI_EVENT_NETDEV_STREAM_DISCONNECTED, ++ QAPI_EVENT_SPICE_CONNECTED, ++ QAPI_EVENT_SPICE_INITIALIZED, ++ QAPI_EVENT_SPICE_DISCONNECTED, ++ QAPI_EVENT_SPICE_MIGRATE_COMPLETED, ++ QAPI_EVENT_VNC_CONNECTED, ++ QAPI_EVENT_VNC_INITIALIZED, ++ QAPI_EVENT_VNC_DISCONNECTED, ++ QAPI_EVENT_MIGRATION, ++ QAPI_EVENT_MIGRATION_PASS, ++ QAPI_EVENT_COLO_EXIT, ++ QAPI_EVENT_UNPLUG_PRIMARY, ++ QAPI_EVENT_DEVICE_DELETED, ++ QAPI_EVENT_DEVICE_UNPLUG_GUEST_ERROR, ++ QAPI_EVENT_BALLOON_CHANGE, ++ QAPI_EVENT_HV_BALLOON_STATUS_REPORT, ++ QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE, ++ QAPI_EVENT_CPU_POLARIZATION_CHANGE, ++ QAPI_EVENT_RTC_CHANGE, ++ QAPI_EVENT_VFU_CLIENT_HANGUP, ++ QAPI_EVENT_ACPI_DEVICE_OST, ++ QAPI_EVENT_VFIO_MIGRATION, ++ QAPI_EVENT__MAX, ++} QAPIEvent; ++ ++#define QAPIEvent_str(val) \ ++ qapi_enum_lookup(&QAPIEvent_lookup, (val)) ++ ++extern const QEnumLookup QAPIEvent_lookup; ++ ++void qapi_event_emit(QAPIEvent event, QDict *qdict); ++ ++#endif /* QAPI_EMIT_EVENTS_H */ +diff --git a/qcow2/lib/qapi/qapi-events-block-core.c b/qcow2/lib/qapi/qapi-events-block-core.c +new file mode 100644 +index 00000000..a08a710c +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-events-block-core.c +@@ -0,0 +1,323 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP events ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * Copyright (c) 2015-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi-emit-events.h" ++#include "qapi-events-block-core.h" ++#include "qapi-visit-block-core.h" ++#include "qapi/compat-policy.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp-event.h" ++ ++void qapi_event_send_block_image_corrupted(const char *device, const char *node_name, const char *msg, bool has_offset, int64_t offset, bool has_size, int64_t size, bool fatal) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_IMAGE_CORRUPTED_arg param = { ++ (char *)device, (char *)node_name, (char *)msg, has_offset, offset, has_size, size, fatal ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_IMAGE_CORRUPTED"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_IMAGE_CORRUPTED", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_IMAGE_CORRUPTED_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_IMAGE_CORRUPTED, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_io_error(const char *device, const char *node_name, IoOperationType operation, BlockErrorAction action, bool has_nospace, bool nospace, const char *reason) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_IO_ERROR_arg param = { ++ (char *)device, (char *)node_name, operation, action, has_nospace, nospace, (char *)reason ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_IO_ERROR"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_IO_ERROR", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_IO_ERROR_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_IO_ERROR, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_job_completed(JobType type, const char *device, int64_t len, int64_t offset, int64_t speed, const char *error) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_JOB_COMPLETED_arg param = { ++ type, (char *)device, len, offset, speed, (char *)error ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_JOB_COMPLETED"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_JOB_COMPLETED", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_JOB_COMPLETED_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_JOB_COMPLETED, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_job_cancelled(JobType type, const char *device, int64_t len, int64_t offset, int64_t speed) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_JOB_CANCELLED_arg param = { ++ type, (char *)device, len, offset, speed ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_JOB_CANCELLED"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_JOB_CANCELLED", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_JOB_CANCELLED_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_JOB_CANCELLED, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_job_error(const char *device, IoOperationType operation, BlockErrorAction action) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_JOB_ERROR_arg param = { ++ (char *)device, operation, action ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_JOB_ERROR"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_JOB_ERROR", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_JOB_ERROR_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_JOB_ERROR, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_job_ready(JobType type, const char *device, int64_t len, int64_t offset, int64_t speed) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_JOB_READY_arg param = { ++ type, (char *)device, len, offset, speed ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_JOB_READY"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_JOB_READY", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_JOB_READY_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_JOB_READY, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_job_pending(JobType type, const char *id) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_JOB_PENDING_arg param = { ++ type, (char *)id ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_JOB_PENDING"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_JOB_PENDING", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_JOB_PENDING_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_JOB_PENDING, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_write_threshold(const char *node_name, uint64_t amount_exceeded, uint64_t write_threshold) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_WRITE_THRESHOLD_arg param = { ++ (char *)node_name, amount_exceeded, write_threshold ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_WRITE_THRESHOLD"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_WRITE_THRESHOLD", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_WRITE_THRESHOLD_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_WRITE_THRESHOLD, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_quorum_failure(const char *reference, int64_t sector_num, int64_t sectors_count) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_QUORUM_FAILURE_arg param = { ++ (char *)reference, sector_num, sectors_count ++ }; ++ ++ qmp = qmp_event_build_dict("QUORUM_FAILURE"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "QUORUM_FAILURE", NULL, 0, &error_abort); ++ visit_type_q_obj_QUORUM_FAILURE_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_QUORUM_FAILURE, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_quorum_report_bad(QuorumOpType type, const char *error, const char *node_name, int64_t sector_num, int64_t sectors_count) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_QUORUM_REPORT_BAD_arg param = { ++ type, (char *)error, (char *)node_name, sector_num, sectors_count ++ }; ++ ++ qmp = qmp_event_build_dict("QUORUM_REPORT_BAD"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "QUORUM_REPORT_BAD", NULL, 0, &error_abort); ++ visit_type_q_obj_QUORUM_REPORT_BAD_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_QUORUM_REPORT_BAD, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_events_block_core_c; +diff --git a/qcow2/lib/qapi/qapi-events-job.c b/qcow2/lib/qapi/qapi-events-job.c +new file mode 100644 +index 00000000..cb5c3b45 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-events-job.c +@@ -0,0 +1,53 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP events ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * Copyright (c) 2015-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi-emit-events.h" ++#include "qapi-events-job.h" ++#include "qapi-visit-job.h" ++#include "qapi/compat-policy.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp-event.h" ++ ++void qapi_event_send_job_status_change(const char *id, JobStatus status) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_JOB_STATUS_CHANGE_arg param = { ++ (char *)id, status ++ }; ++ ++ qmp = qmp_event_build_dict("JOB_STATUS_CHANGE"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "JOB_STATUS_CHANGE", NULL, 0, &error_abort); ++ visit_type_q_obj_JOB_STATUS_CHANGE_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_JOB_STATUS_CHANGE, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_events_job_c; +diff --git a/qcow2/lib/qapi/qapi-types-block-core.c b/qcow2/lib/qapi/qapi-types-block-core.c +new file mode 100644 +index 00000000..f4ee0d21 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-types-block-core.c +@@ -0,0 +1,2484 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi-types-block-core.h" ++#include "qapi-visit-block-core.h" ++ ++void qapi_free_SnapshotInfo(SnapshotInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SnapshotInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificQCow2EncryptionBase(ImageInfoSpecificQCow2EncryptionBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificQCow2EncryptionBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificQCow2Encryption(ImageInfoSpecificQCow2Encryption *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificQCow2Encryption(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_Qcow2BitmapInfoList(Qcow2BitmapInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_Qcow2BitmapInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificQCow2(ImageInfoSpecificQCow2 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificQCow2(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_VmdkExtentInfoList(VmdkExtentInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_VmdkExtentInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificVmdk(ImageInfoSpecificVmdk *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificVmdk(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_VmdkExtentInfo(VmdkExtentInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_VmdkExtentInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificRbd(ImageInfoSpecificRbd *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificRbd(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificFile(ImageInfoSpecificFile *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificFile(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup ImageInfoSpecificKind_lookup = { ++ .array = (const char *const[]) { ++ [IMAGE_INFO_SPECIFIC_KIND_QCOW2] = "qcow2", ++ [IMAGE_INFO_SPECIFIC_KIND_VMDK] = "vmdk", ++ [IMAGE_INFO_SPECIFIC_KIND_LUKS] = "luks", ++ [IMAGE_INFO_SPECIFIC_KIND_RBD] = "rbd", ++ [IMAGE_INFO_SPECIFIC_KIND_FILE] = "file", ++ }, ++ .size = IMAGE_INFO_SPECIFIC_KIND__MAX ++}; ++ ++void qapi_free_ImageInfoSpecificQCow2Wrapper(ImageInfoSpecificQCow2Wrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificQCow2Wrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificVmdkWrapper(ImageInfoSpecificVmdkWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificVmdkWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificLUKSWrapper(ImageInfoSpecificLUKSWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificLUKSWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificRbdWrapper(ImageInfoSpecificRbdWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificRbdWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificFileWrapper(ImageInfoSpecificFileWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificFileWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecific(ImageInfoSpecific *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecific(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_SnapshotInfoList(SnapshotInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SnapshotInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockNodeInfo(BlockNodeInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockNodeInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfo(ImageInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockChildInfo(BlockChildInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockChildInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockChildInfoList(BlockChildInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockChildInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockGraphInfo(BlockGraphInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockGraphInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageCheck(ImageCheck *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageCheck(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_MapEntry(MapEntry *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_MapEntry(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCacheInfo(BlockdevCacheInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCacheInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyInfoList(BlockDirtyInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDeviceInfo(BlockDeviceInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDeviceInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockDeviceIoStatus_lookup = { ++ .array = (const char *const[]) { ++ [BLOCK_DEVICE_IO_STATUS_OK] = "ok", ++ [BLOCK_DEVICE_IO_STATUS_FAILED] = "failed", ++ [BLOCK_DEVICE_IO_STATUS_NOSPACE] = "nospace", ++ }, ++ .size = BLOCK_DEVICE_IO_STATUS__MAX ++}; ++ ++void qapi_free_BlockDirtyInfo(BlockDirtyInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup Qcow2BitmapInfoFlags_lookup = { ++ .array = (const char *const[]) { ++ [QCOW2_BITMAP_INFO_FLAGS_IN_USE] = "in-use", ++ [QCOW2_BITMAP_INFO_FLAGS_AUTO] = "auto", ++ }, ++ .size = QCOW2_BITMAP_INFO_FLAGS__MAX ++}; ++ ++void qapi_free_Qcow2BitmapInfoFlagsList(Qcow2BitmapInfoFlagsList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_Qcow2BitmapInfoFlagsList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_Qcow2BitmapInfo(Qcow2BitmapInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_Qcow2BitmapInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockLatencyHistogramInfo(BlockLatencyHistogramInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockLatencyHistogramInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockInfo(BlockInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockMeasureInfo(BlockMeasureInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockMeasureInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockInfoList(BlockInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDeviceTimedStats(BlockDeviceTimedStats *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDeviceTimedStats(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDeviceTimedStatsList(BlockDeviceTimedStatsList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDeviceTimedStatsList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDeviceStats(BlockDeviceStats *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDeviceStats(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockStatsSpecificFile(BlockStatsSpecificFile *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockStatsSpecificFile(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockStatsSpecificNvme(BlockStatsSpecificNvme *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockStatsSpecificNvme(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockStatsSpecific(BlockStatsSpecific *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockStatsSpecific(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockStats(BlockStats *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockStats(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockStatsList(BlockStatsList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockStatsList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevOnError_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_ON_ERROR_REPORT] = "report", ++ [BLOCKDEV_ON_ERROR_IGNORE] = "ignore", ++ [BLOCKDEV_ON_ERROR_ENOSPC] = "enospc", ++ [BLOCKDEV_ON_ERROR_STOP] = "stop", ++ [BLOCKDEV_ON_ERROR_AUTO] = "auto", ++ }, ++ .size = BLOCKDEV_ON_ERROR__MAX ++}; ++ ++const QEnumLookup MirrorSyncMode_lookup = { ++ .array = (const char *const[]) { ++ [MIRROR_SYNC_MODE_TOP] = "top", ++ [MIRROR_SYNC_MODE_FULL] = "full", ++ [MIRROR_SYNC_MODE_NONE] = "none", ++ [MIRROR_SYNC_MODE_INCREMENTAL] = "incremental", ++ [MIRROR_SYNC_MODE_BITMAP] = "bitmap", ++ }, ++ .size = MIRROR_SYNC_MODE__MAX ++}; ++ ++const QEnumLookup BitmapSyncMode_lookup = { ++ .array = (const char *const[]) { ++ [BITMAP_SYNC_MODE_ON_SUCCESS] = "on-success", ++ [BITMAP_SYNC_MODE_NEVER] = "never", ++ [BITMAP_SYNC_MODE_ALWAYS] = "always", ++ }, ++ .size = BITMAP_SYNC_MODE__MAX ++}; ++ ++const QEnumLookup MirrorCopyMode_lookup = { ++ .array = (const char *const[]) { ++ [MIRROR_COPY_MODE_BACKGROUND] = "background", ++ [MIRROR_COPY_MODE_WRITE_BLOCKING] = "write-blocking", ++ }, ++ .size = MIRROR_COPY_MODE__MAX ++}; ++ ++void qapi_free_BlockJobInfoMirror(BlockJobInfoMirror *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockJobInfoMirror(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockJobInfo(BlockJobInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockJobInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockJobInfoList(BlockJobInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockJobInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup NewImageMode_lookup = { ++ .array = (const char *const[]) { ++ [NEW_IMAGE_MODE_EXISTING] = "existing", ++ [NEW_IMAGE_MODE_ABSOLUTE_PATHS] = "absolute-paths", ++ }, ++ .size = NEW_IMAGE_MODE__MAX ++}; ++ ++void qapi_free_BlockdevSnapshotSync(BlockdevSnapshotSync *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevSnapshotSync(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevSnapshot(BlockdevSnapshot *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevSnapshot(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BackupPerf(BackupPerf *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BackupPerf(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BackupCommon(BackupCommon *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BackupCommon(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_DriveBackup(DriveBackup *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_DriveBackup(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevBackup(BlockdevBackup *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevBackup(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDeviceInfoList(BlockDeviceInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDeviceInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup XDbgBlockGraphNodeType_lookup = { ++ .array = (const char *const[]) { ++ [X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_BACKEND] = "block-backend", ++ [X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB] = "block-job", ++ [X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_DRIVER] = "block-driver", ++ }, ++ .size = X_DBG_BLOCK_GRAPH_NODE_TYPE__MAX ++}; ++ ++void qapi_free_XDbgBlockGraphNode(XDbgBlockGraphNode *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_XDbgBlockGraphNode(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockPermission_lookup = { ++ .array = (const char *const[]) { ++ [BLOCK_PERMISSION_CONSISTENT_READ] = "consistent-read", ++ [BLOCK_PERMISSION_WRITE] = "write", ++ [BLOCK_PERMISSION_WRITE_UNCHANGED] = "write-unchanged", ++ [BLOCK_PERMISSION_RESIZE] = "resize", ++ }, ++ .size = BLOCK_PERMISSION__MAX ++}; ++ ++void qapi_free_BlockPermissionList(BlockPermissionList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockPermissionList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_XDbgBlockGraphEdge(XDbgBlockGraphEdge *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_XDbgBlockGraphEdge(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_XDbgBlockGraphNodeList(XDbgBlockGraphNodeList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_XDbgBlockGraphNodeList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_XDbgBlockGraphEdgeList(XDbgBlockGraphEdgeList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_XDbgBlockGraphEdgeList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_XDbgBlockGraph(XDbgBlockGraph *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_XDbgBlockGraph(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_DriveMirror(DriveMirror *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_DriveMirror(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyBitmap(BlockDirtyBitmap *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyBitmap(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyBitmapAdd(BlockDirtyBitmapAdd *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyBitmapAdd(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyBitmapOrStr(BlockDirtyBitmapOrStr *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyBitmapOrStr(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyBitmapOrStrList(BlockDirtyBitmapOrStrList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyBitmapOrStrList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyBitmapMerge(BlockDirtyBitmapMerge *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyBitmapMerge(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyBitmapSha256(BlockDirtyBitmapSha256 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyBitmapSha256(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockIOThrottle(BlockIOThrottle *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockIOThrottle(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ThrottleLimits(ThrottleLimits *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ThrottleLimits(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ThrottleGroupProperties(ThrottleGroupProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ThrottleGroupProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockJobChangeOptionsMirror(BlockJobChangeOptionsMirror *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockJobChangeOptionsMirror(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockJobChangeOptions(BlockJobChangeOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockJobChangeOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevDiscardOptions_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_DISCARD_OPTIONS_IGNORE] = "ignore", ++ [BLOCKDEV_DISCARD_OPTIONS_UNMAP] = "unmap", ++ }, ++ .size = BLOCKDEV_DISCARD_OPTIONS__MAX ++}; ++ ++const QEnumLookup BlockdevDetectZeroesOptions_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF] = "off", ++ [BLOCKDEV_DETECT_ZEROES_OPTIONS_ON] = "on", ++ [BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP] = "unmap", ++ }, ++ .size = BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX ++}; ++ ++const QEnumLookup BlockdevAioOptions_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_AIO_OPTIONS_THREADS] = "threads", ++ [BLOCKDEV_AIO_OPTIONS_NATIVE] = "native", ++#if defined(CONFIG_LINUX_IO_URING) ++ [BLOCKDEV_AIO_OPTIONS_IO_URING] = "io_uring", ++#endif /* defined(CONFIG_LINUX_IO_URING) */ ++ }, ++ .size = BLOCKDEV_AIO_OPTIONS__MAX ++}; ++ ++void qapi_free_BlockdevCacheOptions(BlockdevCacheOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCacheOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevDriver_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_DRIVER_BLKDEBUG] = "blkdebug", ++ [BLOCKDEV_DRIVER_BLKLOGWRITES] = "blklogwrites", ++ [BLOCKDEV_DRIVER_BLKREPLAY] = "blkreplay", ++ [BLOCKDEV_DRIVER_BLKVERIFY] = "blkverify", ++ [BLOCKDEV_DRIVER_BOCHS] = "bochs", ++ [BLOCKDEV_DRIVER_CLOOP] = "cloop", ++ [BLOCKDEV_DRIVER_COMPRESS] = "compress", ++ [BLOCKDEV_DRIVER_COPY_BEFORE_WRITE] = "copy-before-write", ++ [BLOCKDEV_DRIVER_COPY_ON_READ] = "copy-on-read", ++ [BLOCKDEV_DRIVER_DMG] = "dmg", ++ [BLOCKDEV_DRIVER_FILE] = "file", ++ [BLOCKDEV_DRIVER_SNAPSHOT_ACCESS] = "snapshot-access", ++ [BLOCKDEV_DRIVER_FTP] = "ftp", ++ [BLOCKDEV_DRIVER_FTPS] = "ftps", ++ [BLOCKDEV_DRIVER_GLUSTER] = "gluster", ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ [BLOCKDEV_DRIVER_HOST_CDROM] = "host_cdrom", ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ [BLOCKDEV_DRIVER_HOST_DEVICE] = "host_device", ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ [BLOCKDEV_DRIVER_HTTP] = "http", ++ [BLOCKDEV_DRIVER_HTTPS] = "https", ++#if defined(CONFIG_BLKIO) ++ [BLOCKDEV_DRIVER_IO_URING] = "io_uring", ++#endif /* defined(CONFIG_BLKIO) */ ++ [BLOCKDEV_DRIVER_ISCSI] = "iscsi", ++ [BLOCKDEV_DRIVER_LUKS] = "luks", ++ [BLOCKDEV_DRIVER_NBD] = "nbd", ++ [BLOCKDEV_DRIVER_NFS] = "nfs", ++ [BLOCKDEV_DRIVER_NULL_AIO] = "null-aio", ++ [BLOCKDEV_DRIVER_NULL_CO] = "null-co", ++ [BLOCKDEV_DRIVER_NVME] = "nvme", ++#if defined(CONFIG_BLKIO) ++ [BLOCKDEV_DRIVER_NVME_IO_URING] = "nvme-io_uring", ++#endif /* defined(CONFIG_BLKIO) */ ++ [BLOCKDEV_DRIVER_PARALLELS] = "parallels", ++ [BLOCKDEV_DRIVER_PREALLOCATE] = "preallocate", ++ [BLOCKDEV_DRIVER_QCOW] = "qcow", ++ [BLOCKDEV_DRIVER_QCOW2] = "qcow2", ++ [BLOCKDEV_DRIVER_QED] = "qed", ++ [BLOCKDEV_DRIVER_QUORUM] = "quorum", ++ [BLOCKDEV_DRIVER_RAW] = "raw", ++ [BLOCKDEV_DRIVER_RBD] = "rbd", ++#if defined(CONFIG_REPLICATION) ++ [BLOCKDEV_DRIVER_REPLICATION] = "replication", ++#endif /* defined(CONFIG_REPLICATION) */ ++ [BLOCKDEV_DRIVER_SSH] = "ssh", ++ [BLOCKDEV_DRIVER_THROTTLE] = "throttle", ++ [BLOCKDEV_DRIVER_VDI] = "vdi", ++ [BLOCKDEV_DRIVER_VHDX] = "vhdx", ++#if defined(CONFIG_BLKIO) ++ [BLOCKDEV_DRIVER_VIRTIO_BLK_VFIO_PCI] = "virtio-blk-vfio-pci", ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ [BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_USER] = "virtio-blk-vhost-user", ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ [BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_VDPA] = "virtio-blk-vhost-vdpa", ++#endif /* defined(CONFIG_BLKIO) */ ++ [BLOCKDEV_DRIVER_VMDK] = "vmdk", ++ [BLOCKDEV_DRIVER_VPC] = "vpc", ++ [BLOCKDEV_DRIVER_VVFAT] = "vvfat", ++ }, ++ .size = BLOCKDEV_DRIVER__MAX ++}; ++ ++void qapi_free_BlockdevOptionsFile(BlockdevOptionsFile *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsFile(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsNull(BlockdevOptionsNull *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsNull(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsNVMe(BlockdevOptionsNVMe *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsNVMe(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsVVFAT(BlockdevOptionsVVFAT *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsVVFAT(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsGenericFormat(BlockdevOptionsGenericFormat *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsGenericFormat(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsLUKS(BlockdevOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsGenericCOWFormat(BlockdevOptionsGenericCOWFormat *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsGenericCOWFormat(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup Qcow2OverlapCheckMode_lookup = { ++ .array = (const char *const[]) { ++ [QCOW2_OVERLAP_CHECK_MODE_NONE] = "none", ++ [QCOW2_OVERLAP_CHECK_MODE_CONSTANT] = "constant", ++ [QCOW2_OVERLAP_CHECK_MODE_CACHED] = "cached", ++ [QCOW2_OVERLAP_CHECK_MODE_ALL] = "all", ++ }, ++ .size = QCOW2_OVERLAP_CHECK_MODE__MAX ++}; ++ ++void qapi_free_Qcow2OverlapCheckFlags(Qcow2OverlapCheckFlags *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_Qcow2OverlapCheckFlags(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_Qcow2OverlapChecks(Qcow2OverlapChecks *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_Qcow2OverlapChecks(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevQcowEncryptionFormat_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_QCOW_ENCRYPTION_FORMAT_AES] = "aes", ++ }, ++ .size = BLOCKDEV_QCOW_ENCRYPTION_FORMAT__MAX ++}; ++ ++void qapi_free_BlockdevQcowEncryption(BlockdevQcowEncryption *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevQcowEncryption(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsQcow(BlockdevOptionsQcow *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsQcow(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevQcow2EncryptionFormat_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES] = "aes", ++ [BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS] = "luks", ++ }, ++ .size = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT__MAX ++}; ++ ++void qapi_free_BlockdevQcow2Encryption(BlockdevQcow2Encryption *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevQcow2Encryption(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsPreallocate(BlockdevOptionsPreallocate *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsPreallocate(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsQcow2(BlockdevOptionsQcow2 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsQcow2(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup SshHostKeyCheckMode_lookup = { ++ .array = (const char *const[]) { ++ [SSH_HOST_KEY_CHECK_MODE_NONE] = "none", ++ [SSH_HOST_KEY_CHECK_MODE_HASH] = "hash", ++ [SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS] = "known_hosts", ++ }, ++ .size = SSH_HOST_KEY_CHECK_MODE__MAX ++}; ++ ++const QEnumLookup SshHostKeyCheckHashType_lookup = { ++ .array = (const char *const[]) { ++ [SSH_HOST_KEY_CHECK_HASH_TYPE_MD5] = "md5", ++ [SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1] = "sha1", ++ [SSH_HOST_KEY_CHECK_HASH_TYPE_SHA256] = "sha256", ++ }, ++ .size = SSH_HOST_KEY_CHECK_HASH_TYPE__MAX ++}; ++ ++void qapi_free_SshHostKeyHash(SshHostKeyHash *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SshHostKeyHash(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_SshHostKeyCheck(SshHostKeyCheck *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SshHostKeyCheck(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsSsh(BlockdevOptionsSsh *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsSsh(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlkdebugEvent_lookup = { ++ .array = (const char *const[]) { ++ [BLKDBG_L1_UPDATE] = "l1_update", ++ [BLKDBG_L1_GROW_ALLOC_TABLE] = "l1_grow_alloc_table", ++ [BLKDBG_L1_GROW_WRITE_TABLE] = "l1_grow_write_table", ++ [BLKDBG_L1_GROW_ACTIVATE_TABLE] = "l1_grow_activate_table", ++ [BLKDBG_L2_LOAD] = "l2_load", ++ [BLKDBG_L2_UPDATE] = "l2_update", ++ [BLKDBG_L2_UPDATE_COMPRESSED] = "l2_update_compressed", ++ [BLKDBG_L2_ALLOC_COW_READ] = "l2_alloc_cow_read", ++ [BLKDBG_L2_ALLOC_WRITE] = "l2_alloc_write", ++ [BLKDBG_READ_AIO] = "read_aio", ++ [BLKDBG_READ_BACKING_AIO] = "read_backing_aio", ++ [BLKDBG_READ_COMPRESSED] = "read_compressed", ++ [BLKDBG_WRITE_AIO] = "write_aio", ++ [BLKDBG_WRITE_COMPRESSED] = "write_compressed", ++ [BLKDBG_VMSTATE_LOAD] = "vmstate_load", ++ [BLKDBG_VMSTATE_SAVE] = "vmstate_save", ++ [BLKDBG_COW_READ] = "cow_read", ++ [BLKDBG_COW_WRITE] = "cow_write", ++ [BLKDBG_REFTABLE_LOAD] = "reftable_load", ++ [BLKDBG_REFTABLE_GROW] = "reftable_grow", ++ [BLKDBG_REFTABLE_UPDATE] = "reftable_update", ++ [BLKDBG_REFBLOCK_LOAD] = "refblock_load", ++ [BLKDBG_REFBLOCK_UPDATE] = "refblock_update", ++ [BLKDBG_REFBLOCK_UPDATE_PART] = "refblock_update_part", ++ [BLKDBG_REFBLOCK_ALLOC] = "refblock_alloc", ++ [BLKDBG_REFBLOCK_ALLOC_HOOKUP] = "refblock_alloc_hookup", ++ [BLKDBG_REFBLOCK_ALLOC_WRITE] = "refblock_alloc_write", ++ [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS] = "refblock_alloc_write_blocks", ++ [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE] = "refblock_alloc_write_table", ++ [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE] = "refblock_alloc_switch_table", ++ [BLKDBG_CLUSTER_ALLOC] = "cluster_alloc", ++ [BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes", ++ [BLKDBG_CLUSTER_FREE] = "cluster_free", ++ [BLKDBG_FLUSH_TO_OS] = "flush_to_os", ++ [BLKDBG_FLUSH_TO_DISK] = "flush_to_disk", ++ [BLKDBG_PWRITEV_RMW_HEAD] = "pwritev_rmw_head", ++ [BLKDBG_PWRITEV_RMW_AFTER_HEAD] = "pwritev_rmw_after_head", ++ [BLKDBG_PWRITEV_RMW_TAIL] = "pwritev_rmw_tail", ++ [BLKDBG_PWRITEV_RMW_AFTER_TAIL] = "pwritev_rmw_after_tail", ++ [BLKDBG_PWRITEV] = "pwritev", ++ [BLKDBG_PWRITEV_ZERO] = "pwritev_zero", ++ [BLKDBG_PWRITEV_DONE] = "pwritev_done", ++ [BLKDBG_EMPTY_IMAGE_PREPARE] = "empty_image_prepare", ++ [BLKDBG_L1_SHRINK_WRITE_TABLE] = "l1_shrink_write_table", ++ [BLKDBG_L1_SHRINK_FREE_L2_CLUSTERS] = "l1_shrink_free_l2_clusters", ++ [BLKDBG_COR_WRITE] = "cor_write", ++ [BLKDBG_CLUSTER_ALLOC_SPACE] = "cluster_alloc_space", ++ [BLKDBG_NONE] = "none", ++ }, ++ .size = BLKDBG__MAX ++}; ++ ++const QEnumLookup BlkdebugIOType_lookup = { ++ .array = (const char *const[]) { ++ [BLKDEBUG_IO_TYPE_READ] = "read", ++ [BLKDEBUG_IO_TYPE_WRITE] = "write", ++ [BLKDEBUG_IO_TYPE_WRITE_ZEROES] = "write-zeroes", ++ [BLKDEBUG_IO_TYPE_DISCARD] = "discard", ++ [BLKDEBUG_IO_TYPE_FLUSH] = "flush", ++ [BLKDEBUG_IO_TYPE_BLOCK_STATUS] = "block-status", ++ }, ++ .size = BLKDEBUG_IO_TYPE__MAX ++}; ++ ++void qapi_free_BlkdebugInjectErrorOptions(BlkdebugInjectErrorOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlkdebugInjectErrorOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlkdebugSetStateOptions(BlkdebugSetStateOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlkdebugSetStateOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlkdebugInjectErrorOptionsList(BlkdebugInjectErrorOptionsList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlkdebugInjectErrorOptionsList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlkdebugSetStateOptionsList(BlkdebugSetStateOptionsList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlkdebugSetStateOptionsList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsBlkdebug(BlockdevOptionsBlkdebug *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsBlkdebug(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsBlklogwrites(BlockdevOptionsBlklogwrites *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsBlklogwrites(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsBlkverify(BlockdevOptionsBlkverify *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsBlkverify(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsBlkreplay(BlockdevOptionsBlkreplay *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsBlkreplay(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup QuorumReadPattern_lookup = { ++ .array = (const char *const[]) { ++ [QUORUM_READ_PATTERN_QUORUM] = "quorum", ++ [QUORUM_READ_PATTERN_FIFO] = "fifo", ++ }, ++ .size = QUORUM_READ_PATTERN__MAX ++}; ++ ++void qapi_free_BlockdevRefList(BlockdevRefList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevRefList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsQuorum(BlockdevOptionsQuorum *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsQuorum(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsGluster(BlockdevOptionsGluster *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsGluster(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsIoUring(BlockdevOptionsIoUring *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsIoUring(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsNvmeIoUring(BlockdevOptionsNvmeIoUring *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsNvmeIoUring(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsVirtioBlkVfioPci(BlockdevOptionsVirtioBlkVfioPci *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsVirtioBlkVfioPci(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsVirtioBlkVhostUser(BlockdevOptionsVirtioBlkVhostUser *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsVirtioBlkVhostUser(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsVirtioBlkVhostVdpa(BlockdevOptionsVirtioBlkVhostVdpa *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsVirtioBlkVhostVdpa(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++const QEnumLookup IscsiTransport_lookup = { ++ .array = (const char *const[]) { ++ [ISCSI_TRANSPORT_TCP] = "tcp", ++ [ISCSI_TRANSPORT_ISER] = "iser", ++ }, ++ .size = ISCSI_TRANSPORT__MAX ++}; ++ ++const QEnumLookup IscsiHeaderDigest_lookup = { ++ .array = (const char *const[]) { ++ [QAPI_ISCSI_HEADER_DIGEST_CRC32C] = "crc32c", ++ [QAPI_ISCSI_HEADER_DIGEST_NONE] = "none", ++ [QAPI_ISCSI_HEADER_DIGEST_CRC32C_NONE] = "crc32c-none", ++ [QAPI_ISCSI_HEADER_DIGEST_NONE_CRC32C] = "none-crc32c", ++ }, ++ .size = QAPI_ISCSI_HEADER_DIGEST__MAX ++}; ++ ++void qapi_free_BlockdevOptionsIscsi(BlockdevOptionsIscsi *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsIscsi(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup RbdAuthMode_lookup = { ++ .array = (const char *const[]) { ++ [RBD_AUTH_MODE_CEPHX] = "cephx", ++ [RBD_AUTH_MODE_NONE] = "none", ++ }, ++ .size = RBD_AUTH_MODE__MAX ++}; ++ ++const QEnumLookup RbdImageEncryptionFormat_lookup = { ++ .array = (const char *const[]) { ++ [RBD_IMAGE_ENCRYPTION_FORMAT_LUKS] = "luks", ++ [RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2] = "luks2", ++ [RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY] = "luks-any", ++ }, ++ .size = RBD_IMAGE_ENCRYPTION_FORMAT__MAX ++}; ++ ++void qapi_free_RbdEncryptionOptionsLUKSBase(RbdEncryptionOptionsLUKSBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionOptionsLUKSBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionCreateOptionsLUKSBase(RbdEncryptionCreateOptionsLUKSBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionCreateOptionsLUKSBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionOptionsLUKS(RbdEncryptionOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionOptionsLUKS2(RbdEncryptionOptionsLUKS2 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionOptionsLUKS2(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionOptionsLUKSAny(RbdEncryptionOptionsLUKSAny *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionOptionsLUKSAny(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionCreateOptionsLUKS(RbdEncryptionCreateOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionCreateOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionCreateOptionsLUKS2(RbdEncryptionCreateOptionsLUKS2 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionCreateOptionsLUKS2(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionOptions(RbdEncryptionOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionCreateOptions(RbdEncryptionCreateOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionCreateOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdAuthModeList(RbdAuthModeList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdAuthModeList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsRbd(BlockdevOptionsRbd *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsRbd(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++#if defined(CONFIG_REPLICATION) ++const QEnumLookup ReplicationMode_lookup = { ++ .array = (const char *const[]) { ++ [REPLICATION_MODE_PRIMARY] = "primary", ++ [REPLICATION_MODE_SECONDARY] = "secondary", ++ }, ++ .size = REPLICATION_MODE__MAX ++}; ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++#if defined(CONFIG_REPLICATION) ++void qapi_free_BlockdevOptionsReplication(BlockdevOptionsReplication *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsReplication(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++const QEnumLookup NFSTransport_lookup = { ++ .array = (const char *const[]) { ++ [NFS_TRANSPORT_INET] = "inet", ++ }, ++ .size = NFS_TRANSPORT__MAX ++}; ++ ++void qapi_free_NFSServer(NFSServer *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_NFSServer(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsNfs(BlockdevOptionsNfs *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsNfs(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsCurlBase(BlockdevOptionsCurlBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCurlBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsCurlHttp(BlockdevOptionsCurlHttp *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCurlHttp(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsCurlHttps(BlockdevOptionsCurlHttps *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCurlHttps(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsCurlFtp(BlockdevOptionsCurlFtp *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCurlFtp(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsCurlFtps(BlockdevOptionsCurlFtps *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCurlFtps(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsNbd(BlockdevOptionsNbd *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsNbd(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsRaw(BlockdevOptionsRaw *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsRaw(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsThrottle(BlockdevOptionsThrottle *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsThrottle(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsCor(BlockdevOptionsCor *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCor(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup OnCbwError_lookup = { ++ .array = (const char *const[]) { ++ [ON_CBW_ERROR_BREAK_GUEST_WRITE] = "break-guest-write", ++ [ON_CBW_ERROR_BREAK_SNAPSHOT] = "break-snapshot", ++ }, ++ .size = ON_CBW_ERROR__MAX ++}; ++ ++void qapi_free_BlockdevOptionsCbw(BlockdevOptionsCbw *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCbw(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptions(BlockdevOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevRef(BlockdevRef *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevRef(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevRefOrNull(BlockdevRefOrNull *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevRefOrNull(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsList(BlockdevOptionsList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsFile(BlockdevCreateOptionsFile *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsFile(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsGluster(BlockdevCreateOptionsGluster *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsGluster(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsLUKS(BlockdevCreateOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsNfs(BlockdevCreateOptionsNfs *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsNfs(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsParallels(BlockdevCreateOptionsParallels *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsParallels(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsQcow(BlockdevCreateOptionsQcow *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsQcow(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevQcow2Version_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_QCOW2_VERSION_V2] = "v2", ++ [BLOCKDEV_QCOW2_VERSION_V3] = "v3", ++ }, ++ .size = BLOCKDEV_QCOW2_VERSION__MAX ++}; ++ ++const QEnumLookup Qcow2CompressionType_lookup = { ++ .array = (const char *const[]) { ++ [QCOW2_COMPRESSION_TYPE_ZLIB] = "zlib", ++#if defined(CONFIG_ZSTD) ++ [QCOW2_COMPRESSION_TYPE_ZSTD] = "zstd", ++#endif /* defined(CONFIG_ZSTD) */ ++ }, ++ .size = QCOW2_COMPRESSION_TYPE__MAX ++}; ++ ++void qapi_free_BlockdevCreateOptionsQcow2(BlockdevCreateOptionsQcow2 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsQcow2(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsQed(BlockdevCreateOptionsQed *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsQed(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsRbd(BlockdevCreateOptionsRbd *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsRbd(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevVmdkSubformat_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICSPARSE] = "monolithicSparse", ++ [BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICFLAT] = "monolithicFlat", ++ [BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTSPARSE] = "twoGbMaxExtentSparse", ++ [BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT] = "twoGbMaxExtentFlat", ++ [BLOCKDEV_VMDK_SUBFORMAT_STREAMOPTIMIZED] = "streamOptimized", ++ }, ++ .size = BLOCKDEV_VMDK_SUBFORMAT__MAX ++}; ++ ++const QEnumLookup BlockdevVmdkAdapterType_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_VMDK_ADAPTER_TYPE_IDE] = "ide", ++ [BLOCKDEV_VMDK_ADAPTER_TYPE_BUSLOGIC] = "buslogic", ++ [BLOCKDEV_VMDK_ADAPTER_TYPE_LSILOGIC] = "lsilogic", ++ [BLOCKDEV_VMDK_ADAPTER_TYPE_LEGACYESX] = "legacyESX", ++ }, ++ .size = BLOCKDEV_VMDK_ADAPTER_TYPE__MAX ++}; ++ ++void qapi_free_BlockdevCreateOptionsVmdk(BlockdevCreateOptionsVmdk *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsVmdk(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsSsh(BlockdevCreateOptionsSsh *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsSsh(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsVdi(BlockdevCreateOptionsVdi *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsVdi(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevVhdxSubformat_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC] = "dynamic", ++ [BLOCKDEV_VHDX_SUBFORMAT_FIXED] = "fixed", ++ }, ++ .size = BLOCKDEV_VHDX_SUBFORMAT__MAX ++}; ++ ++void qapi_free_BlockdevCreateOptionsVhdx(BlockdevCreateOptionsVhdx *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsVhdx(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevVpcSubformat_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_VPC_SUBFORMAT_DYNAMIC] = "dynamic", ++ [BLOCKDEV_VPC_SUBFORMAT_FIXED] = "fixed", ++ }, ++ .size = BLOCKDEV_VPC_SUBFORMAT__MAX ++}; ++ ++void qapi_free_BlockdevCreateOptionsVpc(BlockdevCreateOptionsVpc *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsVpc(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptions(BlockdevCreateOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevAmendOptionsLUKS(BlockdevAmendOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevAmendOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevAmendOptionsQcow2(BlockdevAmendOptionsQcow2 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevAmendOptionsQcow2(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevAmendOptions(BlockdevAmendOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevAmendOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockErrorAction_lookup = { ++ .array = (const char *const[]) { ++ [BLOCK_ERROR_ACTION_IGNORE] = "ignore", ++ [BLOCK_ERROR_ACTION_REPORT] = "report", ++ [BLOCK_ERROR_ACTION_STOP] = "stop", ++ }, ++ .size = BLOCK_ERROR_ACTION__MAX ++}; ++ ++const QEnumLookup PreallocMode_lookup = { ++ .array = (const char *const[]) { ++ [PREALLOC_MODE_OFF] = "off", ++ [PREALLOC_MODE_METADATA] = "metadata", ++ [PREALLOC_MODE_FALLOC] = "falloc", ++ [PREALLOC_MODE_FULL] = "full", ++ }, ++ .size = PREALLOC_MODE__MAX ++}; ++ ++const QEnumLookup QuorumOpType_lookup = { ++ .array = (const char *const[]) { ++ [QUORUM_OP_TYPE_READ] = "read", ++ [QUORUM_OP_TYPE_WRITE] = "write", ++ [QUORUM_OP_TYPE_FLUSH] = "flush", ++ }, ++ .size = QUORUM_OP_TYPE__MAX ++}; ++ ++void qapi_free_BlockdevSnapshotInternal(BlockdevSnapshotInternal *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevSnapshotInternal(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockGraphInfoList(BlockGraphInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockGraphInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_DummyBlockCoreForceArrays(DummyBlockCoreForceArrays *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_DummyBlockCoreForceArrays(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_types_block_core_c; +diff --git a/qcow2/lib/qapi/qapi-types-common.c b/qcow2/lib/qapi/qapi-types-common.c +new file mode 100644 +index 00000000..16045e73 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-types-common.c +@@ -0,0 +1,141 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi-types-common.h" ++#include "qapi-visit-common.h" ++ ++const QEnumLookup IoOperationType_lookup = { ++ .array = (const char *const[]) { ++ [IO_OPERATION_TYPE_READ] = "read", ++ [IO_OPERATION_TYPE_WRITE] = "write", ++ }, ++ .size = IO_OPERATION_TYPE__MAX ++}; ++ ++const QEnumLookup OnOffAuto_lookup = { ++ .array = (const char *const[]) { ++ [ON_OFF_AUTO_AUTO] = "auto", ++ [ON_OFF_AUTO_ON] = "on", ++ [ON_OFF_AUTO_OFF] = "off", ++ }, ++ .size = ON_OFF_AUTO__MAX ++}; ++ ++const QEnumLookup OnOffSplit_lookup = { ++ .array = (const char *const[]) { ++ [ON_OFF_SPLIT_ON] = "on", ++ [ON_OFF_SPLIT_OFF] = "off", ++ [ON_OFF_SPLIT_SPLIT] = "split", ++ }, ++ .size = ON_OFF_SPLIT__MAX ++}; ++ ++void qapi_free_StrOrNull(StrOrNull *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_StrOrNull(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup OffAutoPCIBAR_lookup = { ++ .array = (const char *const[]) { ++ [OFF_AUTOPCIBAR_OFF] = "off", ++ [OFF_AUTOPCIBAR_AUTO] = "auto", ++ [OFF_AUTOPCIBAR_BAR0] = "bar0", ++ [OFF_AUTOPCIBAR_BAR1] = "bar1", ++ [OFF_AUTOPCIBAR_BAR2] = "bar2", ++ [OFF_AUTOPCIBAR_BAR3] = "bar3", ++ [OFF_AUTOPCIBAR_BAR4] = "bar4", ++ [OFF_AUTOPCIBAR_BAR5] = "bar5", ++ }, ++ .size = OFF_AUTOPCIBAR__MAX ++}; ++ ++const QEnumLookup PCIELinkSpeed_lookup = { ++ .array = (const char *const[]) { ++ [PCIE_LINK_SPEED_2_5] = "2_5", ++ [PCIE_LINK_SPEED_5] = "5", ++ [PCIE_LINK_SPEED_8] = "8", ++ [PCIE_LINK_SPEED_16] = "16", ++ [PCIE_LINK_SPEED_32] = "32", ++ [PCIE_LINK_SPEED_64] = "64", ++ }, ++ .size = PCIE_LINK_SPEED__MAX ++}; ++ ++const QEnumLookup PCIELinkWidth_lookup = { ++ .array = (const char *const[]) { ++ [PCIE_LINK_WIDTH_1] = "1", ++ [PCIE_LINK_WIDTH_2] = "2", ++ [PCIE_LINK_WIDTH_4] = "4", ++ [PCIE_LINK_WIDTH_8] = "8", ++ [PCIE_LINK_WIDTH_12] = "12", ++ [PCIE_LINK_WIDTH_16] = "16", ++ [PCIE_LINK_WIDTH_32] = "32", ++ }, ++ .size = PCIE_LINK_WIDTH__MAX ++}; ++ ++const QEnumLookup HostMemPolicy_lookup = { ++ .array = (const char *const[]) { ++ [HOST_MEM_POLICY_DEFAULT] = "default", ++ [HOST_MEM_POLICY_PREFERRED] = "preferred", ++ [HOST_MEM_POLICY_BIND] = "bind", ++ [HOST_MEM_POLICY_INTERLEAVE] = "interleave", ++ }, ++ .size = HOST_MEM_POLICY__MAX ++}; ++ ++const QEnumLookup NetFilterDirection_lookup = { ++ .array = (const char *const[]) { ++ [NET_FILTER_DIRECTION_ALL] = "all", ++ [NET_FILTER_DIRECTION_RX] = "rx", ++ [NET_FILTER_DIRECTION_TX] = "tx", ++ }, ++ .size = NET_FILTER_DIRECTION__MAX ++}; ++ ++const QEnumLookup GrabToggleKeys_lookup = { ++ .array = (const char *const[]) { ++ [GRAB_TOGGLE_KEYS_CTRL_CTRL] = "ctrl-ctrl", ++ [GRAB_TOGGLE_KEYS_ALT_ALT] = "alt-alt", ++ [GRAB_TOGGLE_KEYS_SHIFT_SHIFT] = "shift-shift", ++ [GRAB_TOGGLE_KEYS_META_META] = "meta-meta", ++ [GRAB_TOGGLE_KEYS_SCROLLLOCK] = "scrolllock", ++ [GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK] = "ctrl-scrolllock", ++ }, ++ .size = GRAB_TOGGLE_KEYS__MAX ++}; ++ ++void qapi_free_HumanReadableText(HumanReadableText *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_HumanReadableText(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_types_common_c; +diff --git a/qcow2/lib/qapi/qapi-types-crypto.c b/qcow2/lib/qapi/qapi-types-crypto.c +new file mode 100644 +index 00000000..cb8f701a +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-types-crypto.c +@@ -0,0 +1,413 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi-types-crypto.h" ++#include "qapi-visit-crypto.h" ++ ++const QEnumLookup QCryptoTLSCredsEndpoint_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT] = "client", ++ [QCRYPTO_TLS_CREDS_ENDPOINT_SERVER] = "server", ++ }, ++ .size = QCRYPTO_TLS_CREDS_ENDPOINT__MAX ++}; ++ ++const QEnumLookup QCryptoSecretFormat_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_SECRET_FORMAT_RAW] = "raw", ++ [QCRYPTO_SECRET_FORMAT_BASE64] = "base64", ++ }, ++ .size = QCRYPTO_SECRET_FORMAT__MAX ++}; ++ ++const QEnumLookup QCryptoHashAlgorithm_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_HASH_ALG_MD5] = "md5", ++ [QCRYPTO_HASH_ALG_SHA1] = "sha1", ++ [QCRYPTO_HASH_ALG_SHA224] = "sha224", ++ [QCRYPTO_HASH_ALG_SHA256] = "sha256", ++ [QCRYPTO_HASH_ALG_SHA384] = "sha384", ++ [QCRYPTO_HASH_ALG_SHA512] = "sha512", ++ [QCRYPTO_HASH_ALG_RIPEMD160] = "ripemd160", ++ }, ++ .size = QCRYPTO_HASH_ALG__MAX ++}; ++ ++const QEnumLookup QCryptoCipherAlgorithm_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_CIPHER_ALG_AES_128] = "aes-128", ++ [QCRYPTO_CIPHER_ALG_AES_192] = "aes-192", ++ [QCRYPTO_CIPHER_ALG_AES_256] = "aes-256", ++ [QCRYPTO_CIPHER_ALG_DES] = "des", ++ [QCRYPTO_CIPHER_ALG_3DES] = "3des", ++ [QCRYPTO_CIPHER_ALG_CAST5_128] = "cast5-128", ++ [QCRYPTO_CIPHER_ALG_SERPENT_128] = "serpent-128", ++ [QCRYPTO_CIPHER_ALG_SERPENT_192] = "serpent-192", ++ [QCRYPTO_CIPHER_ALG_SERPENT_256] = "serpent-256", ++ [QCRYPTO_CIPHER_ALG_TWOFISH_128] = "twofish-128", ++ [QCRYPTO_CIPHER_ALG_TWOFISH_192] = "twofish-192", ++ [QCRYPTO_CIPHER_ALG_TWOFISH_256] = "twofish-256", ++ [QCRYPTO_CIPHER_ALG_SM4] = "sm4", ++ }, ++ .size = QCRYPTO_CIPHER_ALG__MAX ++}; ++ ++const QEnumLookup QCryptoCipherMode_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_CIPHER_MODE_ECB] = "ecb", ++ [QCRYPTO_CIPHER_MODE_CBC] = "cbc", ++ [QCRYPTO_CIPHER_MODE_XTS] = "xts", ++ [QCRYPTO_CIPHER_MODE_CTR] = "ctr", ++ }, ++ .size = QCRYPTO_CIPHER_MODE__MAX ++}; ++ ++const QEnumLookup QCryptoIVGenAlgorithm_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_IVGEN_ALG_PLAIN] = "plain", ++ [QCRYPTO_IVGEN_ALG_PLAIN64] = "plain64", ++ [QCRYPTO_IVGEN_ALG_ESSIV] = "essiv", ++ }, ++ .size = QCRYPTO_IVGEN_ALG__MAX ++}; ++ ++const QEnumLookup QCryptoBlockFormat_lookup = { ++ .array = (const char *const[]) { ++ [Q_CRYPTO_BLOCK_FORMAT_QCOW] = "qcow", ++ [Q_CRYPTO_BLOCK_FORMAT_LUKS] = "luks", ++ }, ++ .size = Q_CRYPTO_BLOCK_FORMAT__MAX ++}; ++ ++void qapi_free_QCryptoBlockOptionsBase(QCryptoBlockOptionsBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockOptionsBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockOptionsQCow(QCryptoBlockOptionsQCow *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockOptionsQCow(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockOptionsLUKS(QCryptoBlockOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockCreateOptionsLUKS(QCryptoBlockCreateOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockCreateOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockOpenOptions(QCryptoBlockOpenOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockOpenOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockCreateOptions(QCryptoBlockCreateOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockCreateOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockInfoBase(QCryptoBlockInfoBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockInfoBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockInfoLUKSSlot(QCryptoBlockInfoLUKSSlot *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockInfoLUKSSlot(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockInfoLUKSSlotList(QCryptoBlockInfoLUKSSlotList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockInfoLUKSSlotList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockInfoLUKS(QCryptoBlockInfoLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockInfoLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockInfo(QCryptoBlockInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup QCryptoBlockLUKSKeyslotState_lookup = { ++ .array = (const char *const[]) { ++ [Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_ACTIVE] = "active", ++ [Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_INACTIVE] = "inactive", ++ }, ++ .size = Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE__MAX ++}; ++ ++void qapi_free_QCryptoBlockAmendOptionsLUKS(QCryptoBlockAmendOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockAmendOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockAmendOptions(QCryptoBlockAmendOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockAmendOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_SecretCommonProperties(SecretCommonProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SecretCommonProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_SecretProperties(SecretProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SecretProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++#if defined(CONFIG_SECRET_KEYRING) ++void qapi_free_SecretKeyringProperties(SecretKeyringProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SecretKeyringProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_SECRET_KEYRING) */ ++ ++void qapi_free_TlsCredsProperties(TlsCredsProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_TlsCredsProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_TlsCredsAnonProperties(TlsCredsAnonProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_TlsCredsAnonProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_TlsCredsPskProperties(TlsCredsPskProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_TlsCredsPskProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_TlsCredsX509Properties(TlsCredsX509Properties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_TlsCredsX509Properties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup QCryptoAkCipherAlgorithm_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_AKCIPHER_ALG_RSA] = "rsa", ++ }, ++ .size = QCRYPTO_AKCIPHER_ALG__MAX ++}; ++ ++const QEnumLookup QCryptoAkCipherKeyType_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC] = "public", ++ [QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE] = "private", ++ }, ++ .size = QCRYPTO_AKCIPHER_KEY_TYPE__MAX ++}; ++ ++const QEnumLookup QCryptoRSAPaddingAlgorithm_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_RSA_PADDING_ALG_RAW] = "raw", ++ [QCRYPTO_RSA_PADDING_ALG_PKCS1] = "pkcs1", ++ }, ++ .size = QCRYPTO_RSA_PADDING_ALG__MAX ++}; ++ ++void qapi_free_QCryptoAkCipherOptionsRSA(QCryptoAkCipherOptionsRSA *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoAkCipherOptionsRSA(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoAkCipherOptions(QCryptoAkCipherOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoAkCipherOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_types_crypto_c; +diff --git a/qcow2/lib/qapi/qapi-types-job.c b/qcow2/lib/qapi/qapi-types-job.c +new file mode 100644 +index 00000000..eba27079 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-types-job.c +@@ -0,0 +1,91 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi-types-job.h" ++#include "qapi-visit-job.h" ++ ++const QEnumLookup JobType_lookup = { ++ .array = (const char *const[]) { ++ [JOB_TYPE_COMMIT] = "commit", ++ [JOB_TYPE_STREAM] = "stream", ++ [JOB_TYPE_MIRROR] = "mirror", ++ [JOB_TYPE_BACKUP] = "backup", ++ [JOB_TYPE_CREATE] = "create", ++ [JOB_TYPE_AMEND] = "amend", ++ [JOB_TYPE_SNAPSHOT_LOAD] = "snapshot-load", ++ [JOB_TYPE_SNAPSHOT_SAVE] = "snapshot-save", ++ [JOB_TYPE_SNAPSHOT_DELETE] = "snapshot-delete", ++ }, ++ .size = JOB_TYPE__MAX ++}; ++ ++const QEnumLookup JobStatus_lookup = { ++ .array = (const char *const[]) { ++ [JOB_STATUS_UNDEFINED] = "undefined", ++ [JOB_STATUS_CREATED] = "created", ++ [JOB_STATUS_RUNNING] = "running", ++ [JOB_STATUS_PAUSED] = "paused", ++ [JOB_STATUS_READY] = "ready", ++ [JOB_STATUS_STANDBY] = "standby", ++ [JOB_STATUS_WAITING] = "waiting", ++ [JOB_STATUS_PENDING] = "pending", ++ [JOB_STATUS_ABORTING] = "aborting", ++ [JOB_STATUS_CONCLUDED] = "concluded", ++ [JOB_STATUS_NULL] = "null", ++ }, ++ .size = JOB_STATUS__MAX ++}; ++ ++const QEnumLookup JobVerb_lookup = { ++ .array = (const char *const[]) { ++ [JOB_VERB_CANCEL] = "cancel", ++ [JOB_VERB_PAUSE] = "pause", ++ [JOB_VERB_RESUME] = "resume", ++ [JOB_VERB_SET_SPEED] = "set-speed", ++ [JOB_VERB_COMPLETE] = "complete", ++ [JOB_VERB_DISMISS] = "dismiss", ++ [JOB_VERB_FINALIZE] = "finalize", ++ [JOB_VERB_CHANGE] = "change", ++ }, ++ .size = JOB_VERB__MAX ++}; ++ ++void qapi_free_JobInfo(JobInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_JobInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_JobInfoList(JobInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_JobInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_types_job_c; +diff --git a/qcow2/lib/qapi/qapi-types-sockets.c b/qcow2/lib/qapi/qapi-types-sockets.c +new file mode 100644 +index 00000000..568f49e6 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-types-sockets.c +@@ -0,0 +1,209 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi-types-sockets.h" ++#include "qapi-visit-sockets.h" ++ ++const QEnumLookup NetworkAddressFamily_lookup = { ++ .array = (const char *const[]) { ++ [NETWORK_ADDRESS_FAMILY_IPV4] = "ipv4", ++ [NETWORK_ADDRESS_FAMILY_IPV6] = "ipv6", ++ [NETWORK_ADDRESS_FAMILY_UNIX] = "unix", ++ [NETWORK_ADDRESS_FAMILY_VSOCK] = "vsock", ++ [NETWORK_ADDRESS_FAMILY_UNKNOWN] = "unknown", ++ }, ++ .size = NETWORK_ADDRESS_FAMILY__MAX ++}; ++ ++void qapi_free_InetSocketAddressBase(InetSocketAddressBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_InetSocketAddressBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_InetSocketAddress(InetSocketAddress *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_InetSocketAddress(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_UnixSocketAddress(UnixSocketAddress *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_UnixSocketAddress(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_VsockSocketAddress(VsockSocketAddress *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_VsockSocketAddress(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_FdSocketAddress(FdSocketAddress *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_FdSocketAddress(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_InetSocketAddressWrapper(InetSocketAddressWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_InetSocketAddressWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_UnixSocketAddressWrapper(UnixSocketAddressWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_UnixSocketAddressWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_VsockSocketAddressWrapper(VsockSocketAddressWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_VsockSocketAddressWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_FdSocketAddressWrapper(FdSocketAddressWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_FdSocketAddressWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_SocketAddressLegacy(SocketAddressLegacy *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SocketAddressLegacy(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup SocketAddressType_lookup = { ++ .array = (const char *const[]) { ++ [SOCKET_ADDRESS_TYPE_INET] = "inet", ++ [SOCKET_ADDRESS_TYPE_UNIX] = "unix", ++ [SOCKET_ADDRESS_TYPE_VSOCK] = "vsock", ++ [SOCKET_ADDRESS_TYPE_FD] = "fd", ++ }, ++ .size = SOCKET_ADDRESS_TYPE__MAX ++}; ++ ++void qapi_free_SocketAddress(SocketAddress *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SocketAddress(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_SocketAddressList(SocketAddressList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SocketAddressList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_InetSocketAddressBaseList(InetSocketAddressBaseList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_InetSocketAddressBaseList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_types_sockets_c; +diff --git a/qcow2/lib/qapi/qapi-types-yank.c b/qcow2/lib/qapi/qapi-types-yank.c +new file mode 100644 +index 00000000..e75e13a2 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-types-yank.c +@@ -0,0 +1,80 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi-types-yank.h" ++#include "qapi-visit-yank.h" ++ ++const QEnumLookup YankInstanceType_lookup = { ++ .array = (const char *const[]) { ++ [YANK_INSTANCE_TYPE_BLOCK_NODE] = "block-node", ++ [YANK_INSTANCE_TYPE_CHARDEV] = "chardev", ++ [YANK_INSTANCE_TYPE_MIGRATION] = "migration", ++ }, ++ .size = YANK_INSTANCE_TYPE__MAX ++}; ++ ++void qapi_free_YankInstanceBlockNode(YankInstanceBlockNode *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_YankInstanceBlockNode(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_YankInstanceChardev(YankInstanceChardev *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_YankInstanceChardev(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_YankInstance(YankInstance *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_YankInstance(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_YankInstanceList(YankInstanceList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_YankInstanceList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_types_yank_c; +diff --git a/qcow2/lib/qapi/qapi-util.c b/qcow2/lib/qapi/qapi-util.c +new file mode 100644 +index 00000000..65a7d184 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-util.c +@@ -0,0 +1,154 @@ ++/* ++ * QAPI util functions ++ * ++ * Authors: ++ * Hu Tao ++ * Peter Lieven ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/compat-policy.h" ++#include "qapi/error.h" ++#include "qemu/ctype.h" ++#include "qapi/qmp/qerror.h" ++ ++CompatPolicy compat_policy; ++ ++static bool compat_policy_input_ok1(const char *adjective, ++ CompatPolicyInput policy, ++ ErrorClass error_class, ++ const char *kind, const char *name, ++ Error **errp) ++{ ++ switch (policy) { ++ case COMPAT_POLICY_INPUT_ACCEPT: ++ return true; ++ case COMPAT_POLICY_INPUT_REJECT: ++ error_set(errp, error_class, "%s %s %s disabled by policy", ++ adjective, kind, name); ++ return false; ++ case COMPAT_POLICY_INPUT_CRASH: ++ default: ++ abort(); ++ } ++} ++ ++bool compat_policy_input_ok(unsigned special_features, ++ const CompatPolicy *policy, ++ ErrorClass error_class, ++ const char *kind, const char *name, ++ Error **errp) ++{ ++ if ((special_features & 1u << QAPI_DEPRECATED) ++ && !compat_policy_input_ok1("Deprecated", ++ policy->deprecated_input, ++ error_class, kind, name, errp)) { ++ return false; ++ } ++ if ((special_features & (1u << QAPI_UNSTABLE)) ++ && !compat_policy_input_ok1("Unstable", ++ policy->unstable_input, ++ error_class, kind, name, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++const char *qapi_enum_lookup(const QEnumLookup *lookup, int val) ++{ ++ assert(val >= 0 && val < lookup->size); ++ ++ return lookup->array[val]; ++} ++ ++int qapi_enum_parse(const QEnumLookup *lookup, const char *buf, ++ int def, Error **errp) ++{ ++ int i; ++ ++ if (!buf) { ++ return def; ++ } ++ ++ for (i = 0; i < lookup->size; i++) { ++ if (!strcmp(buf, lookup->array[i])) { ++ return i; ++ } ++ } ++ ++ error_setg(errp, "invalid parameter value: %s", buf); ++ return def; ++} ++ ++bool qapi_bool_parse(const char *name, const char *value, bool *obj, Error **errp) ++{ ++ if (g_str_equal(value, "on") || ++ g_str_equal(value, "yes") || ++ g_str_equal(value, "true") || ++ g_str_equal(value, "y")) { ++ *obj = true; ++ return true; ++ } ++ if (g_str_equal(value, "off") || ++ g_str_equal(value, "no") || ++ g_str_equal(value, "false") || ++ g_str_equal(value, "n")) { ++ *obj = false; ++ return true; ++ } ++ ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, ++ "'on' or 'off'"); ++ return false; ++} ++ ++/* ++ * Parse a valid QAPI name from @str. ++ * A valid name consists of letters, digits, hyphen and underscore. ++ * It may be prefixed by __RFQDN_ (downstream extension), where RFQDN ++ * may contain only letters, digits, hyphen and period. ++ * The special exception for enumeration names is not implemented. ++ * See docs/devel/qapi-code-gen.rst for more on QAPI naming rules. ++ * Keep this consistent with scripts/qapi-gen.py! ++ * If @complete, the parse fails unless it consumes @str completely. ++ * Return its length on success, -1 on failure. ++ */ ++int parse_qapi_name(const char *str, bool complete) ++{ ++ const char *p = str; ++ ++ if (*p == '_') { /* Downstream __RFQDN_ */ ++ p++; ++ if (*p != '_') { ++ return -1; ++ } ++ while (*++p) { ++ if (!qemu_isalnum(*p) && *p != '-' && *p != '.') { ++ break; ++ } ++ } ++ ++ if (*p != '_') { ++ return -1; ++ } ++ p++; ++ } ++ ++ if (!qemu_isalpha(*p)) { ++ return -1; ++ } ++ while (*++p) { ++ if (!qemu_isalnum(*p) && *p != '-' && *p != '_') { ++ break; ++ } ++ } ++ ++ if (complete && *p) { ++ return -1; ++ } ++ return p - str; ++} +diff --git a/qcow2/lib/qapi/qapi-visit-block-core.c b/qcow2/lib/qapi/qapi-visit-block-core.c +new file mode 100644 +index 00000000..b8d709cb +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-block-core.c +@@ -0,0 +1,9398 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi-visit-block-core.h" ++ ++bool visit_type_SnapshotInfo_members(Visitor *v, SnapshotInfo *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "vm-state-size", &obj->vm_state_size, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "date-sec", &obj->date_sec, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "date-nsec", &obj->date_nsec, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "vm-clock-sec", &obj->vm_clock_sec, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "vm-clock-nsec", &obj->vm_clock_nsec, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "icount", &obj->has_icount)) { ++ if (!visit_type_int(v, "icount", &obj->icount, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_SnapshotInfo(Visitor *v, const char *name, ++ SnapshotInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SnapshotInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SnapshotInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SnapshotInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2EncryptionBase_members(Visitor *v, ImageInfoSpecificQCow2EncryptionBase *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevQcow2EncryptionFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2EncryptionBase(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2EncryptionBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificQCow2EncryptionBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificQCow2EncryptionBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificQCow2EncryptionBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2Encryption_members(Visitor *v, ImageInfoSpecificQCow2Encryption *obj, Error **errp) ++{ ++ if (!visit_type_ImageInfoSpecificQCow2EncryptionBase_members(v, (ImageInfoSpecificQCow2EncryptionBase *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS: ++ return visit_type_QCryptoBlockInfoLUKS_members(v, &obj->u.luks, errp); ++ case BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2Encryption(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2Encryption **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificQCow2Encryption), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificQCow2Encryption_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificQCow2Encryption(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_Qcow2BitmapInfoList(Visitor *v, const char *name, ++ Qcow2BitmapInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ Qcow2BitmapInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (Qcow2BitmapInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_Qcow2BitmapInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_Qcow2BitmapInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2_members(Visitor *v, ImageInfoSpecificQCow2 *obj, Error **errp) ++{ ++ bool has_data_file = !!obj->data_file; ++ bool has_encrypt = !!obj->encrypt; ++ ++ if (!visit_type_str(v, "compat", &obj->compat, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "data-file", &has_data_file)) { ++ if (!visit_type_str(v, "data-file", &obj->data_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "data-file-raw", &obj->has_data_file_raw)) { ++ if (!visit_type_bool(v, "data-file-raw", &obj->data_file_raw, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "extended-l2", &obj->has_extended_l2)) { ++ if (!visit_type_bool(v, "extended-l2", &obj->extended_l2, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "lazy-refcounts", &obj->has_lazy_refcounts)) { ++ if (!visit_type_bool(v, "lazy-refcounts", &obj->lazy_refcounts, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "corrupt", &obj->has_corrupt)) { ++ if (!visit_type_bool(v, "corrupt", &obj->corrupt, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "refcount-bits", &obj->refcount_bits, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_ImageInfoSpecificQCow2Encryption(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bitmaps", &obj->has_bitmaps)) { ++ if (!visit_type_Qcow2BitmapInfoList(v, "bitmaps", &obj->bitmaps, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_Qcow2CompressionType(v, "compression-type", &obj->compression_type, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificQCow2), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificQCow2_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificQCow2(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_VmdkExtentInfoList(Visitor *v, const char *name, ++ VmdkExtentInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ VmdkExtentInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (VmdkExtentInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_VmdkExtentInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_VmdkExtentInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificVmdk_members(Visitor *v, ImageInfoSpecificVmdk *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "create-type", &obj->create_type, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "cid", &obj->cid, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "parent-cid", &obj->parent_cid, errp)) { ++ return false; ++ } ++ if (!visit_type_VmdkExtentInfoList(v, "extents", &obj->extents, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificVmdk(Visitor *v, const char *name, ++ ImageInfoSpecificVmdk **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificVmdk), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificVmdk_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificVmdk(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_VmdkExtentInfo_members(Visitor *v, VmdkExtentInfo *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "virtual-size", &obj->virtual_size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { ++ if (!visit_type_int(v, "cluster-size", &obj->cluster_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "compressed", &obj->has_compressed)) { ++ if (!visit_type_bool(v, "compressed", &obj->compressed, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_VmdkExtentInfo(Visitor *v, const char *name, ++ VmdkExtentInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(VmdkExtentInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_VmdkExtentInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_VmdkExtentInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificRbd_members(Visitor *v, ImageInfoSpecificRbd *obj, Error **errp) ++{ ++ if (visit_optional(v, "encryption-format", &obj->has_encryption_format)) { ++ if (!visit_type_RbdImageEncryptionFormat(v, "encryption-format", &obj->encryption_format, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificRbd(Visitor *v, const char *name, ++ ImageInfoSpecificRbd **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificRbd), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificRbd_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificRbd(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificFile_members(Visitor *v, ImageInfoSpecificFile *obj, Error **errp) ++{ ++ if (visit_optional(v, "extent-size-hint", &obj->has_extent_size_hint)) { ++ if (!visit_type_size(v, "extent-size-hint", &obj->extent_size_hint, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificFile(Visitor *v, const char *name, ++ ImageInfoSpecificFile **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificFile), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificFile_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificFile(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificKind(Visitor *v, const char *name, ++ ImageInfoSpecificKind *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &ImageInfoSpecificKind_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2Wrapper_members(Visitor *v, ImageInfoSpecificQCow2Wrapper *obj, Error **errp) ++{ ++ if (!visit_type_ImageInfoSpecificQCow2(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2Wrapper(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2Wrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificQCow2Wrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificQCow2Wrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificQCow2Wrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificVmdkWrapper_members(Visitor *v, ImageInfoSpecificVmdkWrapper *obj, Error **errp) ++{ ++ if (!visit_type_ImageInfoSpecificVmdk(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificVmdkWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificVmdkWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificVmdkWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificVmdkWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificVmdkWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificLUKSWrapper_members(Visitor *v, ImageInfoSpecificLUKSWrapper *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockInfoLUKS(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificLUKSWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificLUKSWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificLUKSWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificLUKSWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificLUKSWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificRbdWrapper_members(Visitor *v, ImageInfoSpecificRbdWrapper *obj, Error **errp) ++{ ++ if (!visit_type_ImageInfoSpecificRbd(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificRbdWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificRbdWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificRbdWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificRbdWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificRbdWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificFileWrapper_members(Visitor *v, ImageInfoSpecificFileWrapper *obj, Error **errp) ++{ ++ if (!visit_type_ImageInfoSpecificFile(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificFileWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificFileWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificFileWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificFileWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificFileWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_ImageInfoSpecific_base_members(Visitor *v, q_obj_ImageInfoSpecific_base *obj, Error **errp) ++{ ++ if (!visit_type_ImageInfoSpecificKind(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecific_members(Visitor *v, ImageInfoSpecific *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_ImageInfoSpecific_base_members(v, (q_obj_ImageInfoSpecific_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->type) { ++ case IMAGE_INFO_SPECIFIC_KIND_QCOW2: ++ return visit_type_ImageInfoSpecificQCow2Wrapper_members(v, &obj->u.qcow2, errp); ++ case IMAGE_INFO_SPECIFIC_KIND_VMDK: ++ return visit_type_ImageInfoSpecificVmdkWrapper_members(v, &obj->u.vmdk, errp); ++ case IMAGE_INFO_SPECIFIC_KIND_LUKS: ++ return visit_type_ImageInfoSpecificLUKSWrapper_members(v, &obj->u.luks, errp); ++ case IMAGE_INFO_SPECIFIC_KIND_RBD: ++ return visit_type_ImageInfoSpecificRbdWrapper_members(v, &obj->u.rbd, errp); ++ case IMAGE_INFO_SPECIFIC_KIND_FILE: ++ return visit_type_ImageInfoSpecificFileWrapper_members(v, &obj->u.file, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecific(Visitor *v, const char *name, ++ ImageInfoSpecific **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecific), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecific_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecific(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_SnapshotInfoList(Visitor *v, const char *name, ++ SnapshotInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ SnapshotInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (SnapshotInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_SnapshotInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SnapshotInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockNodeInfo_members(Visitor *v, BlockNodeInfo *obj, Error **errp) ++{ ++ bool has_backing_filename = !!obj->backing_filename; ++ bool has_full_backing_filename = !!obj->full_backing_filename; ++ bool has_backing_filename_format = !!obj->backing_filename_format; ++ bool has_format_specific = !!obj->format_specific; ++ ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "dirty-flag", &obj->has_dirty_flag)) { ++ if (!visit_type_bool(v, "dirty-flag", &obj->dirty_flag, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "actual-size", &obj->has_actual_size)) { ++ if (!visit_type_int(v, "actual-size", &obj->actual_size, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "virtual-size", &obj->virtual_size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { ++ if (!visit_type_int(v, "cluster-size", &obj->cluster_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "encrypted", &obj->has_encrypted)) { ++ if (!visit_type_bool(v, "encrypted", &obj->encrypted, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "compressed", &obj->has_compressed)) { ++ if (!visit_type_bool(v, "compressed", &obj->compressed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-filename", &has_backing_filename)) { ++ if (!visit_type_str(v, "backing-filename", &obj->backing_filename, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "full-backing-filename", &has_full_backing_filename)) { ++ if (!visit_type_str(v, "full-backing-filename", &obj->full_backing_filename, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-filename-format", &has_backing_filename_format)) { ++ if (!visit_type_str(v, "backing-filename-format", &obj->backing_filename_format, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "snapshots", &obj->has_snapshots)) { ++ if (!visit_type_SnapshotInfoList(v, "snapshots", &obj->snapshots, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "format-specific", &has_format_specific)) { ++ if (!visit_type_ImageInfoSpecific(v, "format-specific", &obj->format_specific, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockNodeInfo(Visitor *v, const char *name, ++ BlockNodeInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockNodeInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockNodeInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockNodeInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfo_members(Visitor *v, ImageInfo *obj, Error **errp) ++{ ++ bool has_backing_image = !!obj->backing_image; ++ ++ if (!visit_type_BlockNodeInfo_members(v, (BlockNodeInfo *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "backing-image", &has_backing_image)) { ++ if (!visit_type_ImageInfo(v, "backing-image", &obj->backing_image, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfo(Visitor *v, const char *name, ++ ImageInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockChildInfo_members(Visitor *v, BlockChildInfo *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockGraphInfo(v, "info", &obj->info, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockChildInfo(Visitor *v, const char *name, ++ BlockChildInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockChildInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockChildInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockChildInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockChildInfoList(Visitor *v, const char *name, ++ BlockChildInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockChildInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockChildInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockChildInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockChildInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockGraphInfo_members(Visitor *v, BlockGraphInfo *obj, Error **errp) ++{ ++ if (!visit_type_BlockNodeInfo_members(v, (BlockNodeInfo *)obj, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockChildInfoList(v, "children", &obj->children, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockGraphInfo(Visitor *v, const char *name, ++ BlockGraphInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockGraphInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockGraphInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockGraphInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageCheck_members(Visitor *v, ImageCheck *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "check-errors", &obj->check_errors, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "image-end-offset", &obj->has_image_end_offset)) { ++ if (!visit_type_int(v, "image-end-offset", &obj->image_end_offset, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "corruptions", &obj->has_corruptions)) { ++ if (!visit_type_int(v, "corruptions", &obj->corruptions, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "leaks", &obj->has_leaks)) { ++ if (!visit_type_int(v, "leaks", &obj->leaks, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "corruptions-fixed", &obj->has_corruptions_fixed)) { ++ if (!visit_type_int(v, "corruptions-fixed", &obj->corruptions_fixed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "leaks-fixed", &obj->has_leaks_fixed)) { ++ if (!visit_type_int(v, "leaks-fixed", &obj->leaks_fixed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "total-clusters", &obj->has_total_clusters)) { ++ if (!visit_type_int(v, "total-clusters", &obj->total_clusters, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "allocated-clusters", &obj->has_allocated_clusters)) { ++ if (!visit_type_int(v, "allocated-clusters", &obj->allocated_clusters, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "fragmented-clusters", &obj->has_fragmented_clusters)) { ++ if (!visit_type_int(v, "fragmented-clusters", &obj->fragmented_clusters, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "compressed-clusters", &obj->has_compressed_clusters)) { ++ if (!visit_type_int(v, "compressed-clusters", &obj->compressed_clusters, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_ImageCheck(Visitor *v, const char *name, ++ ImageCheck **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageCheck), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageCheck_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageCheck(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_MapEntry_members(Visitor *v, MapEntry *obj, Error **errp) ++{ ++ bool has_filename = !!obj->filename; ++ ++ if (!visit_type_int(v, "start", &obj->start, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "length", &obj->length, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "zero", &obj->zero, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "compressed", &obj->compressed, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "depth", &obj->depth, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "present", &obj->present, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "offset", &obj->has_offset)) { ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "filename", &has_filename)) { ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_MapEntry(Visitor *v, const char *name, ++ MapEntry **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(MapEntry), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_MapEntry_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_MapEntry(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCacheInfo_members(Visitor *v, BlockdevCacheInfo *obj, Error **errp) ++{ ++ if (!visit_type_bool(v, "writeback", &obj->writeback, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "direct", &obj->direct, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "no-flush", &obj->no_flush, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCacheInfo(Visitor *v, const char *name, ++ BlockdevCacheInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCacheInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCacheInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCacheInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyInfoList(Visitor *v, const char *name, ++ BlockDirtyInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockDirtyInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockDirtyInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockDirtyInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDeviceInfo_members(Visitor *v, BlockDeviceInfo *obj, Error **errp) ++{ ++ bool has_node_name = !!obj->node_name; ++ bool has_backing_file = !!obj->backing_file; ++ bool has_group = !!obj->group; ++ ++ if (!visit_type_str(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_bool(v, "ro", &obj->ro, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "drv", &obj->drv, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "backing_file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing_file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "backing_file_depth", &obj->backing_file_depth, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "encrypted", &obj->encrypted, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevDetectZeroesOptions(v, "detect_zeroes", &obj->detect_zeroes, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "bps", &obj->bps, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "bps_rd", &obj->bps_rd, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "bps_wr", &obj->bps_wr, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "iops", &obj->iops, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "iops_rd", &obj->iops_rd, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "iops_wr", &obj->iops_wr, errp)) { ++ return false; ++ } ++ if (!visit_type_ImageInfo(v, "image", &obj->image, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "bps_max", &obj->has_bps_max)) { ++ if (!visit_type_int(v, "bps_max", &obj->bps_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_rd_max", &obj->has_bps_rd_max)) { ++ if (!visit_type_int(v, "bps_rd_max", &obj->bps_rd_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_wr_max", &obj->has_bps_wr_max)) { ++ if (!visit_type_int(v, "bps_wr_max", &obj->bps_wr_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_max", &obj->has_iops_max)) { ++ if (!visit_type_int(v, "iops_max", &obj->iops_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_rd_max", &obj->has_iops_rd_max)) { ++ if (!visit_type_int(v, "iops_rd_max", &obj->iops_rd_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_wr_max", &obj->has_iops_wr_max)) { ++ if (!visit_type_int(v, "iops_wr_max", &obj->iops_wr_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_max_length", &obj->has_bps_max_length)) { ++ if (!visit_type_int(v, "bps_max_length", &obj->bps_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_rd_max_length", &obj->has_bps_rd_max_length)) { ++ if (!visit_type_int(v, "bps_rd_max_length", &obj->bps_rd_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_wr_max_length", &obj->has_bps_wr_max_length)) { ++ if (!visit_type_int(v, "bps_wr_max_length", &obj->bps_wr_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_max_length", &obj->has_iops_max_length)) { ++ if (!visit_type_int(v, "iops_max_length", &obj->iops_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_rd_max_length", &obj->has_iops_rd_max_length)) { ++ if (!visit_type_int(v, "iops_rd_max_length", &obj->iops_rd_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_wr_max_length", &obj->has_iops_wr_max_length)) { ++ if (!visit_type_int(v, "iops_wr_max_length", &obj->iops_wr_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_size", &obj->has_iops_size)) { ++ if (!visit_type_int(v, "iops_size", &obj->iops_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "group", &has_group)) { ++ if (!visit_type_str(v, "group", &obj->group, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_BlockdevCacheInfo(v, "cache", &obj->cache, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "write_threshold", &obj->write_threshold, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "dirty-bitmaps", &obj->has_dirty_bitmaps)) { ++ if (!visit_type_BlockDirtyInfoList(v, "dirty-bitmaps", &obj->dirty_bitmaps, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockDeviceInfo(Visitor *v, const char *name, ++ BlockDeviceInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDeviceInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDeviceInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDeviceInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDeviceIoStatus(Visitor *v, const char *name, ++ BlockDeviceIoStatus *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockDeviceIoStatus_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockDirtyInfo_members(Visitor *v, BlockDirtyInfo *obj, Error **errp) ++{ ++ bool has_name = !!obj->name; ++ ++ if (visit_optional(v, "name", &has_name)) { ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "count", &obj->count, errp)) { ++ return false; ++ } ++ if (!visit_type_uint32(v, "granularity", &obj->granularity, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "recording", &obj->recording, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "busy", &obj->busy, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "persistent", &obj->persistent, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "inconsistent", &obj->has_inconsistent)) { ++ if (!visit_type_bool(v, "inconsistent", &obj->inconsistent, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockDirtyInfo(Visitor *v, const char *name, ++ BlockDirtyInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDirtyInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDirtyInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_Qcow2BitmapInfoFlags(Visitor *v, const char *name, ++ Qcow2BitmapInfoFlags *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &Qcow2BitmapInfoFlags_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_Qcow2BitmapInfoFlagsList(Visitor *v, const char *name, ++ Qcow2BitmapInfoFlagsList **obj, Error **errp) ++{ ++ bool ok = false; ++ Qcow2BitmapInfoFlagsList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (Qcow2BitmapInfoFlagsList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_Qcow2BitmapInfoFlags(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_Qcow2BitmapInfoFlagsList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_Qcow2BitmapInfo_members(Visitor *v, Qcow2BitmapInfo *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ if (!visit_type_uint32(v, "granularity", &obj->granularity, errp)) { ++ return false; ++ } ++ if (!visit_type_Qcow2BitmapInfoFlagsList(v, "flags", &obj->flags, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_Qcow2BitmapInfo(Visitor *v, const char *name, ++ Qcow2BitmapInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(Qcow2BitmapInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_Qcow2BitmapInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_Qcow2BitmapInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockLatencyHistogramInfo_members(Visitor *v, BlockLatencyHistogramInfo *obj, Error **errp) ++{ ++ if (!visit_type_uint64List(v, "boundaries", &obj->boundaries, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64List(v, "bins", &obj->bins, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockLatencyHistogramInfo(Visitor *v, const char *name, ++ BlockLatencyHistogramInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockLatencyHistogramInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockLatencyHistogramInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockLatencyHistogramInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockInfo_members(Visitor *v, BlockInfo *obj, Error **errp) ++{ ++ bool has_qdev = !!obj->qdev; ++ bool has_inserted = !!obj->inserted; ++ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "qdev", &has_qdev)) { ++ if (!visit_type_str(v, "qdev", &obj->qdev, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "removable", &obj->removable, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "locked", &obj->locked, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "inserted", &has_inserted)) { ++ if (!visit_type_BlockDeviceInfo(v, "inserted", &obj->inserted, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "tray_open", &obj->has_tray_open)) { ++ if (!visit_type_bool(v, "tray_open", &obj->tray_open, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "io-status", &obj->has_io_status)) { ++ if (!visit_type_BlockDeviceIoStatus(v, "io-status", &obj->io_status, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockInfo(Visitor *v, const char *name, ++ BlockInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockMeasureInfo_members(Visitor *v, BlockMeasureInfo *obj, Error **errp) ++{ ++ if (!visit_type_int(v, "required", &obj->required, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "fully-allocated", &obj->fully_allocated, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "bitmaps", &obj->has_bitmaps)) { ++ if (!visit_type_int(v, "bitmaps", &obj->bitmaps, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockMeasureInfo(Visitor *v, const char *name, ++ BlockMeasureInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockMeasureInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockMeasureInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockMeasureInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockInfoList(Visitor *v, const char *name, ++ BlockInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDeviceTimedStats_members(Visitor *v, BlockDeviceTimedStats *obj, Error **errp) ++{ ++ if (!visit_type_int(v, "interval_length", &obj->interval_length, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "min_rd_latency_ns", &obj->min_rd_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "max_rd_latency_ns", &obj->max_rd_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "avg_rd_latency_ns", &obj->avg_rd_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "min_wr_latency_ns", &obj->min_wr_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "max_wr_latency_ns", &obj->max_wr_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "avg_wr_latency_ns", &obj->avg_wr_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "min_zone_append_latency_ns", &obj->min_zone_append_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "max_zone_append_latency_ns", &obj->max_zone_append_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "avg_zone_append_latency_ns", &obj->avg_zone_append_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "min_flush_latency_ns", &obj->min_flush_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "max_flush_latency_ns", &obj->max_flush_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "avg_flush_latency_ns", &obj->avg_flush_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_number(v, "avg_rd_queue_depth", &obj->avg_rd_queue_depth, errp)) { ++ return false; ++ } ++ if (!visit_type_number(v, "avg_wr_queue_depth", &obj->avg_wr_queue_depth, errp)) { ++ return false; ++ } ++ if (!visit_type_number(v, "avg_zone_append_queue_depth", &obj->avg_zone_append_queue_depth, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockDeviceTimedStats(Visitor *v, const char *name, ++ BlockDeviceTimedStats **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDeviceTimedStats), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDeviceTimedStats_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDeviceTimedStats(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDeviceTimedStatsList(Visitor *v, const char *name, ++ BlockDeviceTimedStatsList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockDeviceTimedStatsList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockDeviceTimedStatsList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockDeviceTimedStats(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDeviceTimedStatsList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDeviceStats_members(Visitor *v, BlockDeviceStats *obj, Error **errp) ++{ ++ bool has_rd_latency_histogram = !!obj->rd_latency_histogram; ++ bool has_wr_latency_histogram = !!obj->wr_latency_histogram; ++ bool has_zone_append_latency_histogram = !!obj->zone_append_latency_histogram; ++ bool has_flush_latency_histogram = !!obj->flush_latency_histogram; ++ ++ if (!visit_type_int(v, "rd_bytes", &obj->rd_bytes, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "wr_bytes", &obj->wr_bytes, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "zone_append_bytes", &obj->zone_append_bytes, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "unmap_bytes", &obj->unmap_bytes, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "rd_operations", &obj->rd_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "wr_operations", &obj->wr_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "zone_append_operations", &obj->zone_append_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "flush_operations", &obj->flush_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "unmap_operations", &obj->unmap_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "rd_total_time_ns", &obj->rd_total_time_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "wr_total_time_ns", &obj->wr_total_time_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "zone_append_total_time_ns", &obj->zone_append_total_time_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "flush_total_time_ns", &obj->flush_total_time_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "unmap_total_time_ns", &obj->unmap_total_time_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "wr_highest_offset", &obj->wr_highest_offset, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "rd_merged", &obj->rd_merged, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "wr_merged", &obj->wr_merged, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "zone_append_merged", &obj->zone_append_merged, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "unmap_merged", &obj->unmap_merged, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "idle_time_ns", &obj->has_idle_time_ns)) { ++ if (!visit_type_int(v, "idle_time_ns", &obj->idle_time_ns, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "failed_rd_operations", &obj->failed_rd_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "failed_wr_operations", &obj->failed_wr_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "failed_zone_append_operations", &obj->failed_zone_append_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "failed_flush_operations", &obj->failed_flush_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "failed_unmap_operations", &obj->failed_unmap_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "invalid_rd_operations", &obj->invalid_rd_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "invalid_wr_operations", &obj->invalid_wr_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "invalid_zone_append_operations", &obj->invalid_zone_append_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "invalid_flush_operations", &obj->invalid_flush_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "invalid_unmap_operations", &obj->invalid_unmap_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "account_invalid", &obj->account_invalid, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "account_failed", &obj->account_failed, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockDeviceTimedStatsList(v, "timed_stats", &obj->timed_stats, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "rd_latency_histogram", &has_rd_latency_histogram)) { ++ if (!visit_type_BlockLatencyHistogramInfo(v, "rd_latency_histogram", &obj->rd_latency_histogram, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "wr_latency_histogram", &has_wr_latency_histogram)) { ++ if (!visit_type_BlockLatencyHistogramInfo(v, "wr_latency_histogram", &obj->wr_latency_histogram, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "zone_append_latency_histogram", &has_zone_append_latency_histogram)) { ++ if (!visit_type_BlockLatencyHistogramInfo(v, "zone_append_latency_histogram", &obj->zone_append_latency_histogram, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "flush_latency_histogram", &has_flush_latency_histogram)) { ++ if (!visit_type_BlockLatencyHistogramInfo(v, "flush_latency_histogram", &obj->flush_latency_histogram, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockDeviceStats(Visitor *v, const char *name, ++ BlockDeviceStats **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDeviceStats), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDeviceStats_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDeviceStats(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockStatsSpecificFile_members(Visitor *v, BlockStatsSpecificFile *obj, Error **errp) ++{ ++ if (!visit_type_uint64(v, "discard-nb-ok", &obj->discard_nb_ok, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "discard-nb-failed", &obj->discard_nb_failed, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "discard-bytes-ok", &obj->discard_bytes_ok, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockStatsSpecificFile(Visitor *v, const char *name, ++ BlockStatsSpecificFile **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockStatsSpecificFile), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockStatsSpecificFile_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockStatsSpecificFile(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockStatsSpecificNvme_members(Visitor *v, BlockStatsSpecificNvme *obj, Error **errp) ++{ ++ if (!visit_type_uint64(v, "completion-errors", &obj->completion_errors, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "aligned-accesses", &obj->aligned_accesses, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "unaligned-accesses", &obj->unaligned_accesses, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockStatsSpecificNvme(Visitor *v, const char *name, ++ BlockStatsSpecificNvme **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockStatsSpecificNvme), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockStatsSpecificNvme_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockStatsSpecificNvme(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockStatsSpecific_base_members(Visitor *v, q_obj_BlockStatsSpecific_base *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevDriver(v, "driver", &obj->driver, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockStatsSpecific_members(Visitor *v, BlockStatsSpecific *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockStatsSpecific_base_members(v, (q_obj_BlockStatsSpecific_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->driver) { ++ case BLOCKDEV_DRIVER_FILE: ++ return visit_type_BlockStatsSpecificFile_members(v, &obj->u.file, errp); ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_DEVICE: ++ return visit_type_BlockStatsSpecificFile_members(v, &obj->u.host_device, errp); ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ case BLOCKDEV_DRIVER_NVME: ++ return visit_type_BlockStatsSpecificNvme_members(v, &obj->u.nvme, errp); ++ case BLOCKDEV_DRIVER_BLKDEBUG: ++ break; ++ case BLOCKDEV_DRIVER_BLKLOGWRITES: ++ break; ++ case BLOCKDEV_DRIVER_BLKREPLAY: ++ break; ++ case BLOCKDEV_DRIVER_BLKVERIFY: ++ break; ++ case BLOCKDEV_DRIVER_BOCHS: ++ break; ++ case BLOCKDEV_DRIVER_CLOOP: ++ break; ++ case BLOCKDEV_DRIVER_COMPRESS: ++ break; ++ case BLOCKDEV_DRIVER_COPY_BEFORE_WRITE: ++ break; ++ case BLOCKDEV_DRIVER_COPY_ON_READ: ++ break; ++ case BLOCKDEV_DRIVER_DMG: ++ break; ++ case BLOCKDEV_DRIVER_SNAPSHOT_ACCESS: ++ break; ++ case BLOCKDEV_DRIVER_FTP: ++ break; ++ case BLOCKDEV_DRIVER_FTPS: ++ break; ++ case BLOCKDEV_DRIVER_GLUSTER: ++ break; ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_CDROM: ++ break; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ case BLOCKDEV_DRIVER_HTTP: ++ break; ++ case BLOCKDEV_DRIVER_HTTPS: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_IO_URING: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_ISCSI: ++ break; ++ case BLOCKDEV_DRIVER_LUKS: ++ break; ++ case BLOCKDEV_DRIVER_NBD: ++ break; ++ case BLOCKDEV_DRIVER_NFS: ++ break; ++ case BLOCKDEV_DRIVER_NULL_AIO: ++ break; ++ case BLOCKDEV_DRIVER_NULL_CO: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_NVME_IO_URING: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_PARALLELS: ++ break; ++ case BLOCKDEV_DRIVER_PREALLOCATE: ++ break; ++ case BLOCKDEV_DRIVER_QCOW: ++ break; ++ case BLOCKDEV_DRIVER_QCOW2: ++ break; ++ case BLOCKDEV_DRIVER_QED: ++ break; ++ case BLOCKDEV_DRIVER_QUORUM: ++ break; ++ case BLOCKDEV_DRIVER_RAW: ++ break; ++ case BLOCKDEV_DRIVER_RBD: ++ break; ++#if defined(CONFIG_REPLICATION) ++ case BLOCKDEV_DRIVER_REPLICATION: ++ break; ++#endif /* defined(CONFIG_REPLICATION) */ ++ case BLOCKDEV_DRIVER_SSH: ++ break; ++ case BLOCKDEV_DRIVER_THROTTLE: ++ break; ++ case BLOCKDEV_DRIVER_VDI: ++ break; ++ case BLOCKDEV_DRIVER_VHDX: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VFIO_PCI: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_USER: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_VDPA: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_VMDK: ++ break; ++ case BLOCKDEV_DRIVER_VPC: ++ break; ++ case BLOCKDEV_DRIVER_VVFAT: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockStatsSpecific(Visitor *v, const char *name, ++ BlockStatsSpecific **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockStatsSpecific), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockStatsSpecific_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockStatsSpecific(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockStats_members(Visitor *v, BlockStats *obj, Error **errp) ++{ ++ bool has_device = !!obj->device; ++ bool has_qdev = !!obj->qdev; ++ bool has_node_name = !!obj->node_name; ++ bool has_driver_specific = !!obj->driver_specific; ++ bool has_parent = !!obj->parent; ++ bool has_backing = !!obj->backing; ++ ++ if (visit_optional(v, "device", &has_device)) { ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "qdev", &has_qdev)) { ++ if (!visit_type_str(v, "qdev", &obj->qdev, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_BlockDeviceStats(v, "stats", &obj->stats, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "driver-specific", &has_driver_specific)) { ++ if (!visit_type_BlockStatsSpecific(v, "driver-specific", &obj->driver_specific, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "parent", &has_parent)) { ++ if (!visit_type_BlockStats(v, "parent", &obj->parent, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing", &has_backing)) { ++ if (!visit_type_BlockStats(v, "backing", &obj->backing, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockStats(Visitor *v, const char *name, ++ BlockStats **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockStats), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockStats_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockStats(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_query_blockstats_arg_members(Visitor *v, q_obj_query_blockstats_arg *obj, Error **errp) ++{ ++ if (visit_optional(v, "query-nodes", &obj->has_query_nodes)) { ++ if (!visit_type_bool(v, "query-nodes", &obj->query_nodes, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockStatsList(Visitor *v, const char *name, ++ BlockStatsList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockStatsList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockStatsList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockStats(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockStatsList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOnError(Visitor *v, const char *name, ++ BlockdevOnError *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevOnError_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_MirrorSyncMode(Visitor *v, const char *name, ++ MirrorSyncMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &MirrorSyncMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BitmapSyncMode(Visitor *v, const char *name, ++ BitmapSyncMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BitmapSyncMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_MirrorCopyMode(Visitor *v, const char *name, ++ MirrorCopyMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &MirrorCopyMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockJobInfoMirror_members(Visitor *v, BlockJobInfoMirror *obj, Error **errp) ++{ ++ if (!visit_type_bool(v, "actively-synced", &obj->actively_synced, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobInfoMirror(Visitor *v, const char *name, ++ BlockJobInfoMirror **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockJobInfoMirror), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockJobInfoMirror_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockJobInfoMirror(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockJobInfo_base_members(Visitor *v, q_obj_BlockJobInfo_base *obj, Error **errp) ++{ ++ bool has_error = !!obj->error; ++ ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "len", &obj->len, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "busy", &obj->busy, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "paused", &obj->paused, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockDeviceIoStatus(v, "io-status", &obj->io_status, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "ready", &obj->ready, errp)) { ++ return false; ++ } ++ if (!visit_type_JobStatus(v, "status", &obj->status, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "auto-finalize", &obj->auto_finalize, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "auto-dismiss", &obj->auto_dismiss, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "error", &has_error)) { ++ if (!visit_type_str(v, "error", &obj->error, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobInfo_members(Visitor *v, BlockJobInfo *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockJobInfo_base_members(v, (q_obj_BlockJobInfo_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->type) { ++ case JOB_TYPE_MIRROR: ++ return visit_type_BlockJobInfoMirror_members(v, &obj->u.mirror, errp); ++ case JOB_TYPE_COMMIT: ++ break; ++ case JOB_TYPE_STREAM: ++ break; ++ case JOB_TYPE_BACKUP: ++ break; ++ case JOB_TYPE_CREATE: ++ break; ++ case JOB_TYPE_AMEND: ++ break; ++ case JOB_TYPE_SNAPSHOT_LOAD: ++ break; ++ case JOB_TYPE_SNAPSHOT_SAVE: ++ break; ++ case JOB_TYPE_SNAPSHOT_DELETE: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobInfo(Visitor *v, const char *name, ++ BlockJobInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockJobInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockJobInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockJobInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockJobInfoList(Visitor *v, const char *name, ++ BlockJobInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockJobInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockJobInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockJobInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockJobInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_block_resize_arg_members(Visitor *v, q_obj_block_resize_arg *obj, Error **errp) ++{ ++ bool has_device = !!obj->device; ++ bool has_node_name = !!obj->node_name; ++ ++ if (visit_optional(v, "device", &has_device)) { ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_NewImageMode(Visitor *v, const char *name, ++ NewImageMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &NewImageMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevSnapshotSync_members(Visitor *v, BlockdevSnapshotSync *obj, Error **errp) ++{ ++ bool has_device = !!obj->device; ++ bool has_node_name = !!obj->node_name; ++ bool has_snapshot_node_name = !!obj->snapshot_node_name; ++ bool has_format = !!obj->format; ++ ++ if (visit_optional(v, "device", &has_device)) { ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "snapshot-file", &obj->snapshot_file, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "snapshot-node-name", &has_snapshot_node_name)) { ++ if (!visit_type_str(v, "snapshot-node-name", &obj->snapshot_node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "format", &has_format)) { ++ if (!visit_type_str(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "mode", &obj->has_mode)) { ++ if (!visit_type_NewImageMode(v, "mode", &obj->mode, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevSnapshotSync(Visitor *v, const char *name, ++ BlockdevSnapshotSync **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevSnapshotSync), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevSnapshotSync_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevSnapshotSync(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevSnapshot_members(Visitor *v, BlockdevSnapshot *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node", &obj->node, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "overlay", &obj->overlay, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevSnapshot(Visitor *v, const char *name, ++ BlockdevSnapshot **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevSnapshot), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevSnapshot_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevSnapshot(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BackupPerf_members(Visitor *v, BackupPerf *obj, Error **errp) ++{ ++ if (visit_optional(v, "use-copy-range", &obj->has_use_copy_range)) { ++ if (!visit_type_bool(v, "use-copy-range", &obj->use_copy_range, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "max-workers", &obj->has_max_workers)) { ++ if (!visit_type_int(v, "max-workers", &obj->max_workers, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "max-chunk", &obj->has_max_chunk)) { ++ if (!visit_type_int64(v, "max-chunk", &obj->max_chunk, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BackupPerf(Visitor *v, const char *name, ++ BackupPerf **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BackupPerf), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BackupPerf_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BackupPerf(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BackupCommon_members(Visitor *v, BackupCommon *obj, Error **errp) ++{ ++ bool has_job_id = !!obj->job_id; ++ bool has_bitmap = !!obj->bitmap; ++ bool has_filter_node_name = !!obj->filter_node_name; ++ bool has_x_perf = !!obj->x_perf; ++ ++ if (visit_optional(v, "job-id", &has_job_id)) { ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_MirrorSyncMode(v, "sync", &obj->sync, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "speed", &obj->has_speed)) { ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bitmap", &has_bitmap)) { ++ if (!visit_type_str(v, "bitmap", &obj->bitmap, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bitmap-mode", &obj->has_bitmap_mode)) { ++ if (!visit_type_BitmapSyncMode(v, "bitmap-mode", &obj->bitmap_mode, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "compress", &obj->has_compress)) { ++ if (!visit_type_bool(v, "compress", &obj->compress, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-source-error", &obj->has_on_source_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-source-error", &obj->on_source_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-target-error", &obj->has_on_target_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-target-error", &obj->on_target_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-finalize", &obj->has_auto_finalize)) { ++ if (!visit_type_bool(v, "auto-finalize", &obj->auto_finalize, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-dismiss", &obj->has_auto_dismiss)) { ++ if (!visit_type_bool(v, "auto-dismiss", &obj->auto_dismiss, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "filter-node-name", &has_filter_node_name)) { ++ if (!visit_type_str(v, "filter-node-name", &obj->filter_node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "discard-source", &obj->has_discard_source)) { ++ if (!visit_type_bool(v, "discard-source", &obj->discard_source, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "x-perf", &has_x_perf)) { ++ if (visit_policy_reject(v, "x-perf", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-perf", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_BackupPerf(v, "x-perf", &obj->x_perf, errp)) { ++ return false; ++ } ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BackupCommon(Visitor *v, const char *name, ++ BackupCommon **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BackupCommon), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BackupCommon_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BackupCommon(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_DriveBackup_members(Visitor *v, DriveBackup *obj, Error **errp) ++{ ++ bool has_format = !!obj->format; ++ ++ if (!visit_type_BackupCommon_members(v, (BackupCommon *)obj, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "format", &has_format)) { ++ if (!visit_type_str(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "mode", &obj->has_mode)) { ++ if (!visit_type_NewImageMode(v, "mode", &obj->mode, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_DriveBackup(Visitor *v, const char *name, ++ DriveBackup **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(DriveBackup), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_DriveBackup_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_DriveBackup(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevBackup_members(Visitor *v, BlockdevBackup *obj, Error **errp) ++{ ++ if (!visit_type_BackupCommon_members(v, (BackupCommon *)obj, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevBackup(Visitor *v, const char *name, ++ BlockdevBackup **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevBackup), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevBackup_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevBackup(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_change_backing_file_arg_members(Visitor *v, q_obj_change_backing_file_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "image-node-name", &obj->image_node_name, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_commit_arg_members(Visitor *v, q_obj_block_commit_arg *obj, Error **errp) ++{ ++ bool has_job_id = !!obj->job_id; ++ bool has_base_node = !!obj->base_node; ++ bool has_base = !!obj->base; ++ bool has_top_node = !!obj->top_node; ++ bool has_top = !!obj->top; ++ bool has_backing_file = !!obj->backing_file; ++ bool has_filter_node_name = !!obj->filter_node_name; ++ ++ if (visit_optional(v, "job-id", &has_job_id)) { ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "base-node", &has_base_node)) { ++ if (!visit_type_str(v, "base-node", &obj->base_node, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "base", &has_base)) { ++ if (visit_policy_reject(v, "base", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "base", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_str(v, "base", &obj->base, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "top-node", &has_top_node)) { ++ if (!visit_type_str(v, "top-node", &obj->top_node, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "top", &has_top)) { ++ if (visit_policy_reject(v, "top", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "top", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_str(v, "top", &obj->top, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "backing-file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-mask-protocol", &obj->has_backing_mask_protocol)) { ++ if (!visit_type_bool(v, "backing-mask-protocol", &obj->backing_mask_protocol, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "speed", &obj->has_speed)) { ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-error", &obj->has_on_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-error", &obj->on_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "filter-node-name", &has_filter_node_name)) { ++ if (!visit_type_str(v, "filter-node-name", &obj->filter_node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-finalize", &obj->has_auto_finalize)) { ++ if (!visit_type_bool(v, "auto-finalize", &obj->auto_finalize, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-dismiss", &obj->has_auto_dismiss)) { ++ if (!visit_type_bool(v, "auto-dismiss", &obj->auto_dismiss, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_query_named_block_nodes_arg_members(Visitor *v, q_obj_query_named_block_nodes_arg *obj, Error **errp) ++{ ++ if (visit_optional(v, "flat", &obj->has_flat)) { ++ if (!visit_type_bool(v, "flat", &obj->flat, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockDeviceInfoList(Visitor *v, const char *name, ++ BlockDeviceInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockDeviceInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockDeviceInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockDeviceInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDeviceInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_XDbgBlockGraphNodeType(Visitor *v, const char *name, ++ XDbgBlockGraphNodeType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &XDbgBlockGraphNodeType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_XDbgBlockGraphNode_members(Visitor *v, XDbgBlockGraphNode *obj, Error **errp) ++{ ++ if (!visit_type_uint64(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ if (!visit_type_XDbgBlockGraphNodeType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_XDbgBlockGraphNode(Visitor *v, const char *name, ++ XDbgBlockGraphNode **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(XDbgBlockGraphNode), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_XDbgBlockGraphNode_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_XDbgBlockGraphNode(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockPermission(Visitor *v, const char *name, ++ BlockPermission *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockPermission_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockPermissionList(Visitor *v, const char *name, ++ BlockPermissionList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockPermissionList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockPermissionList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockPermission(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockPermissionList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_XDbgBlockGraphEdge_members(Visitor *v, XDbgBlockGraphEdge *obj, Error **errp) ++{ ++ if (!visit_type_uint64(v, "parent", &obj->parent, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "child", &obj->child, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockPermissionList(v, "perm", &obj->perm, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockPermissionList(v, "shared-perm", &obj->shared_perm, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_XDbgBlockGraphEdge(Visitor *v, const char *name, ++ XDbgBlockGraphEdge **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(XDbgBlockGraphEdge), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_XDbgBlockGraphEdge_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_XDbgBlockGraphEdge(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_XDbgBlockGraphNodeList(Visitor *v, const char *name, ++ XDbgBlockGraphNodeList **obj, Error **errp) ++{ ++ bool ok = false; ++ XDbgBlockGraphNodeList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (XDbgBlockGraphNodeList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_XDbgBlockGraphNode(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_XDbgBlockGraphNodeList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_XDbgBlockGraphEdgeList(Visitor *v, const char *name, ++ XDbgBlockGraphEdgeList **obj, Error **errp) ++{ ++ bool ok = false; ++ XDbgBlockGraphEdgeList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (XDbgBlockGraphEdgeList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_XDbgBlockGraphEdge(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_XDbgBlockGraphEdgeList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_XDbgBlockGraph_members(Visitor *v, XDbgBlockGraph *obj, Error **errp) ++{ ++ if (!visit_type_XDbgBlockGraphNodeList(v, "nodes", &obj->nodes, errp)) { ++ return false; ++ } ++ if (!visit_type_XDbgBlockGraphEdgeList(v, "edges", &obj->edges, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_XDbgBlockGraph(Visitor *v, const char *name, ++ XDbgBlockGraph **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(XDbgBlockGraph), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_XDbgBlockGraph_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_XDbgBlockGraph(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_DriveMirror_members(Visitor *v, DriveMirror *obj, Error **errp) ++{ ++ bool has_job_id = !!obj->job_id; ++ bool has_format = !!obj->format; ++ bool has_node_name = !!obj->node_name; ++ bool has_replaces = !!obj->replaces; ++ ++ if (visit_optional(v, "job-id", &has_job_id)) { ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "format", &has_format)) { ++ if (!visit_type_str(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "replaces", &has_replaces)) { ++ if (!visit_type_str(v, "replaces", &obj->replaces, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_MirrorSyncMode(v, "sync", &obj->sync, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "mode", &obj->has_mode)) { ++ if (!visit_type_NewImageMode(v, "mode", &obj->mode, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "speed", &obj->has_speed)) { ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "granularity", &obj->has_granularity)) { ++ if (!visit_type_uint32(v, "granularity", &obj->granularity, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "buf-size", &obj->has_buf_size)) { ++ if (!visit_type_int(v, "buf-size", &obj->buf_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-source-error", &obj->has_on_source_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-source-error", &obj->on_source_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-target-error", &obj->has_on_target_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-target-error", &obj->on_target_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "unmap", &obj->has_unmap)) { ++ if (!visit_type_bool(v, "unmap", &obj->unmap, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "copy-mode", &obj->has_copy_mode)) { ++ if (!visit_type_MirrorCopyMode(v, "copy-mode", &obj->copy_mode, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-finalize", &obj->has_auto_finalize)) { ++ if (!visit_type_bool(v, "auto-finalize", &obj->auto_finalize, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-dismiss", &obj->has_auto_dismiss)) { ++ if (!visit_type_bool(v, "auto-dismiss", &obj->auto_dismiss, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_DriveMirror(Visitor *v, const char *name, ++ DriveMirror **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(DriveMirror), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_DriveMirror_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_DriveMirror(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyBitmap_members(Visitor *v, BlockDirtyBitmap *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node", &obj->node, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockDirtyBitmap(Visitor *v, const char *name, ++ BlockDirtyBitmap **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDirtyBitmap), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDirtyBitmap_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyBitmap(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyBitmapAdd_members(Visitor *v, BlockDirtyBitmapAdd *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node", &obj->node, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "granularity", &obj->has_granularity)) { ++ if (!visit_type_uint32(v, "granularity", &obj->granularity, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "persistent", &obj->has_persistent)) { ++ if (!visit_type_bool(v, "persistent", &obj->persistent, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "disabled", &obj->has_disabled)) { ++ if (!visit_type_bool(v, "disabled", &obj->disabled, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockDirtyBitmapAdd(Visitor *v, const char *name, ++ BlockDirtyBitmapAdd **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDirtyBitmapAdd), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDirtyBitmapAdd_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyBitmapAdd(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyBitmapOrStr(Visitor *v, const char *name, ++ BlockDirtyBitmapOrStr **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_alternate(v, name, (GenericAlternate **)obj, ++ sizeof(**obj), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ switch ((*obj)->type) { ++ case QTYPE_QSTRING: ++ ok = visit_type_str(v, name, &(*obj)->u.local, errp); ++ break; ++ case QTYPE_QDICT: ++ if (!visit_start_struct(v, name, NULL, 0, errp)) { ++ break; ++ } ++ if (visit_type_BlockDirtyBitmap_members(v, &(*obj)->u.external, errp)) { ++ ok = visit_check_struct(v, errp); ++ } ++ visit_end_struct(v, NULL); ++ break; ++ case QTYPE_NONE: ++ abort(); ++ default: ++ assert(visit_is_input(v)); ++ error_setg(errp, ++ "Invalid parameter type for '%s', expected: BlockDirtyBitmapOrStr", ++ name ? name : "null"); ++ /* Avoid passing invalid *obj to qapi_free_BlockDirtyBitmapOrStr() */ ++ g_free(*obj); ++ *obj = NULL; ++ } ++out_obj: ++ visit_end_alternate(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyBitmapOrStr(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyBitmapOrStrList(Visitor *v, const char *name, ++ BlockDirtyBitmapOrStrList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockDirtyBitmapOrStrList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockDirtyBitmapOrStrList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockDirtyBitmapOrStr(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyBitmapOrStrList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyBitmapMerge_members(Visitor *v, BlockDirtyBitmapMerge *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node", &obj->node, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockDirtyBitmapOrStrList(v, "bitmaps", &obj->bitmaps, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockDirtyBitmapMerge(Visitor *v, const char *name, ++ BlockDirtyBitmapMerge **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDirtyBitmapMerge), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDirtyBitmapMerge_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyBitmapMerge(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyBitmapSha256_members(Visitor *v, BlockDirtyBitmapSha256 *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "sha256", &obj->sha256, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockDirtyBitmapSha256(Visitor *v, const char *name, ++ BlockDirtyBitmapSha256 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDirtyBitmapSha256), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDirtyBitmapSha256_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyBitmapSha256(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_blockdev_mirror_arg_members(Visitor *v, q_obj_blockdev_mirror_arg *obj, Error **errp) ++{ ++ bool has_job_id = !!obj->job_id; ++ bool has_replaces = !!obj->replaces; ++ bool has_filter_node_name = !!obj->filter_node_name; ++ ++ if (visit_optional(v, "job-id", &has_job_id)) { ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "replaces", &has_replaces)) { ++ if (!visit_type_str(v, "replaces", &obj->replaces, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_MirrorSyncMode(v, "sync", &obj->sync, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "speed", &obj->has_speed)) { ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "granularity", &obj->has_granularity)) { ++ if (!visit_type_uint32(v, "granularity", &obj->granularity, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "buf-size", &obj->has_buf_size)) { ++ if (!visit_type_int(v, "buf-size", &obj->buf_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-source-error", &obj->has_on_source_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-source-error", &obj->on_source_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-target-error", &obj->has_on_target_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-target-error", &obj->on_target_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "filter-node-name", &has_filter_node_name)) { ++ if (!visit_type_str(v, "filter-node-name", &obj->filter_node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "copy-mode", &obj->has_copy_mode)) { ++ if (!visit_type_MirrorCopyMode(v, "copy-mode", &obj->copy_mode, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-finalize", &obj->has_auto_finalize)) { ++ if (!visit_type_bool(v, "auto-finalize", &obj->auto_finalize, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-dismiss", &obj->has_auto_dismiss)) { ++ if (!visit_type_bool(v, "auto-dismiss", &obj->auto_dismiss, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockIOThrottle_members(Visitor *v, BlockIOThrottle *obj, Error **errp) ++{ ++ bool has_device = !!obj->device; ++ bool has_id = !!obj->id; ++ bool has_group = !!obj->group; ++ ++ if (visit_optional(v, "device", &has_device)) { ++ if (visit_policy_reject(v, "device", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "device", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "id", &has_id)) { ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "bps", &obj->bps, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "bps_rd", &obj->bps_rd, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "bps_wr", &obj->bps_wr, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "iops", &obj->iops, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "iops_rd", &obj->iops_rd, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "iops_wr", &obj->iops_wr, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "bps_max", &obj->has_bps_max)) { ++ if (!visit_type_int(v, "bps_max", &obj->bps_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_rd_max", &obj->has_bps_rd_max)) { ++ if (!visit_type_int(v, "bps_rd_max", &obj->bps_rd_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_wr_max", &obj->has_bps_wr_max)) { ++ if (!visit_type_int(v, "bps_wr_max", &obj->bps_wr_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_max", &obj->has_iops_max)) { ++ if (!visit_type_int(v, "iops_max", &obj->iops_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_rd_max", &obj->has_iops_rd_max)) { ++ if (!visit_type_int(v, "iops_rd_max", &obj->iops_rd_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_wr_max", &obj->has_iops_wr_max)) { ++ if (!visit_type_int(v, "iops_wr_max", &obj->iops_wr_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_max_length", &obj->has_bps_max_length)) { ++ if (!visit_type_int(v, "bps_max_length", &obj->bps_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_rd_max_length", &obj->has_bps_rd_max_length)) { ++ if (!visit_type_int(v, "bps_rd_max_length", &obj->bps_rd_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_wr_max_length", &obj->has_bps_wr_max_length)) { ++ if (!visit_type_int(v, "bps_wr_max_length", &obj->bps_wr_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_max_length", &obj->has_iops_max_length)) { ++ if (!visit_type_int(v, "iops_max_length", &obj->iops_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_rd_max_length", &obj->has_iops_rd_max_length)) { ++ if (!visit_type_int(v, "iops_rd_max_length", &obj->iops_rd_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_wr_max_length", &obj->has_iops_wr_max_length)) { ++ if (!visit_type_int(v, "iops_wr_max_length", &obj->iops_wr_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_size", &obj->has_iops_size)) { ++ if (!visit_type_int(v, "iops_size", &obj->iops_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "group", &has_group)) { ++ if (!visit_type_str(v, "group", &obj->group, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockIOThrottle(Visitor *v, const char *name, ++ BlockIOThrottle **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockIOThrottle), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockIOThrottle_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockIOThrottle(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ThrottleLimits_members(Visitor *v, ThrottleLimits *obj, Error **errp) ++{ ++ if (visit_optional(v, "iops-total", &obj->has_iops_total)) { ++ if (!visit_type_int(v, "iops-total", &obj->iops_total, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-total-max", &obj->has_iops_total_max)) { ++ if (!visit_type_int(v, "iops-total-max", &obj->iops_total_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-total-max-length", &obj->has_iops_total_max_length)) { ++ if (!visit_type_int(v, "iops-total-max-length", &obj->iops_total_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-read", &obj->has_iops_read)) { ++ if (!visit_type_int(v, "iops-read", &obj->iops_read, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-read-max", &obj->has_iops_read_max)) { ++ if (!visit_type_int(v, "iops-read-max", &obj->iops_read_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-read-max-length", &obj->has_iops_read_max_length)) { ++ if (!visit_type_int(v, "iops-read-max-length", &obj->iops_read_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-write", &obj->has_iops_write)) { ++ if (!visit_type_int(v, "iops-write", &obj->iops_write, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-write-max", &obj->has_iops_write_max)) { ++ if (!visit_type_int(v, "iops-write-max", &obj->iops_write_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-write-max-length", &obj->has_iops_write_max_length)) { ++ if (!visit_type_int(v, "iops-write-max-length", &obj->iops_write_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-total", &obj->has_bps_total)) { ++ if (!visit_type_int(v, "bps-total", &obj->bps_total, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-total-max", &obj->has_bps_total_max)) { ++ if (!visit_type_int(v, "bps-total-max", &obj->bps_total_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-total-max-length", &obj->has_bps_total_max_length)) { ++ if (!visit_type_int(v, "bps-total-max-length", &obj->bps_total_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-read", &obj->has_bps_read)) { ++ if (!visit_type_int(v, "bps-read", &obj->bps_read, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-read-max", &obj->has_bps_read_max)) { ++ if (!visit_type_int(v, "bps-read-max", &obj->bps_read_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-read-max-length", &obj->has_bps_read_max_length)) { ++ if (!visit_type_int(v, "bps-read-max-length", &obj->bps_read_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-write", &obj->has_bps_write)) { ++ if (!visit_type_int(v, "bps-write", &obj->bps_write, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-write-max", &obj->has_bps_write_max)) { ++ if (!visit_type_int(v, "bps-write-max", &obj->bps_write_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-write-max-length", &obj->has_bps_write_max_length)) { ++ if (!visit_type_int(v, "bps-write-max-length", &obj->bps_write_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-size", &obj->has_iops_size)) { ++ if (!visit_type_int(v, "iops-size", &obj->iops_size, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_ThrottleLimits(Visitor *v, const char *name, ++ ThrottleLimits **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ThrottleLimits), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ThrottleLimits_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ThrottleLimits(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ThrottleGroupProperties_members(Visitor *v, ThrottleGroupProperties *obj, Error **errp) ++{ ++ bool has_limits = !!obj->limits; ++ ++ if (visit_optional(v, "limits", &has_limits)) { ++ if (!visit_type_ThrottleLimits(v, "limits", &obj->limits, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "x-iops-total", &obj->has_x_iops_total)) { ++ if (visit_policy_reject(v, "x-iops-total", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-total", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-total", &obj->x_iops_total, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-total-max", &obj->has_x_iops_total_max)) { ++ if (visit_policy_reject(v, "x-iops-total-max", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-total-max", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-total-max", &obj->x_iops_total_max, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-total-max-length", &obj->has_x_iops_total_max_length)) { ++ if (visit_policy_reject(v, "x-iops-total-max-length", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-total-max-length", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-total-max-length", &obj->x_iops_total_max_length, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-read", &obj->has_x_iops_read)) { ++ if (visit_policy_reject(v, "x-iops-read", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-read", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-read", &obj->x_iops_read, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-read-max", &obj->has_x_iops_read_max)) { ++ if (visit_policy_reject(v, "x-iops-read-max", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-read-max", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-read-max", &obj->x_iops_read_max, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-read-max-length", &obj->has_x_iops_read_max_length)) { ++ if (visit_policy_reject(v, "x-iops-read-max-length", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-read-max-length", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-read-max-length", &obj->x_iops_read_max_length, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-write", &obj->has_x_iops_write)) { ++ if (visit_policy_reject(v, "x-iops-write", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-write", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-write", &obj->x_iops_write, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-write-max", &obj->has_x_iops_write_max)) { ++ if (visit_policy_reject(v, "x-iops-write-max", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-write-max", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-write-max", &obj->x_iops_write_max, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-write-max-length", &obj->has_x_iops_write_max_length)) { ++ if (visit_policy_reject(v, "x-iops-write-max-length", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-write-max-length", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-write-max-length", &obj->x_iops_write_max_length, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-total", &obj->has_x_bps_total)) { ++ if (visit_policy_reject(v, "x-bps-total", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-total", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-total", &obj->x_bps_total, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-total-max", &obj->has_x_bps_total_max)) { ++ if (visit_policy_reject(v, "x-bps-total-max", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-total-max", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-total-max", &obj->x_bps_total_max, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-total-max-length", &obj->has_x_bps_total_max_length)) { ++ if (visit_policy_reject(v, "x-bps-total-max-length", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-total-max-length", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-total-max-length", &obj->x_bps_total_max_length, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-read", &obj->has_x_bps_read)) { ++ if (visit_policy_reject(v, "x-bps-read", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-read", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-read", &obj->x_bps_read, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-read-max", &obj->has_x_bps_read_max)) { ++ if (visit_policy_reject(v, "x-bps-read-max", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-read-max", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-read-max", &obj->x_bps_read_max, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-read-max-length", &obj->has_x_bps_read_max_length)) { ++ if (visit_policy_reject(v, "x-bps-read-max-length", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-read-max-length", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-read-max-length", &obj->x_bps_read_max_length, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-write", &obj->has_x_bps_write)) { ++ if (visit_policy_reject(v, "x-bps-write", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-write", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-write", &obj->x_bps_write, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-write-max", &obj->has_x_bps_write_max)) { ++ if (visit_policy_reject(v, "x-bps-write-max", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-write-max", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-write-max", &obj->x_bps_write_max, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-write-max-length", &obj->has_x_bps_write_max_length)) { ++ if (visit_policy_reject(v, "x-bps-write-max-length", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-write-max-length", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-write-max-length", &obj->x_bps_write_max_length, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-size", &obj->has_x_iops_size)) { ++ if (visit_policy_reject(v, "x-iops-size", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-size", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-size", &obj->x_iops_size, errp)) { ++ return false; ++ } ++ } ++ } ++ return true; ++} ++ ++bool visit_type_ThrottleGroupProperties(Visitor *v, const char *name, ++ ThrottleGroupProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ThrottleGroupProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ThrottleGroupProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ThrottleGroupProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_block_stream_arg_members(Visitor *v, q_obj_block_stream_arg *obj, Error **errp) ++{ ++ bool has_job_id = !!obj->job_id; ++ bool has_base = !!obj->base; ++ bool has_base_node = !!obj->base_node; ++ bool has_backing_file = !!obj->backing_file; ++ bool has_bottom = !!obj->bottom; ++ bool has_filter_node_name = !!obj->filter_node_name; ++ ++ if (visit_optional(v, "job-id", &has_job_id)) { ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "base", &has_base)) { ++ if (!visit_type_str(v, "base", &obj->base, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "base-node", &has_base_node)) { ++ if (!visit_type_str(v, "base-node", &obj->base_node, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-mask-protocol", &obj->has_backing_mask_protocol)) { ++ if (!visit_type_bool(v, "backing-mask-protocol", &obj->backing_mask_protocol, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bottom", &has_bottom)) { ++ if (!visit_type_str(v, "bottom", &obj->bottom, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "speed", &obj->has_speed)) { ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-error", &obj->has_on_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-error", &obj->on_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "filter-node-name", &has_filter_node_name)) { ++ if (!visit_type_str(v, "filter-node-name", &obj->filter_node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-finalize", &obj->has_auto_finalize)) { ++ if (!visit_type_bool(v, "auto-finalize", &obj->auto_finalize, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-dismiss", &obj->has_auto_dismiss)) { ++ if (!visit_type_bool(v, "auto-dismiss", &obj->auto_dismiss, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_set_speed_arg_members(Visitor *v, q_obj_block_job_set_speed_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_cancel_arg_members(Visitor *v, q_obj_block_job_cancel_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "force", &obj->has_force)) { ++ if (!visit_type_bool(v, "force", &obj->force, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_pause_arg_members(Visitor *v, q_obj_block_job_pause_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_resume_arg_members(Visitor *v, q_obj_block_job_resume_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_complete_arg_members(Visitor *v, q_obj_block_job_complete_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_dismiss_arg_members(Visitor *v, q_obj_block_job_dismiss_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_finalize_arg_members(Visitor *v, q_obj_block_job_finalize_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobChangeOptionsMirror_members(Visitor *v, BlockJobChangeOptionsMirror *obj, Error **errp) ++{ ++ if (!visit_type_MirrorCopyMode(v, "copy-mode", &obj->copy_mode, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobChangeOptionsMirror(Visitor *v, const char *name, ++ BlockJobChangeOptionsMirror **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockJobChangeOptionsMirror), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockJobChangeOptionsMirror_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockJobChangeOptionsMirror(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockJobChangeOptions_base_members(Visitor *v, q_obj_BlockJobChangeOptions_base *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobChangeOptions_members(Visitor *v, BlockJobChangeOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockJobChangeOptions_base_members(v, (q_obj_BlockJobChangeOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->type) { ++ case JOB_TYPE_MIRROR: ++ return visit_type_BlockJobChangeOptionsMirror_members(v, &obj->u.mirror, errp); ++ case JOB_TYPE_COMMIT: ++ break; ++ case JOB_TYPE_STREAM: ++ break; ++ case JOB_TYPE_BACKUP: ++ break; ++ case JOB_TYPE_CREATE: ++ break; ++ case JOB_TYPE_AMEND: ++ break; ++ case JOB_TYPE_SNAPSHOT_LOAD: ++ break; ++ case JOB_TYPE_SNAPSHOT_SAVE: ++ break; ++ case JOB_TYPE_SNAPSHOT_DELETE: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobChangeOptions(Visitor *v, const char *name, ++ BlockJobChangeOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockJobChangeOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockJobChangeOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockJobChangeOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevDiscardOptions(Visitor *v, const char *name, ++ BlockdevDiscardOptions *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevDiscardOptions_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevDetectZeroesOptions(Visitor *v, const char *name, ++ BlockdevDetectZeroesOptions *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevDetectZeroesOptions_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevAioOptions(Visitor *v, const char *name, ++ BlockdevAioOptions *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevAioOptions_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevCacheOptions_members(Visitor *v, BlockdevCacheOptions *obj, Error **errp) ++{ ++ if (visit_optional(v, "direct", &obj->has_direct)) { ++ if (!visit_type_bool(v, "direct", &obj->direct, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "no-flush", &obj->has_no_flush)) { ++ if (!visit_type_bool(v, "no-flush", &obj->no_flush, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCacheOptions(Visitor *v, const char *name, ++ BlockdevCacheOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCacheOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCacheOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCacheOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevDriver(Visitor *v, const char *name, ++ BlockdevDriver *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevDriver_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsFile_members(Visitor *v, BlockdevOptionsFile *obj, Error **errp) ++{ ++ bool has_pr_manager = !!obj->pr_manager; ++ ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "pr-manager", &has_pr_manager)) { ++ if (!visit_type_str(v, "pr-manager", &obj->pr_manager, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "locking", &obj->has_locking)) { ++ if (!visit_type_OnOffAuto(v, "locking", &obj->locking, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "aio", &obj->has_aio)) { ++ if (!visit_type_BlockdevAioOptions(v, "aio", &obj->aio, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "aio-max-batch", &obj->has_aio_max_batch)) { ++ if (!visit_type_int(v, "aio-max-batch", &obj->aio_max_batch, errp)) { ++ return false; ++ } ++ } ++#if defined(CONFIG_LINUX) ++ if (visit_optional(v, "drop-cache", &obj->has_drop_cache)) { ++ if (!visit_type_bool(v, "drop-cache", &obj->drop_cache, errp)) { ++ return false; ++ } ++ } ++#endif /* defined(CONFIG_LINUX) */ ++ if (visit_optional(v, "x-check-cache-dropped", &obj->has_x_check_cache_dropped)) { ++ if (visit_policy_reject(v, "x-check-cache-dropped", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-check-cache-dropped", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_bool(v, "x-check-cache-dropped", &obj->x_check_cache_dropped, errp)) { ++ return false; ++ } ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsFile(Visitor *v, const char *name, ++ BlockdevOptionsFile **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsFile), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsFile_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsFile(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsNull_members(Visitor *v, BlockdevOptionsNull *obj, Error **errp) ++{ ++ if (visit_optional(v, "size", &obj->has_size)) { ++ if (!visit_type_int(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "latency-ns", &obj->has_latency_ns)) { ++ if (!visit_type_uint64(v, "latency-ns", &obj->latency_ns, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "read-zeroes", &obj->has_read_zeroes)) { ++ if (!visit_type_bool(v, "read-zeroes", &obj->read_zeroes, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsNull(Visitor *v, const char *name, ++ BlockdevOptionsNull **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsNull), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsNull_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsNull(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsNVMe_members(Visitor *v, BlockdevOptionsNVMe *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "namespace", &obj->q_namespace, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsNVMe(Visitor *v, const char *name, ++ BlockdevOptionsNVMe **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsNVMe), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsNVMe_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsNVMe(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsVVFAT_members(Visitor *v, BlockdevOptionsVVFAT *obj, Error **errp) ++{ ++ bool has_label = !!obj->label; ++ ++ if (!visit_type_str(v, "dir", &obj->dir, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "fat-type", &obj->has_fat_type)) { ++ if (!visit_type_int(v, "fat-type", &obj->fat_type, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "floppy", &obj->has_floppy)) { ++ if (!visit_type_bool(v, "floppy", &obj->floppy, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "label", &has_label)) { ++ if (!visit_type_str(v, "label", &obj->label, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "rw", &obj->has_rw)) { ++ if (!visit_type_bool(v, "rw", &obj->rw, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsVVFAT(Visitor *v, const char *name, ++ BlockdevOptionsVVFAT **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsVVFAT), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsVVFAT_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsVVFAT(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsGenericFormat_members(Visitor *v, BlockdevOptionsGenericFormat *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsGenericFormat(Visitor *v, const char *name, ++ BlockdevOptionsGenericFormat **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsGenericFormat), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsGenericFormat(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsLUKS_members(Visitor *v, BlockdevOptionsLUKS *obj, Error **errp) ++{ ++ bool has_key_secret = !!obj->key_secret; ++ bool has_header = !!obj->header; ++ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "key-secret", &has_key_secret)) { ++ if (!visit_type_str(v, "key-secret", &obj->key_secret, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "header", &has_header)) { ++ if (!visit_type_BlockdevRef(v, "header", &obj->header, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsLUKS(Visitor *v, const char *name, ++ BlockdevOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsGenericCOWFormat_members(Visitor *v, BlockdevOptionsGenericCOWFormat *obj, Error **errp) ++{ ++ bool has_backing = !!obj->backing; ++ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "backing", &has_backing)) { ++ if (!visit_type_BlockdevRefOrNull(v, "backing", &obj->backing, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsGenericCOWFormat(Visitor *v, const char *name, ++ BlockdevOptionsGenericCOWFormat **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsGenericCOWFormat), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsGenericCOWFormat_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsGenericCOWFormat(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_Qcow2OverlapCheckMode(Visitor *v, const char *name, ++ Qcow2OverlapCheckMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &Qcow2OverlapCheckMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_Qcow2OverlapCheckFlags_members(Visitor *v, Qcow2OverlapCheckFlags *obj, Error **errp) ++{ ++ if (visit_optional(v, "template", &obj->has_q_template)) { ++ if (!visit_type_Qcow2OverlapCheckMode(v, "template", &obj->q_template, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "main-header", &obj->has_main_header)) { ++ if (!visit_type_bool(v, "main-header", &obj->main_header, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "active-l1", &obj->has_active_l1)) { ++ if (!visit_type_bool(v, "active-l1", &obj->active_l1, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "active-l2", &obj->has_active_l2)) { ++ if (!visit_type_bool(v, "active-l2", &obj->active_l2, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "refcount-table", &obj->has_refcount_table)) { ++ if (!visit_type_bool(v, "refcount-table", &obj->refcount_table, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "refcount-block", &obj->has_refcount_block)) { ++ if (!visit_type_bool(v, "refcount-block", &obj->refcount_block, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "snapshot-table", &obj->has_snapshot_table)) { ++ if (!visit_type_bool(v, "snapshot-table", &obj->snapshot_table, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "inactive-l1", &obj->has_inactive_l1)) { ++ if (!visit_type_bool(v, "inactive-l1", &obj->inactive_l1, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "inactive-l2", &obj->has_inactive_l2)) { ++ if (!visit_type_bool(v, "inactive-l2", &obj->inactive_l2, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bitmap-directory", &obj->has_bitmap_directory)) { ++ if (!visit_type_bool(v, "bitmap-directory", &obj->bitmap_directory, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_Qcow2OverlapCheckFlags(Visitor *v, const char *name, ++ Qcow2OverlapCheckFlags **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(Qcow2OverlapCheckFlags), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_Qcow2OverlapCheckFlags_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_Qcow2OverlapCheckFlags(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_Qcow2OverlapChecks(Visitor *v, const char *name, ++ Qcow2OverlapChecks **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_alternate(v, name, (GenericAlternate **)obj, ++ sizeof(**obj), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ switch ((*obj)->type) { ++ case QTYPE_QDICT: ++ if (!visit_start_struct(v, name, NULL, 0, errp)) { ++ break; ++ } ++ if (visit_type_Qcow2OverlapCheckFlags_members(v, &(*obj)->u.flags, errp)) { ++ ok = visit_check_struct(v, errp); ++ } ++ visit_end_struct(v, NULL); ++ break; ++ case QTYPE_QSTRING: ++ ok = visit_type_Qcow2OverlapCheckMode(v, name, &(*obj)->u.mode, errp); ++ break; ++ case QTYPE_NONE: ++ abort(); ++ default: ++ assert(visit_is_input(v)); ++ error_setg(errp, ++ "Invalid parameter type for '%s', expected: Qcow2OverlapChecks", ++ name ? name : "null"); ++ /* Avoid passing invalid *obj to qapi_free_Qcow2OverlapChecks() */ ++ g_free(*obj); ++ *obj = NULL; ++ } ++out_obj: ++ visit_end_alternate(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_Qcow2OverlapChecks(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevQcowEncryptionFormat(Visitor *v, const char *name, ++ BlockdevQcowEncryptionFormat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevQcowEncryptionFormat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockdevQcowEncryption_base_members(Visitor *v, q_obj_BlockdevQcowEncryption_base *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevQcowEncryptionFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevQcowEncryption_members(Visitor *v, BlockdevQcowEncryption *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockdevQcowEncryption_base_members(v, (q_obj_BlockdevQcowEncryption_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case BLOCKDEV_QCOW_ENCRYPTION_FORMAT_AES: ++ return visit_type_QCryptoBlockOptionsQCow_members(v, &obj->u.aes, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevQcowEncryption(Visitor *v, const char *name, ++ BlockdevQcowEncryption **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevQcowEncryption), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevQcowEncryption_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevQcowEncryption(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsQcow_members(Visitor *v, BlockdevOptionsQcow *obj, Error **errp) ++{ ++ bool has_encrypt = !!obj->encrypt; ++ ++ if (!visit_type_BlockdevOptionsGenericCOWFormat_members(v, (BlockdevOptionsGenericCOWFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_BlockdevQcowEncryption(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsQcow(Visitor *v, const char *name, ++ BlockdevOptionsQcow **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsQcow), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsQcow_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsQcow(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevQcow2EncryptionFormat(Visitor *v, const char *name, ++ BlockdevQcow2EncryptionFormat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevQcow2EncryptionFormat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockdevQcow2Encryption_base_members(Visitor *v, q_obj_BlockdevQcow2Encryption_base *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevQcow2EncryptionFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevQcow2Encryption_members(Visitor *v, BlockdevQcow2Encryption *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockdevQcow2Encryption_base_members(v, (q_obj_BlockdevQcow2Encryption_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES: ++ return visit_type_QCryptoBlockOptionsQCow_members(v, &obj->u.aes, errp); ++ case BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS: ++ return visit_type_QCryptoBlockOptionsLUKS_members(v, &obj->u.luks, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevQcow2Encryption(Visitor *v, const char *name, ++ BlockdevQcow2Encryption **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevQcow2Encryption), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevQcow2Encryption_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevQcow2Encryption(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsPreallocate_members(Visitor *v, BlockdevOptionsPreallocate *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "prealloc-align", &obj->has_prealloc_align)) { ++ if (!visit_type_int(v, "prealloc-align", &obj->prealloc_align, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "prealloc-size", &obj->has_prealloc_size)) { ++ if (!visit_type_int(v, "prealloc-size", &obj->prealloc_size, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsPreallocate(Visitor *v, const char *name, ++ BlockdevOptionsPreallocate **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsPreallocate), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsPreallocate_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsPreallocate(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsQcow2_members(Visitor *v, BlockdevOptionsQcow2 *obj, Error **errp) ++{ ++ bool has_overlap_check = !!obj->overlap_check; ++ bool has_encrypt = !!obj->encrypt; ++ bool has_data_file = !!obj->data_file; ++ ++ if (!visit_type_BlockdevOptionsGenericCOWFormat_members(v, (BlockdevOptionsGenericCOWFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "lazy-refcounts", &obj->has_lazy_refcounts)) { ++ if (!visit_type_bool(v, "lazy-refcounts", &obj->lazy_refcounts, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "pass-discard-request", &obj->has_pass_discard_request)) { ++ if (!visit_type_bool(v, "pass-discard-request", &obj->pass_discard_request, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "pass-discard-snapshot", &obj->has_pass_discard_snapshot)) { ++ if (!visit_type_bool(v, "pass-discard-snapshot", &obj->pass_discard_snapshot, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "pass-discard-other", &obj->has_pass_discard_other)) { ++ if (!visit_type_bool(v, "pass-discard-other", &obj->pass_discard_other, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "discard-no-unref", &obj->has_discard_no_unref)) { ++ if (!visit_type_bool(v, "discard-no-unref", &obj->discard_no_unref, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "overlap-check", &has_overlap_check)) { ++ if (!visit_type_Qcow2OverlapChecks(v, "overlap-check", &obj->overlap_check, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cache-size", &obj->has_cache_size)) { ++ if (!visit_type_int(v, "cache-size", &obj->cache_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "l2-cache-size", &obj->has_l2_cache_size)) { ++ if (!visit_type_int(v, "l2-cache-size", &obj->l2_cache_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "l2-cache-entry-size", &obj->has_l2_cache_entry_size)) { ++ if (!visit_type_int(v, "l2-cache-entry-size", &obj->l2_cache_entry_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "refcount-cache-size", &obj->has_refcount_cache_size)) { ++ if (!visit_type_int(v, "refcount-cache-size", &obj->refcount_cache_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cache-clean-interval", &obj->has_cache_clean_interval)) { ++ if (!visit_type_int(v, "cache-clean-interval", &obj->cache_clean_interval, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_BlockdevQcow2Encryption(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "data-file", &has_data_file)) { ++ if (!visit_type_BlockdevRef(v, "data-file", &obj->data_file, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsQcow2(Visitor *v, const char *name, ++ BlockdevOptionsQcow2 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsQcow2), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsQcow2_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsQcow2(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_SshHostKeyCheckMode(Visitor *v, const char *name, ++ SshHostKeyCheckMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &SshHostKeyCheckMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_SshHostKeyCheckHashType(Visitor *v, const char *name, ++ SshHostKeyCheckHashType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &SshHostKeyCheckHashType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_SshHostKeyHash_members(Visitor *v, SshHostKeyHash *obj, Error **errp) ++{ ++ if (!visit_type_SshHostKeyCheckHashType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "hash", &obj->hash, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_SshHostKeyHash(Visitor *v, const char *name, ++ SshHostKeyHash **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SshHostKeyHash), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SshHostKeyHash_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SshHostKeyHash(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_SshHostKeyCheck_base_members(Visitor *v, q_obj_SshHostKeyCheck_base *obj, Error **errp) ++{ ++ if (!visit_type_SshHostKeyCheckMode(v, "mode", &obj->mode, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_SshHostKeyCheck_members(Visitor *v, SshHostKeyCheck *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_SshHostKeyCheck_base_members(v, (q_obj_SshHostKeyCheck_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->mode) { ++ case SSH_HOST_KEY_CHECK_MODE_HASH: ++ return visit_type_SshHostKeyHash_members(v, &obj->u.hash, errp); ++ case SSH_HOST_KEY_CHECK_MODE_NONE: ++ break; ++ case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_SshHostKeyCheck(Visitor *v, const char *name, ++ SshHostKeyCheck **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SshHostKeyCheck), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SshHostKeyCheck_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SshHostKeyCheck(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsSsh_members(Visitor *v, BlockdevOptionsSsh *obj, Error **errp) ++{ ++ bool has_user = !!obj->user; ++ bool has_host_key_check = !!obj->host_key_check; ++ ++ if (!visit_type_InetSocketAddress(v, "server", &obj->server, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "user", &has_user)) { ++ if (!visit_type_str(v, "user", &obj->user, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "host-key-check", &has_host_key_check)) { ++ if (!visit_type_SshHostKeyCheck(v, "host-key-check", &obj->host_key_check, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsSsh(Visitor *v, const char *name, ++ BlockdevOptionsSsh **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsSsh), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsSsh_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsSsh(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlkdebugEvent(Visitor *v, const char *name, ++ BlkdebugEvent *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlkdebugEvent_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlkdebugIOType(Visitor *v, const char *name, ++ BlkdebugIOType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlkdebugIOType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlkdebugInjectErrorOptions_members(Visitor *v, BlkdebugInjectErrorOptions *obj, Error **errp) ++{ ++ if (!visit_type_BlkdebugEvent(v, "event", &obj->event, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "state", &obj->has_state)) { ++ if (!visit_type_int(v, "state", &obj->state, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iotype", &obj->has_iotype)) { ++ if (!visit_type_BlkdebugIOType(v, "iotype", &obj->iotype, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "errno", &obj->has_q_errno)) { ++ if (!visit_type_int(v, "errno", &obj->q_errno, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "sector", &obj->has_sector)) { ++ if (!visit_type_int(v, "sector", &obj->sector, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "once", &obj->has_once)) { ++ if (!visit_type_bool(v, "once", &obj->once, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "immediately", &obj->has_immediately)) { ++ if (!visit_type_bool(v, "immediately", &obj->immediately, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlkdebugInjectErrorOptions(Visitor *v, const char *name, ++ BlkdebugInjectErrorOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlkdebugInjectErrorOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlkdebugInjectErrorOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlkdebugInjectErrorOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlkdebugSetStateOptions_members(Visitor *v, BlkdebugSetStateOptions *obj, Error **errp) ++{ ++ if (!visit_type_BlkdebugEvent(v, "event", &obj->event, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "state", &obj->has_state)) { ++ if (!visit_type_int(v, "state", &obj->state, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "new_state", &obj->new_state, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlkdebugSetStateOptions(Visitor *v, const char *name, ++ BlkdebugSetStateOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlkdebugSetStateOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlkdebugSetStateOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlkdebugSetStateOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlkdebugInjectErrorOptionsList(Visitor *v, const char *name, ++ BlkdebugInjectErrorOptionsList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlkdebugInjectErrorOptionsList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlkdebugInjectErrorOptionsList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlkdebugInjectErrorOptions(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlkdebugInjectErrorOptionsList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlkdebugSetStateOptionsList(Visitor *v, const char *name, ++ BlkdebugSetStateOptionsList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlkdebugSetStateOptionsList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlkdebugSetStateOptionsList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlkdebugSetStateOptions(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlkdebugSetStateOptionsList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsBlkdebug_members(Visitor *v, BlockdevOptionsBlkdebug *obj, Error **errp) ++{ ++ bool has_config = !!obj->config; ++ ++ if (!visit_type_BlockdevRef(v, "image", &obj->image, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "config", &has_config)) { ++ if (!visit_type_str(v, "config", &obj->config, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "align", &obj->has_align)) { ++ if (!visit_type_int(v, "align", &obj->align, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "max-transfer", &obj->has_max_transfer)) { ++ if (!visit_type_int32(v, "max-transfer", &obj->max_transfer, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "opt-write-zero", &obj->has_opt_write_zero)) { ++ if (!visit_type_int32(v, "opt-write-zero", &obj->opt_write_zero, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "max-write-zero", &obj->has_max_write_zero)) { ++ if (!visit_type_int32(v, "max-write-zero", &obj->max_write_zero, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "opt-discard", &obj->has_opt_discard)) { ++ if (!visit_type_int32(v, "opt-discard", &obj->opt_discard, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "max-discard", &obj->has_max_discard)) { ++ if (!visit_type_int32(v, "max-discard", &obj->max_discard, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "inject-error", &obj->has_inject_error)) { ++ if (!visit_type_BlkdebugInjectErrorOptionsList(v, "inject-error", &obj->inject_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "set-state", &obj->has_set_state)) { ++ if (!visit_type_BlkdebugSetStateOptionsList(v, "set-state", &obj->set_state, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "take-child-perms", &obj->has_take_child_perms)) { ++ if (!visit_type_BlockPermissionList(v, "take-child-perms", &obj->take_child_perms, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "unshare-child-perms", &obj->has_unshare_child_perms)) { ++ if (!visit_type_BlockPermissionList(v, "unshare-child-perms", &obj->unshare_child_perms, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsBlkdebug(Visitor *v, const char *name, ++ BlockdevOptionsBlkdebug **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsBlkdebug), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsBlkdebug_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsBlkdebug(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsBlklogwrites_members(Visitor *v, BlockdevOptionsBlklogwrites *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevRef(v, "log", &obj->log, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "log-sector-size", &obj->has_log_sector_size)) { ++ if (!visit_type_uint32(v, "log-sector-size", &obj->log_sector_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "log-append", &obj->has_log_append)) { ++ if (!visit_type_bool(v, "log-append", &obj->log_append, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "log-super-update-interval", &obj->has_log_super_update_interval)) { ++ if (!visit_type_uint64(v, "log-super-update-interval", &obj->log_super_update_interval, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsBlklogwrites(Visitor *v, const char *name, ++ BlockdevOptionsBlklogwrites **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsBlklogwrites), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsBlklogwrites_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsBlklogwrites(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsBlkverify_members(Visitor *v, BlockdevOptionsBlkverify *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "test", &obj->test, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevRef(v, "raw", &obj->raw, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsBlkverify(Visitor *v, const char *name, ++ BlockdevOptionsBlkverify **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsBlkverify), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsBlkverify_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsBlkverify(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsBlkreplay_members(Visitor *v, BlockdevOptionsBlkreplay *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "image", &obj->image, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsBlkreplay(Visitor *v, const char *name, ++ BlockdevOptionsBlkreplay **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsBlkreplay), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsBlkreplay_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsBlkreplay(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QuorumReadPattern(Visitor *v, const char *name, ++ QuorumReadPattern *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QuorumReadPattern_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevRefList(Visitor *v, const char *name, ++ BlockdevRefList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockdevRefList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockdevRefList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockdevRef(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevRefList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsQuorum_members(Visitor *v, BlockdevOptionsQuorum *obj, Error **errp) ++{ ++ if (visit_optional(v, "blkverify", &obj->has_blkverify)) { ++ if (!visit_type_bool(v, "blkverify", &obj->blkverify, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_BlockdevRefList(v, "children", &obj->children, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "vote-threshold", &obj->vote_threshold, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "rewrite-corrupted", &obj->has_rewrite_corrupted)) { ++ if (!visit_type_bool(v, "rewrite-corrupted", &obj->rewrite_corrupted, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "read-pattern", &obj->has_read_pattern)) { ++ if (!visit_type_QuorumReadPattern(v, "read-pattern", &obj->read_pattern, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsQuorum(Visitor *v, const char *name, ++ BlockdevOptionsQuorum **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsQuorum), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsQuorum_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsQuorum(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsGluster_members(Visitor *v, BlockdevOptionsGluster *obj, Error **errp) ++{ ++ bool has_logfile = !!obj->logfile; ++ ++ if (!visit_type_str(v, "volume", &obj->volume, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ if (!visit_type_SocketAddressList(v, "server", &obj->server, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "debug", &obj->has_debug)) { ++ if (!visit_type_int(v, "debug", &obj->debug, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "logfile", &has_logfile)) { ++ if (!visit_type_str(v, "logfile", &obj->logfile, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsGluster(Visitor *v, const char *name, ++ BlockdevOptionsGluster **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsGluster), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsGluster_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsGluster(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsIoUring_members(Visitor *v, BlockdevOptionsIoUring *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsIoUring(Visitor *v, const char *name, ++ BlockdevOptionsIoUring **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsIoUring), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsIoUring_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsIoUring(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsNvmeIoUring_members(Visitor *v, BlockdevOptionsNvmeIoUring *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsNvmeIoUring(Visitor *v, const char *name, ++ BlockdevOptionsNvmeIoUring **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsNvmeIoUring), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsNvmeIoUring_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsNvmeIoUring(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsVirtioBlkVfioPci_members(Visitor *v, BlockdevOptionsVirtioBlkVfioPci *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsVirtioBlkVfioPci(Visitor *v, const char *name, ++ BlockdevOptionsVirtioBlkVfioPci **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsVirtioBlkVfioPci), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsVirtioBlkVfioPci_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsVirtioBlkVfioPci(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsVirtioBlkVhostUser_members(Visitor *v, BlockdevOptionsVirtioBlkVhostUser *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsVirtioBlkVhostUser(Visitor *v, const char *name, ++ BlockdevOptionsVirtioBlkVhostUser **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsVirtioBlkVhostUser), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsVirtioBlkVhostUser_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsVirtioBlkVhostUser(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsVirtioBlkVhostVdpa_members(Visitor *v, BlockdevOptionsVirtioBlkVhostVdpa *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsVirtioBlkVhostVdpa(Visitor *v, const char *name, ++ BlockdevOptionsVirtioBlkVhostVdpa **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsVirtioBlkVhostVdpa), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsVirtioBlkVhostVdpa_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsVirtioBlkVhostVdpa(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++bool visit_type_IscsiTransport(Visitor *v, const char *name, ++ IscsiTransport *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &IscsiTransport_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_IscsiHeaderDigest(Visitor *v, const char *name, ++ IscsiHeaderDigest *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &IscsiHeaderDigest_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsIscsi_members(Visitor *v, BlockdevOptionsIscsi *obj, Error **errp) ++{ ++ bool has_user = !!obj->user; ++ bool has_password_secret = !!obj->password_secret; ++ bool has_initiator_name = !!obj->initiator_name; ++ ++ if (!visit_type_IscsiTransport(v, "transport", &obj->transport, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "portal", &obj->portal, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "lun", &obj->has_lun)) { ++ if (!visit_type_int(v, "lun", &obj->lun, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "user", &has_user)) { ++ if (!visit_type_str(v, "user", &obj->user, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "password-secret", &has_password_secret)) { ++ if (!visit_type_str(v, "password-secret", &obj->password_secret, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "initiator-name", &has_initiator_name)) { ++ if (!visit_type_str(v, "initiator-name", &obj->initiator_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "header-digest", &obj->has_header_digest)) { ++ if (!visit_type_IscsiHeaderDigest(v, "header-digest", &obj->header_digest, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "timeout", &obj->has_timeout)) { ++ if (!visit_type_int(v, "timeout", &obj->timeout, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsIscsi(Visitor *v, const char *name, ++ BlockdevOptionsIscsi **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsIscsi), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsIscsi_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsIscsi(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdAuthMode(Visitor *v, const char *name, ++ RbdAuthMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &RbdAuthMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_RbdImageEncryptionFormat(Visitor *v, const char *name, ++ RbdImageEncryptionFormat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &RbdImageEncryptionFormat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKSBase_members(Visitor *v, RbdEncryptionOptionsLUKSBase *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "key-secret", &obj->key_secret, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKSBase(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKSBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionOptionsLUKSBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionOptionsLUKSBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionOptionsLUKSBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKSBase_members(Visitor *v, RbdEncryptionCreateOptionsLUKSBase *obj, Error **errp) ++{ ++ if (!visit_type_RbdEncryptionOptionsLUKSBase_members(v, (RbdEncryptionOptionsLUKSBase *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cipher-alg", &obj->has_cipher_alg)) { ++ if (!visit_type_QCryptoCipherAlgorithm(v, "cipher-alg", &obj->cipher_alg, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKSBase(Visitor *v, const char *name, ++ RbdEncryptionCreateOptionsLUKSBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionCreateOptionsLUKSBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionCreateOptionsLUKSBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionCreateOptionsLUKSBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKS_members(Visitor *v, RbdEncryptionOptionsLUKS *obj, Error **errp) ++{ ++ if (!visit_type_RbdEncryptionOptionsLUKSBase_members(v, (RbdEncryptionOptionsLUKSBase *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKS(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKS2_members(Visitor *v, RbdEncryptionOptionsLUKS2 *obj, Error **errp) ++{ ++ if (!visit_type_RbdEncryptionOptionsLUKSBase_members(v, (RbdEncryptionOptionsLUKSBase *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKS2(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKS2 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionOptionsLUKS2), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionOptionsLUKS2_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionOptionsLUKS2(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKSAny_members(Visitor *v, RbdEncryptionOptionsLUKSAny *obj, Error **errp) ++{ ++ if (!visit_type_RbdEncryptionOptionsLUKSBase_members(v, (RbdEncryptionOptionsLUKSBase *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKSAny(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKSAny **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionOptionsLUKSAny), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionOptionsLUKSAny_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionOptionsLUKSAny(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS_members(Visitor *v, RbdEncryptionCreateOptionsLUKS *obj, Error **errp) ++{ ++ if (!visit_type_RbdEncryptionCreateOptionsLUKSBase_members(v, (RbdEncryptionCreateOptionsLUKSBase *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS(Visitor *v, const char *name, ++ RbdEncryptionCreateOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionCreateOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionCreateOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionCreateOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS2_members(Visitor *v, RbdEncryptionCreateOptionsLUKS2 *obj, Error **errp) ++{ ++ if (!visit_type_RbdEncryptionCreateOptionsLUKSBase_members(v, (RbdEncryptionCreateOptionsLUKSBase *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS2(Visitor *v, const char *name, ++ RbdEncryptionCreateOptionsLUKS2 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionCreateOptionsLUKS2), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionCreateOptionsLUKS2_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionCreateOptionsLUKS2(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_RbdEncryptionOptions_base_members(Visitor *v, q_obj_RbdEncryptionOptions_base *obj, Error **errp) ++{ ++ bool has_parent = !!obj->parent; ++ ++ if (!visit_type_RbdImageEncryptionFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "parent", &has_parent)) { ++ if (!visit_type_RbdEncryptionOptions(v, "parent", &obj->parent, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionOptions_members(Visitor *v, RbdEncryptionOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_RbdEncryptionOptions_base_members(v, (q_obj_RbdEncryptionOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS: ++ return visit_type_RbdEncryptionOptionsLUKS_members(v, &obj->u.luks, errp); ++ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2: ++ return visit_type_RbdEncryptionOptionsLUKS2_members(v, &obj->u.luks2, errp); ++ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY: ++ return visit_type_RbdEncryptionOptionsLUKSAny_members(v, &obj->u.luks_any, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionOptions(Visitor *v, const char *name, ++ RbdEncryptionOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_RbdEncryptionCreateOptions_base_members(Visitor *v, q_obj_RbdEncryptionCreateOptions_base *obj, Error **errp) ++{ ++ if (!visit_type_RbdImageEncryptionFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionCreateOptions_members(Visitor *v, RbdEncryptionCreateOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_RbdEncryptionCreateOptions_base_members(v, (q_obj_RbdEncryptionCreateOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS: ++ return visit_type_RbdEncryptionCreateOptionsLUKS_members(v, &obj->u.luks, errp); ++ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2: ++ return visit_type_RbdEncryptionCreateOptionsLUKS2_members(v, &obj->u.luks2, errp); ++ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionCreateOptions(Visitor *v, const char *name, ++ RbdEncryptionCreateOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionCreateOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionCreateOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionCreateOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdAuthModeList(Visitor *v, const char *name, ++ RbdAuthModeList **obj, Error **errp) ++{ ++ bool ok = false; ++ RbdAuthModeList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (RbdAuthModeList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_RbdAuthMode(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdAuthModeList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsRbd_members(Visitor *v, BlockdevOptionsRbd *obj, Error **errp) ++{ ++ bool has_q_namespace = !!obj->q_namespace; ++ bool has_conf = !!obj->conf; ++ bool has_snapshot = !!obj->snapshot; ++ bool has_encrypt = !!obj->encrypt; ++ bool has_user = !!obj->user; ++ bool has_key_secret = !!obj->key_secret; ++ ++ if (!visit_type_str(v, "pool", &obj->pool, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "namespace", &has_q_namespace)) { ++ if (!visit_type_str(v, "namespace", &obj->q_namespace, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "image", &obj->image, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "conf", &has_conf)) { ++ if (!visit_type_str(v, "conf", &obj->conf, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "snapshot", &has_snapshot)) { ++ if (!visit_type_str(v, "snapshot", &obj->snapshot, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_RbdEncryptionOptions(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "user", &has_user)) { ++ if (!visit_type_str(v, "user", &obj->user, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auth-client-required", &obj->has_auth_client_required)) { ++ if (!visit_type_RbdAuthModeList(v, "auth-client-required", &obj->auth_client_required, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "key-secret", &has_key_secret)) { ++ if (!visit_type_str(v, "key-secret", &obj->key_secret, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "server", &obj->has_server)) { ++ if (!visit_type_InetSocketAddressBaseList(v, "server", &obj->server, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsRbd(Visitor *v, const char *name, ++ BlockdevOptionsRbd **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsRbd), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsRbd_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsRbd(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++#if defined(CONFIG_REPLICATION) ++bool visit_type_ReplicationMode(Visitor *v, const char *name, ++ ReplicationMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &ReplicationMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++#if defined(CONFIG_REPLICATION) ++bool visit_type_BlockdevOptionsReplication_members(Visitor *v, BlockdevOptionsReplication *obj, Error **errp) ++{ ++ bool has_top_id = !!obj->top_id; ++ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (!visit_type_ReplicationMode(v, "mode", &obj->mode, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "top-id", &has_top_id)) { ++ if (!visit_type_str(v, "top-id", &obj->top_id, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsReplication(Visitor *v, const char *name, ++ BlockdevOptionsReplication **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsReplication), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsReplication_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsReplication(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++bool visit_type_NFSTransport(Visitor *v, const char *name, ++ NFSTransport *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &NFSTransport_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_NFSServer_members(Visitor *v, NFSServer *obj, Error **errp) ++{ ++ if (!visit_type_NFSTransport(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "host", &obj->host, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_NFSServer(Visitor *v, const char *name, ++ NFSServer **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(NFSServer), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_NFSServer_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_NFSServer(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsNfs_members(Visitor *v, BlockdevOptionsNfs *obj, Error **errp) ++{ ++ if (!visit_type_NFSServer(v, "server", &obj->server, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "user", &obj->has_user)) { ++ if (!visit_type_int(v, "user", &obj->user, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "group", &obj->has_group)) { ++ if (!visit_type_int(v, "group", &obj->group, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "tcp-syn-count", &obj->has_tcp_syn_count)) { ++ if (!visit_type_int(v, "tcp-syn-count", &obj->tcp_syn_count, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "readahead-size", &obj->has_readahead_size)) { ++ if (!visit_type_int(v, "readahead-size", &obj->readahead_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "page-cache-size", &obj->has_page_cache_size)) { ++ if (!visit_type_int(v, "page-cache-size", &obj->page_cache_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "debug", &obj->has_debug)) { ++ if (!visit_type_int(v, "debug", &obj->debug, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsNfs(Visitor *v, const char *name, ++ BlockdevOptionsNfs **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsNfs), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsNfs_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsNfs(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCurlBase_members(Visitor *v, BlockdevOptionsCurlBase *obj, Error **errp) ++{ ++ bool has_username = !!obj->username; ++ bool has_password_secret = !!obj->password_secret; ++ bool has_proxy_username = !!obj->proxy_username; ++ bool has_proxy_password_secret = !!obj->proxy_password_secret; ++ ++ if (!visit_type_str(v, "url", &obj->url, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "readahead", &obj->has_readahead)) { ++ if (!visit_type_int(v, "readahead", &obj->readahead, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "timeout", &obj->has_timeout)) { ++ if (!visit_type_int(v, "timeout", &obj->timeout, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "username", &has_username)) { ++ if (!visit_type_str(v, "username", &obj->username, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "password-secret", &has_password_secret)) { ++ if (!visit_type_str(v, "password-secret", &obj->password_secret, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "proxy-username", &has_proxy_username)) { ++ if (!visit_type_str(v, "proxy-username", &obj->proxy_username, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "proxy-password-secret", &has_proxy_password_secret)) { ++ if (!visit_type_str(v, "proxy-password-secret", &obj->proxy_password_secret, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCurlBase(Visitor *v, const char *name, ++ BlockdevOptionsCurlBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCurlBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCurlBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCurlBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCurlHttp_members(Visitor *v, BlockdevOptionsCurlHttp *obj, Error **errp) ++{ ++ bool has_cookie = !!obj->cookie; ++ bool has_cookie_secret = !!obj->cookie_secret; ++ ++ if (!visit_type_BlockdevOptionsCurlBase_members(v, (BlockdevOptionsCurlBase *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cookie", &has_cookie)) { ++ if (!visit_type_str(v, "cookie", &obj->cookie, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cookie-secret", &has_cookie_secret)) { ++ if (!visit_type_str(v, "cookie-secret", &obj->cookie_secret, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCurlHttp(Visitor *v, const char *name, ++ BlockdevOptionsCurlHttp **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCurlHttp), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCurlHttp_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCurlHttp(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCurlHttps_members(Visitor *v, BlockdevOptionsCurlHttps *obj, Error **errp) ++{ ++ bool has_cookie = !!obj->cookie; ++ bool has_cookie_secret = !!obj->cookie_secret; ++ ++ if (!visit_type_BlockdevOptionsCurlBase_members(v, (BlockdevOptionsCurlBase *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cookie", &has_cookie)) { ++ if (!visit_type_str(v, "cookie", &obj->cookie, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "sslverify", &obj->has_sslverify)) { ++ if (!visit_type_bool(v, "sslverify", &obj->sslverify, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cookie-secret", &has_cookie_secret)) { ++ if (!visit_type_str(v, "cookie-secret", &obj->cookie_secret, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCurlHttps(Visitor *v, const char *name, ++ BlockdevOptionsCurlHttps **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCurlHttps), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCurlHttps_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCurlHttps(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCurlFtp_members(Visitor *v, BlockdevOptionsCurlFtp *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsCurlBase_members(v, (BlockdevOptionsCurlBase *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCurlFtp(Visitor *v, const char *name, ++ BlockdevOptionsCurlFtp **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCurlFtp), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCurlFtp_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCurlFtp(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCurlFtps_members(Visitor *v, BlockdevOptionsCurlFtps *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsCurlBase_members(v, (BlockdevOptionsCurlBase *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "sslverify", &obj->has_sslverify)) { ++ if (!visit_type_bool(v, "sslverify", &obj->sslverify, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCurlFtps(Visitor *v, const char *name, ++ BlockdevOptionsCurlFtps **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCurlFtps), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCurlFtps_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCurlFtps(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsNbd_members(Visitor *v, BlockdevOptionsNbd *obj, Error **errp) ++{ ++ bool has_export = !!obj->export; ++ bool has_tls_creds = !!obj->tls_creds; ++ bool has_tls_hostname = !!obj->tls_hostname; ++ bool has_x_dirty_bitmap = !!obj->x_dirty_bitmap; ++ ++ if (!visit_type_SocketAddress(v, "server", &obj->server, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "export", &has_export)) { ++ if (!visit_type_str(v, "export", &obj->export, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "tls-creds", &has_tls_creds)) { ++ if (!visit_type_str(v, "tls-creds", &obj->tls_creds, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "tls-hostname", &has_tls_hostname)) { ++ if (!visit_type_str(v, "tls-hostname", &obj->tls_hostname, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "x-dirty-bitmap", &has_x_dirty_bitmap)) { ++ if (visit_policy_reject(v, "x-dirty-bitmap", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-dirty-bitmap", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_str(v, "x-dirty-bitmap", &obj->x_dirty_bitmap, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "reconnect-delay", &obj->has_reconnect_delay)) { ++ if (!visit_type_uint32(v, "reconnect-delay", &obj->reconnect_delay, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "open-timeout", &obj->has_open_timeout)) { ++ if (!visit_type_uint32(v, "open-timeout", &obj->open_timeout, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsNbd(Visitor *v, const char *name, ++ BlockdevOptionsNbd **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsNbd), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsNbd_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsNbd(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsRaw_members(Visitor *v, BlockdevOptionsRaw *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "offset", &obj->has_offset)) { ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "size", &obj->has_size)) { ++ if (!visit_type_int(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsRaw(Visitor *v, const char *name, ++ BlockdevOptionsRaw **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsRaw), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsRaw_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsRaw(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsThrottle_members(Visitor *v, BlockdevOptionsThrottle *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "throttle-group", &obj->throttle_group, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsThrottle(Visitor *v, const char *name, ++ BlockdevOptionsThrottle **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsThrottle), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsThrottle_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsThrottle(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCor_members(Visitor *v, BlockdevOptionsCor *obj, Error **errp) ++{ ++ bool has_bottom = !!obj->bottom; ++ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "bottom", &has_bottom)) { ++ if (!visit_type_str(v, "bottom", &obj->bottom, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCor(Visitor *v, const char *name, ++ BlockdevOptionsCor **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCor), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCor_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCor(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_OnCbwError(Visitor *v, const char *name, ++ OnCbwError *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &OnCbwError_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCbw_members(Visitor *v, BlockdevOptionsCbw *obj, Error **errp) ++{ ++ bool has_bitmap = !!obj->bitmap; ++ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevRef(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "bitmap", &has_bitmap)) { ++ if (!visit_type_BlockDirtyBitmap(v, "bitmap", &obj->bitmap, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-cbw-error", &obj->has_on_cbw_error)) { ++ if (!visit_type_OnCbwError(v, "on-cbw-error", &obj->on_cbw_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cbw-timeout", &obj->has_cbw_timeout)) { ++ if (!visit_type_uint32(v, "cbw-timeout", &obj->cbw_timeout, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCbw(Visitor *v, const char *name, ++ BlockdevOptionsCbw **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCbw), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCbw_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCbw(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockdevOptions_base_members(Visitor *v, q_obj_BlockdevOptions_base *obj, Error **errp) ++{ ++ bool has_node_name = !!obj->node_name; ++ bool has_cache = !!obj->cache; ++ ++ if (!visit_type_BlockdevDriver(v, "driver", &obj->driver, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "discard", &obj->has_discard)) { ++ if (!visit_type_BlockdevDiscardOptions(v, "discard", &obj->discard, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cache", &has_cache)) { ++ if (!visit_type_BlockdevCacheOptions(v, "cache", &obj->cache, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "read-only", &obj->has_read_only)) { ++ if (!visit_type_bool(v, "read-only", &obj->read_only, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-read-only", &obj->has_auto_read_only)) { ++ if (!visit_type_bool(v, "auto-read-only", &obj->auto_read_only, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "force-share", &obj->has_force_share)) { ++ if (!visit_type_bool(v, "force-share", &obj->force_share, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "detect-zeroes", &obj->has_detect_zeroes)) { ++ if (!visit_type_BlockdevDetectZeroesOptions(v, "detect-zeroes", &obj->detect_zeroes, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptions_members(Visitor *v, BlockdevOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockdevOptions_base_members(v, (q_obj_BlockdevOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->driver) { ++ case BLOCKDEV_DRIVER_BLKDEBUG: ++ return visit_type_BlockdevOptionsBlkdebug_members(v, &obj->u.blkdebug, errp); ++ case BLOCKDEV_DRIVER_BLKLOGWRITES: ++ return visit_type_BlockdevOptionsBlklogwrites_members(v, &obj->u.blklogwrites, errp); ++ case BLOCKDEV_DRIVER_BLKVERIFY: ++ return visit_type_BlockdevOptionsBlkverify_members(v, &obj->u.blkverify, errp); ++ case BLOCKDEV_DRIVER_BLKREPLAY: ++ return visit_type_BlockdevOptionsBlkreplay_members(v, &obj->u.blkreplay, errp); ++ case BLOCKDEV_DRIVER_BOCHS: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.bochs, errp); ++ case BLOCKDEV_DRIVER_CLOOP: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.cloop, errp); ++ case BLOCKDEV_DRIVER_COMPRESS: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.compress, errp); ++ case BLOCKDEV_DRIVER_COPY_BEFORE_WRITE: ++ return visit_type_BlockdevOptionsCbw_members(v, &obj->u.copy_before_write, errp); ++ case BLOCKDEV_DRIVER_COPY_ON_READ: ++ return visit_type_BlockdevOptionsCor_members(v, &obj->u.copy_on_read, errp); ++ case BLOCKDEV_DRIVER_DMG: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.dmg, errp); ++ case BLOCKDEV_DRIVER_FILE: ++ return visit_type_BlockdevOptionsFile_members(v, &obj->u.file, errp); ++ case BLOCKDEV_DRIVER_FTP: ++ return visit_type_BlockdevOptionsCurlFtp_members(v, &obj->u.ftp, errp); ++ case BLOCKDEV_DRIVER_FTPS: ++ return visit_type_BlockdevOptionsCurlFtps_members(v, &obj->u.ftps, errp); ++ case BLOCKDEV_DRIVER_GLUSTER: ++ return visit_type_BlockdevOptionsGluster_members(v, &obj->u.gluster, errp); ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_CDROM: ++ return visit_type_BlockdevOptionsFile_members(v, &obj->u.host_cdrom, errp); ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_DEVICE: ++ return visit_type_BlockdevOptionsFile_members(v, &obj->u.host_device, errp); ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ case BLOCKDEV_DRIVER_HTTP: ++ return visit_type_BlockdevOptionsCurlHttp_members(v, &obj->u.http, errp); ++ case BLOCKDEV_DRIVER_HTTPS: ++ return visit_type_BlockdevOptionsCurlHttps_members(v, &obj->u.https, errp); ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_IO_URING: ++ return visit_type_BlockdevOptionsIoUring_members(v, &obj->u.io_uring, errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_ISCSI: ++ return visit_type_BlockdevOptionsIscsi_members(v, &obj->u.iscsi, errp); ++ case BLOCKDEV_DRIVER_LUKS: ++ return visit_type_BlockdevOptionsLUKS_members(v, &obj->u.luks, errp); ++ case BLOCKDEV_DRIVER_NBD: ++ return visit_type_BlockdevOptionsNbd_members(v, &obj->u.nbd, errp); ++ case BLOCKDEV_DRIVER_NFS: ++ return visit_type_BlockdevOptionsNfs_members(v, &obj->u.nfs, errp); ++ case BLOCKDEV_DRIVER_NULL_AIO: ++ return visit_type_BlockdevOptionsNull_members(v, &obj->u.null_aio, errp); ++ case BLOCKDEV_DRIVER_NULL_CO: ++ return visit_type_BlockdevOptionsNull_members(v, &obj->u.null_co, errp); ++ case BLOCKDEV_DRIVER_NVME: ++ return visit_type_BlockdevOptionsNVMe_members(v, &obj->u.nvme, errp); ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_NVME_IO_URING: ++ return visit_type_BlockdevOptionsNvmeIoUring_members(v, &obj->u.nvme_io_uring, errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_PARALLELS: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.parallels, errp); ++ case BLOCKDEV_DRIVER_PREALLOCATE: ++ return visit_type_BlockdevOptionsPreallocate_members(v, &obj->u.preallocate, errp); ++ case BLOCKDEV_DRIVER_QCOW2: ++ return visit_type_BlockdevOptionsQcow2_members(v, &obj->u.qcow2, errp); ++ case BLOCKDEV_DRIVER_QCOW: ++ return visit_type_BlockdevOptionsQcow_members(v, &obj->u.qcow, errp); ++ case BLOCKDEV_DRIVER_QED: ++ return visit_type_BlockdevOptionsGenericCOWFormat_members(v, &obj->u.qed, errp); ++ case BLOCKDEV_DRIVER_QUORUM: ++ return visit_type_BlockdevOptionsQuorum_members(v, &obj->u.quorum, errp); ++ case BLOCKDEV_DRIVER_RAW: ++ return visit_type_BlockdevOptionsRaw_members(v, &obj->u.raw, errp); ++ case BLOCKDEV_DRIVER_RBD: ++ return visit_type_BlockdevOptionsRbd_members(v, &obj->u.rbd, errp); ++#if defined(CONFIG_REPLICATION) ++ case BLOCKDEV_DRIVER_REPLICATION: ++ return visit_type_BlockdevOptionsReplication_members(v, &obj->u.replication, errp); ++#endif /* defined(CONFIG_REPLICATION) */ ++ case BLOCKDEV_DRIVER_SNAPSHOT_ACCESS: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.snapshot_access, errp); ++ case BLOCKDEV_DRIVER_SSH: ++ return visit_type_BlockdevOptionsSsh_members(v, &obj->u.ssh, errp); ++ case BLOCKDEV_DRIVER_THROTTLE: ++ return visit_type_BlockdevOptionsThrottle_members(v, &obj->u.throttle, errp); ++ case BLOCKDEV_DRIVER_VDI: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.vdi, errp); ++ case BLOCKDEV_DRIVER_VHDX: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.vhdx, errp); ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VFIO_PCI: ++ return visit_type_BlockdevOptionsVirtioBlkVfioPci_members(v, &obj->u.virtio_blk_vfio_pci, errp); ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_USER: ++ return visit_type_BlockdevOptionsVirtioBlkVhostUser_members(v, &obj->u.virtio_blk_vhost_user, errp); ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_VDPA: ++ return visit_type_BlockdevOptionsVirtioBlkVhostVdpa_members(v, &obj->u.virtio_blk_vhost_vdpa, errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_VMDK: ++ return visit_type_BlockdevOptionsGenericCOWFormat_members(v, &obj->u.vmdk, errp); ++ case BLOCKDEV_DRIVER_VPC: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.vpc, errp); ++ case BLOCKDEV_DRIVER_VVFAT: ++ return visit_type_BlockdevOptionsVVFAT_members(v, &obj->u.vvfat, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptions(Visitor *v, const char *name, ++ BlockdevOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevRef(Visitor *v, const char *name, ++ BlockdevRef **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_alternate(v, name, (GenericAlternate **)obj, ++ sizeof(**obj), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ switch ((*obj)->type) { ++ case QTYPE_QDICT: ++ if (!visit_start_struct(v, name, NULL, 0, errp)) { ++ break; ++ } ++ if (visit_type_BlockdevOptions_members(v, &(*obj)->u.definition, errp)) { ++ ok = visit_check_struct(v, errp); ++ } ++ visit_end_struct(v, NULL); ++ break; ++ case QTYPE_QSTRING: ++ ok = visit_type_str(v, name, &(*obj)->u.reference, errp); ++ break; ++ case QTYPE_NONE: ++ abort(); ++ default: ++ assert(visit_is_input(v)); ++ error_setg(errp, ++ "Invalid parameter type for '%s', expected: BlockdevRef", ++ name ? name : "null"); ++ /* Avoid passing invalid *obj to qapi_free_BlockdevRef() */ ++ g_free(*obj); ++ *obj = NULL; ++ } ++out_obj: ++ visit_end_alternate(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevRef(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevRefOrNull(Visitor *v, const char *name, ++ BlockdevRefOrNull **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_alternate(v, name, (GenericAlternate **)obj, ++ sizeof(**obj), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ switch ((*obj)->type) { ++ case QTYPE_QDICT: ++ if (!visit_start_struct(v, name, NULL, 0, errp)) { ++ break; ++ } ++ if (visit_type_BlockdevOptions_members(v, &(*obj)->u.definition, errp)) { ++ ok = visit_check_struct(v, errp); ++ } ++ visit_end_struct(v, NULL); ++ break; ++ case QTYPE_QSTRING: ++ ok = visit_type_str(v, name, &(*obj)->u.reference, errp); ++ break; ++ case QTYPE_QNULL: ++ ok = visit_type_null(v, name, &(*obj)->u.null, errp); ++ break; ++ case QTYPE_NONE: ++ abort(); ++ default: ++ assert(visit_is_input(v)); ++ error_setg(errp, ++ "Invalid parameter type for '%s', expected: BlockdevRefOrNull", ++ name ? name : "null"); ++ /* Avoid passing invalid *obj to qapi_free_BlockdevRefOrNull() */ ++ g_free(*obj); ++ *obj = NULL; ++ } ++out_obj: ++ visit_end_alternate(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevRefOrNull(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsList(Visitor *v, const char *name, ++ BlockdevOptionsList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockdevOptionsList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockdevOptionsList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockdevOptions(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_blockdev_reopen_arg_members(Visitor *v, q_obj_blockdev_reopen_arg *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsList(v, "options", &obj->options, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_blockdev_del_arg_members(Visitor *v, q_obj_blockdev_del_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsFile_members(Visitor *v, BlockdevCreateOptionsFile *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "preallocation", &obj->has_preallocation)) { ++ if (!visit_type_PreallocMode(v, "preallocation", &obj->preallocation, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "nocow", &obj->has_nocow)) { ++ if (!visit_type_bool(v, "nocow", &obj->nocow, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "extent-size-hint", &obj->has_extent_size_hint)) { ++ if (!visit_type_size(v, "extent-size-hint", &obj->extent_size_hint, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsFile(Visitor *v, const char *name, ++ BlockdevCreateOptionsFile **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsFile), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsFile_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsFile(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsGluster_members(Visitor *v, BlockdevCreateOptionsGluster *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsGluster(v, "location", &obj->location, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "preallocation", &obj->has_preallocation)) { ++ if (!visit_type_PreallocMode(v, "preallocation", &obj->preallocation, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsGluster(Visitor *v, const char *name, ++ BlockdevCreateOptionsGluster **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsGluster), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsGluster_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsGluster(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsLUKS_members(Visitor *v, BlockdevCreateOptionsLUKS *obj, Error **errp) ++{ ++ bool has_file = !!obj->file; ++ bool has_header = !!obj->header; ++ ++ if (!visit_type_QCryptoBlockCreateOptionsLUKS_members(v, (QCryptoBlockCreateOptionsLUKS *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "file", &has_file)) { ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "header", &has_header)) { ++ if (!visit_type_BlockdevRef(v, "header", &obj->header, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "preallocation", &obj->has_preallocation)) { ++ if (!visit_type_PreallocMode(v, "preallocation", &obj->preallocation, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsLUKS(Visitor *v, const char *name, ++ BlockdevCreateOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsNfs_members(Visitor *v, BlockdevCreateOptionsNfs *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsNfs(v, "location", &obj->location, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsNfs(Visitor *v, const char *name, ++ BlockdevCreateOptionsNfs **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsNfs), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsNfs_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsNfs(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsParallels_members(Visitor *v, BlockdevCreateOptionsParallels *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { ++ if (!visit_type_size(v, "cluster-size", &obj->cluster_size, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsParallels(Visitor *v, const char *name, ++ BlockdevCreateOptionsParallels **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsParallels), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsParallels_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsParallels(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsQcow_members(Visitor *v, BlockdevCreateOptionsQcow *obj, Error **errp) ++{ ++ bool has_backing_file = !!obj->backing_file; ++ bool has_encrypt = !!obj->encrypt; ++ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "backing-file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_QCryptoBlockCreateOptions(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsQcow(Visitor *v, const char *name, ++ BlockdevCreateOptionsQcow **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsQcow), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsQcow_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsQcow(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevQcow2Version(Visitor *v, const char *name, ++ BlockdevQcow2Version *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevQcow2Version_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_Qcow2CompressionType(Visitor *v, const char *name, ++ Qcow2CompressionType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &Qcow2CompressionType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsQcow2_members(Visitor *v, BlockdevCreateOptionsQcow2 *obj, Error **errp) ++{ ++ bool has_data_file = !!obj->data_file; ++ bool has_backing_file = !!obj->backing_file; ++ bool has_encrypt = !!obj->encrypt; ++ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "data-file", &has_data_file)) { ++ if (!visit_type_BlockdevRef(v, "data-file", &obj->data_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "data-file-raw", &obj->has_data_file_raw)) { ++ if (!visit_type_bool(v, "data-file-raw", &obj->data_file_raw, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "extended-l2", &obj->has_extended_l2)) { ++ if (!visit_type_bool(v, "extended-l2", &obj->extended_l2, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "version", &obj->has_version)) { ++ if (!visit_type_BlockdevQcow2Version(v, "version", &obj->version, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-fmt", &obj->has_backing_fmt)) { ++ if (!visit_type_BlockdevDriver(v, "backing-fmt", &obj->backing_fmt, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_QCryptoBlockCreateOptions(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { ++ if (!visit_type_size(v, "cluster-size", &obj->cluster_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "preallocation", &obj->has_preallocation)) { ++ if (!visit_type_PreallocMode(v, "preallocation", &obj->preallocation, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "lazy-refcounts", &obj->has_lazy_refcounts)) { ++ if (!visit_type_bool(v, "lazy-refcounts", &obj->lazy_refcounts, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "refcount-bits", &obj->has_refcount_bits)) { ++ if (!visit_type_int(v, "refcount-bits", &obj->refcount_bits, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "compression-type", &obj->has_compression_type)) { ++ if (!visit_type_Qcow2CompressionType(v, "compression-type", &obj->compression_type, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsQcow2(Visitor *v, const char *name, ++ BlockdevCreateOptionsQcow2 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsQcow2), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsQcow2_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsQcow2(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsQed_members(Visitor *v, BlockdevCreateOptionsQed *obj, Error **errp) ++{ ++ bool has_backing_file = !!obj->backing_file; ++ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "backing-file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-fmt", &obj->has_backing_fmt)) { ++ if (!visit_type_BlockdevDriver(v, "backing-fmt", &obj->backing_fmt, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { ++ if (!visit_type_size(v, "cluster-size", &obj->cluster_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "table-size", &obj->has_table_size)) { ++ if (!visit_type_int(v, "table-size", &obj->table_size, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsQed(Visitor *v, const char *name, ++ BlockdevCreateOptionsQed **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsQed), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsQed_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsQed(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsRbd_members(Visitor *v, BlockdevCreateOptionsRbd *obj, Error **errp) ++{ ++ bool has_encrypt = !!obj->encrypt; ++ ++ if (!visit_type_BlockdevOptionsRbd(v, "location", &obj->location, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { ++ if (!visit_type_size(v, "cluster-size", &obj->cluster_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_RbdEncryptionCreateOptions(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsRbd(Visitor *v, const char *name, ++ BlockdevCreateOptionsRbd **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsRbd), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsRbd_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsRbd(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevVmdkSubformat(Visitor *v, const char *name, ++ BlockdevVmdkSubformat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevVmdkSubformat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevVmdkAdapterType(Visitor *v, const char *name, ++ BlockdevVmdkAdapterType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevVmdkAdapterType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsVmdk_members(Visitor *v, BlockdevCreateOptionsVmdk *obj, Error **errp) ++{ ++ bool has_backing_file = !!obj->backing_file; ++ bool has_hwversion = !!obj->hwversion; ++ bool has_toolsversion = !!obj->toolsversion; ++ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "extents", &obj->has_extents)) { ++ if (!visit_type_BlockdevRefList(v, "extents", &obj->extents, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "subformat", &obj->has_subformat)) { ++ if (!visit_type_BlockdevVmdkSubformat(v, "subformat", &obj->subformat, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "adapter-type", &obj->has_adapter_type)) { ++ if (!visit_type_BlockdevVmdkAdapterType(v, "adapter-type", &obj->adapter_type, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "hwversion", &has_hwversion)) { ++ if (!visit_type_str(v, "hwversion", &obj->hwversion, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "toolsversion", &has_toolsversion)) { ++ if (!visit_type_str(v, "toolsversion", &obj->toolsversion, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "zeroed-grain", &obj->has_zeroed_grain)) { ++ if (!visit_type_bool(v, "zeroed-grain", &obj->zeroed_grain, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsVmdk(Visitor *v, const char *name, ++ BlockdevCreateOptionsVmdk **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsVmdk), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsVmdk_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsVmdk(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsSsh_members(Visitor *v, BlockdevCreateOptionsSsh *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsSsh(v, "location", &obj->location, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsSsh(Visitor *v, const char *name, ++ BlockdevCreateOptionsSsh **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsSsh), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsSsh_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsSsh(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsVdi_members(Visitor *v, BlockdevCreateOptionsVdi *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "preallocation", &obj->has_preallocation)) { ++ if (!visit_type_PreallocMode(v, "preallocation", &obj->preallocation, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsVdi(Visitor *v, const char *name, ++ BlockdevCreateOptionsVdi **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsVdi), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsVdi_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsVdi(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevVhdxSubformat(Visitor *v, const char *name, ++ BlockdevVhdxSubformat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevVhdxSubformat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsVhdx_members(Visitor *v, BlockdevCreateOptionsVhdx *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "log-size", &obj->has_log_size)) { ++ if (!visit_type_size(v, "log-size", &obj->log_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "block-size", &obj->has_block_size)) { ++ if (!visit_type_size(v, "block-size", &obj->block_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "subformat", &obj->has_subformat)) { ++ if (!visit_type_BlockdevVhdxSubformat(v, "subformat", &obj->subformat, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "block-state-zero", &obj->has_block_state_zero)) { ++ if (!visit_type_bool(v, "block-state-zero", &obj->block_state_zero, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsVhdx(Visitor *v, const char *name, ++ BlockdevCreateOptionsVhdx **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsVhdx), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsVhdx_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsVhdx(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevVpcSubformat(Visitor *v, const char *name, ++ BlockdevVpcSubformat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevVpcSubformat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsVpc_members(Visitor *v, BlockdevCreateOptionsVpc *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "subformat", &obj->has_subformat)) { ++ if (!visit_type_BlockdevVpcSubformat(v, "subformat", &obj->subformat, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "force-size", &obj->has_force_size)) { ++ if (!visit_type_bool(v, "force-size", &obj->force_size, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsVpc(Visitor *v, const char *name, ++ BlockdevCreateOptionsVpc **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsVpc), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsVpc_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsVpc(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockdevCreateOptions_base_members(Visitor *v, q_obj_BlockdevCreateOptions_base *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevDriver(v, "driver", &obj->driver, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptions_members(Visitor *v, BlockdevCreateOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockdevCreateOptions_base_members(v, (q_obj_BlockdevCreateOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->driver) { ++ case BLOCKDEV_DRIVER_FILE: ++ return visit_type_BlockdevCreateOptionsFile_members(v, &obj->u.file, errp); ++ case BLOCKDEV_DRIVER_GLUSTER: ++ return visit_type_BlockdevCreateOptionsGluster_members(v, &obj->u.gluster, errp); ++ case BLOCKDEV_DRIVER_LUKS: ++ return visit_type_BlockdevCreateOptionsLUKS_members(v, &obj->u.luks, errp); ++ case BLOCKDEV_DRIVER_NFS: ++ return visit_type_BlockdevCreateOptionsNfs_members(v, &obj->u.nfs, errp); ++ case BLOCKDEV_DRIVER_PARALLELS: ++ return visit_type_BlockdevCreateOptionsParallels_members(v, &obj->u.parallels, errp); ++ case BLOCKDEV_DRIVER_QCOW: ++ return visit_type_BlockdevCreateOptionsQcow_members(v, &obj->u.qcow, errp); ++ case BLOCKDEV_DRIVER_QCOW2: ++ return visit_type_BlockdevCreateOptionsQcow2_members(v, &obj->u.qcow2, errp); ++ case BLOCKDEV_DRIVER_QED: ++ return visit_type_BlockdevCreateOptionsQed_members(v, &obj->u.qed, errp); ++ case BLOCKDEV_DRIVER_RBD: ++ return visit_type_BlockdevCreateOptionsRbd_members(v, &obj->u.rbd, errp); ++ case BLOCKDEV_DRIVER_SSH: ++ return visit_type_BlockdevCreateOptionsSsh_members(v, &obj->u.ssh, errp); ++ case BLOCKDEV_DRIVER_VDI: ++ return visit_type_BlockdevCreateOptionsVdi_members(v, &obj->u.vdi, errp); ++ case BLOCKDEV_DRIVER_VHDX: ++ return visit_type_BlockdevCreateOptionsVhdx_members(v, &obj->u.vhdx, errp); ++ case BLOCKDEV_DRIVER_VMDK: ++ return visit_type_BlockdevCreateOptionsVmdk_members(v, &obj->u.vmdk, errp); ++ case BLOCKDEV_DRIVER_VPC: ++ return visit_type_BlockdevCreateOptionsVpc_members(v, &obj->u.vpc, errp); ++ case BLOCKDEV_DRIVER_BLKDEBUG: ++ break; ++ case BLOCKDEV_DRIVER_BLKLOGWRITES: ++ break; ++ case BLOCKDEV_DRIVER_BLKREPLAY: ++ break; ++ case BLOCKDEV_DRIVER_BLKVERIFY: ++ break; ++ case BLOCKDEV_DRIVER_BOCHS: ++ break; ++ case BLOCKDEV_DRIVER_CLOOP: ++ break; ++ case BLOCKDEV_DRIVER_COMPRESS: ++ break; ++ case BLOCKDEV_DRIVER_COPY_BEFORE_WRITE: ++ break; ++ case BLOCKDEV_DRIVER_COPY_ON_READ: ++ break; ++ case BLOCKDEV_DRIVER_DMG: ++ break; ++ case BLOCKDEV_DRIVER_SNAPSHOT_ACCESS: ++ break; ++ case BLOCKDEV_DRIVER_FTP: ++ break; ++ case BLOCKDEV_DRIVER_FTPS: ++ break; ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_CDROM: ++ break; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_DEVICE: ++ break; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ case BLOCKDEV_DRIVER_HTTP: ++ break; ++ case BLOCKDEV_DRIVER_HTTPS: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_IO_URING: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_ISCSI: ++ break; ++ case BLOCKDEV_DRIVER_NBD: ++ break; ++ case BLOCKDEV_DRIVER_NULL_AIO: ++ break; ++ case BLOCKDEV_DRIVER_NULL_CO: ++ break; ++ case BLOCKDEV_DRIVER_NVME: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_NVME_IO_URING: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_PREALLOCATE: ++ break; ++ case BLOCKDEV_DRIVER_QUORUM: ++ break; ++ case BLOCKDEV_DRIVER_RAW: ++ break; ++#if defined(CONFIG_REPLICATION) ++ case BLOCKDEV_DRIVER_REPLICATION: ++ break; ++#endif /* defined(CONFIG_REPLICATION) */ ++ case BLOCKDEV_DRIVER_THROTTLE: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VFIO_PCI: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_USER: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_VDPA: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_VVFAT: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptions(Visitor *v, const char *name, ++ BlockdevCreateOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_blockdev_create_arg_members(Visitor *v, q_obj_blockdev_create_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevCreateOptions(v, "options", &obj->options, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevAmendOptionsLUKS_members(Visitor *v, BlockdevAmendOptionsLUKS *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockAmendOptionsLUKS_members(v, (QCryptoBlockAmendOptionsLUKS *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevAmendOptionsLUKS(Visitor *v, const char *name, ++ BlockdevAmendOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevAmendOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevAmendOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevAmendOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevAmendOptionsQcow2_members(Visitor *v, BlockdevAmendOptionsQcow2 *obj, Error **errp) ++{ ++ bool has_encrypt = !!obj->encrypt; ++ ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_QCryptoBlockAmendOptions(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevAmendOptionsQcow2(Visitor *v, const char *name, ++ BlockdevAmendOptionsQcow2 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevAmendOptionsQcow2), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevAmendOptionsQcow2_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevAmendOptionsQcow2(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockdevAmendOptions_base_members(Visitor *v, q_obj_BlockdevAmendOptions_base *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevDriver(v, "driver", &obj->driver, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevAmendOptions_members(Visitor *v, BlockdevAmendOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockdevAmendOptions_base_members(v, (q_obj_BlockdevAmendOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->driver) { ++ case BLOCKDEV_DRIVER_LUKS: ++ return visit_type_BlockdevAmendOptionsLUKS_members(v, &obj->u.luks, errp); ++ case BLOCKDEV_DRIVER_QCOW2: ++ return visit_type_BlockdevAmendOptionsQcow2_members(v, &obj->u.qcow2, errp); ++ case BLOCKDEV_DRIVER_BLKDEBUG: ++ break; ++ case BLOCKDEV_DRIVER_BLKLOGWRITES: ++ break; ++ case BLOCKDEV_DRIVER_BLKREPLAY: ++ break; ++ case BLOCKDEV_DRIVER_BLKVERIFY: ++ break; ++ case BLOCKDEV_DRIVER_BOCHS: ++ break; ++ case BLOCKDEV_DRIVER_CLOOP: ++ break; ++ case BLOCKDEV_DRIVER_COMPRESS: ++ break; ++ case BLOCKDEV_DRIVER_COPY_BEFORE_WRITE: ++ break; ++ case BLOCKDEV_DRIVER_COPY_ON_READ: ++ break; ++ case BLOCKDEV_DRIVER_DMG: ++ break; ++ case BLOCKDEV_DRIVER_FILE: ++ break; ++ case BLOCKDEV_DRIVER_SNAPSHOT_ACCESS: ++ break; ++ case BLOCKDEV_DRIVER_FTP: ++ break; ++ case BLOCKDEV_DRIVER_FTPS: ++ break; ++ case BLOCKDEV_DRIVER_GLUSTER: ++ break; ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_CDROM: ++ break; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_DEVICE: ++ break; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ case BLOCKDEV_DRIVER_HTTP: ++ break; ++ case BLOCKDEV_DRIVER_HTTPS: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_IO_URING: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_ISCSI: ++ break; ++ case BLOCKDEV_DRIVER_NBD: ++ break; ++ case BLOCKDEV_DRIVER_NFS: ++ break; ++ case BLOCKDEV_DRIVER_NULL_AIO: ++ break; ++ case BLOCKDEV_DRIVER_NULL_CO: ++ break; ++ case BLOCKDEV_DRIVER_NVME: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_NVME_IO_URING: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_PARALLELS: ++ break; ++ case BLOCKDEV_DRIVER_PREALLOCATE: ++ break; ++ case BLOCKDEV_DRIVER_QCOW: ++ break; ++ case BLOCKDEV_DRIVER_QED: ++ break; ++ case BLOCKDEV_DRIVER_QUORUM: ++ break; ++ case BLOCKDEV_DRIVER_RAW: ++ break; ++ case BLOCKDEV_DRIVER_RBD: ++ break; ++#if defined(CONFIG_REPLICATION) ++ case BLOCKDEV_DRIVER_REPLICATION: ++ break; ++#endif /* defined(CONFIG_REPLICATION) */ ++ case BLOCKDEV_DRIVER_SSH: ++ break; ++ case BLOCKDEV_DRIVER_THROTTLE: ++ break; ++ case BLOCKDEV_DRIVER_VDI: ++ break; ++ case BLOCKDEV_DRIVER_VHDX: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VFIO_PCI: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_USER: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_VDPA: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_VMDK: ++ break; ++ case BLOCKDEV_DRIVER_VPC: ++ break; ++ case BLOCKDEV_DRIVER_VVFAT: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevAmendOptions(Visitor *v, const char *name, ++ BlockdevAmendOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevAmendOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevAmendOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevAmendOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_x_blockdev_amend_arg_members(Visitor *v, q_obj_x_blockdev_amend_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevAmendOptions(v, "options", &obj->options, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "force", &obj->has_force)) { ++ if (!visit_type_bool(v, "force", &obj->force, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockErrorAction(Visitor *v, const char *name, ++ BlockErrorAction *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockErrorAction_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_BLOCK_IMAGE_CORRUPTED_arg_members(Visitor *v, q_obj_BLOCK_IMAGE_CORRUPTED_arg *obj, Error **errp) ++{ ++ bool has_node_name = !!obj->node_name; ++ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "msg", &obj->msg, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "offset", &obj->has_offset)) { ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "size", &obj->has_size)) { ++ if (!visit_type_int(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_bool(v, "fatal", &obj->fatal, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_BLOCK_IO_ERROR_arg_members(Visitor *v, q_obj_BLOCK_IO_ERROR_arg *obj, Error **errp) ++{ ++ bool has_node_name = !!obj->node_name; ++ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_IoOperationType(v, "operation", &obj->operation, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockErrorAction(v, "action", &obj->action, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "nospace", &obj->has_nospace)) { ++ if (!visit_type_bool(v, "nospace", &obj->nospace, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "reason", &obj->reason, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_BLOCK_JOB_COMPLETED_arg_members(Visitor *v, q_obj_BLOCK_JOB_COMPLETED_arg *obj, Error **errp) ++{ ++ bool has_error = !!obj->error; ++ ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "len", &obj->len, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "error", &has_error)) { ++ if (!visit_type_str(v, "error", &obj->error, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_BLOCK_JOB_CANCELLED_arg_members(Visitor *v, q_obj_BLOCK_JOB_CANCELLED_arg *obj, Error **errp) ++{ ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "len", &obj->len, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_BLOCK_JOB_ERROR_arg_members(Visitor *v, q_obj_BLOCK_JOB_ERROR_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_IoOperationType(v, "operation", &obj->operation, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockErrorAction(v, "action", &obj->action, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_BLOCK_JOB_READY_arg_members(Visitor *v, q_obj_BLOCK_JOB_READY_arg *obj, Error **errp) ++{ ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "len", &obj->len, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_BLOCK_JOB_PENDING_arg_members(Visitor *v, q_obj_BLOCK_JOB_PENDING_arg *obj, Error **errp) ++{ ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_PreallocMode(Visitor *v, const char *name, ++ PreallocMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &PreallocMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_BLOCK_WRITE_THRESHOLD_arg_members(Visitor *v, q_obj_BLOCK_WRITE_THRESHOLD_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "amount-exceeded", &obj->amount_exceeded, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "write-threshold", &obj->write_threshold, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_set_write_threshold_arg_members(Visitor *v, q_obj_block_set_write_threshold_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "write-threshold", &obj->write_threshold, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_x_blockdev_change_arg_members(Visitor *v, q_obj_x_blockdev_change_arg *obj, Error **errp) ++{ ++ bool has_child = !!obj->child; ++ bool has_node = !!obj->node; ++ ++ if (!visit_type_str(v, "parent", &obj->parent, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "child", &has_child)) { ++ if (!visit_type_str(v, "child", &obj->child, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "node", &has_node)) { ++ if (!visit_type_str(v, "node", &obj->node, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_x_blockdev_set_iothread_arg_members(Visitor *v, q_obj_x_blockdev_set_iothread_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ if (!visit_type_StrOrNull(v, "iothread", &obj->iothread, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "force", &obj->has_force)) { ++ if (!visit_type_bool(v, "force", &obj->force, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_QuorumOpType(Visitor *v, const char *name, ++ QuorumOpType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QuorumOpType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_QUORUM_FAILURE_arg_members(Visitor *v, q_obj_QUORUM_FAILURE_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "reference", &obj->reference, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "sector-num", &obj->sector_num, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "sectors-count", &obj->sectors_count, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_QUORUM_REPORT_BAD_arg_members(Visitor *v, q_obj_QUORUM_REPORT_BAD_arg *obj, Error **errp) ++{ ++ bool has_error = !!obj->error; ++ ++ if (!visit_type_QuorumOpType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "error", &has_error)) { ++ if (!visit_type_str(v, "error", &obj->error, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "sector-num", &obj->sector_num, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "sectors-count", &obj->sectors_count, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevSnapshotInternal_members(Visitor *v, BlockdevSnapshotInternal *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevSnapshotInternal(Visitor *v, const char *name, ++ BlockdevSnapshotInternal **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevSnapshotInternal), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevSnapshotInternal_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevSnapshotInternal(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_blockdev_snapshot_delete_internal_sync_arg_members(Visitor *v, q_obj_blockdev_snapshot_delete_internal_sync_arg *obj, Error **errp) ++{ ++ bool has_id = !!obj->id; ++ bool has_name = !!obj->name; ++ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "id", &has_id)) { ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "name", &has_name)) { ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockGraphInfoList(Visitor *v, const char *name, ++ BlockGraphInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockGraphInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockGraphInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockGraphInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockGraphInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_DummyBlockCoreForceArrays_members(Visitor *v, DummyBlockCoreForceArrays *obj, Error **errp) ++{ ++ if (!visit_type_BlockGraphInfoList(v, "unused-block-graph-info", &obj->unused_block_graph_info, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_DummyBlockCoreForceArrays(Visitor *v, const char *name, ++ DummyBlockCoreForceArrays **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(DummyBlockCoreForceArrays), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_DummyBlockCoreForceArrays_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_DummyBlockCoreForceArrays(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_visit_block_core_c; +diff --git a/qcow2/lib/qapi/qapi-visit-common.c b/qcow2/lib/qapi/qapi-visit-common.c +new file mode 100644 +index 00000000..cacb730a +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-common.c +@@ -0,0 +1,176 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi-visit-common.h" ++ ++bool visit_type_IoOperationType(Visitor *v, const char *name, ++ IoOperationType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &IoOperationType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_OnOffAuto(Visitor *v, const char *name, ++ OnOffAuto *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &OnOffAuto_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_OnOffSplit(Visitor *v, const char *name, ++ OnOffSplit *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &OnOffSplit_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_StrOrNull(Visitor *v, const char *name, ++ StrOrNull **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_alternate(v, name, (GenericAlternate **)obj, ++ sizeof(**obj), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ switch ((*obj)->type) { ++ case QTYPE_QSTRING: ++ ok = visit_type_str(v, name, &(*obj)->u.s, errp); ++ break; ++ case QTYPE_QNULL: ++ ok = visit_type_null(v, name, &(*obj)->u.n, errp); ++ break; ++ case QTYPE_NONE: ++ abort(); ++ default: ++ assert(visit_is_input(v)); ++ error_setg(errp, ++ "Invalid parameter type for '%s', expected: StrOrNull", ++ name ? name : "null"); ++ /* Avoid passing invalid *obj to qapi_free_StrOrNull() */ ++ g_free(*obj); ++ *obj = NULL; ++ } ++out_obj: ++ visit_end_alternate(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_StrOrNull(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_OffAutoPCIBAR(Visitor *v, const char *name, ++ OffAutoPCIBAR *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &OffAutoPCIBAR_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_PCIELinkSpeed(Visitor *v, const char *name, ++ PCIELinkSpeed *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &PCIELinkSpeed_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_PCIELinkWidth(Visitor *v, const char *name, ++ PCIELinkWidth *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &PCIELinkWidth_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_HostMemPolicy(Visitor *v, const char *name, ++ HostMemPolicy *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &HostMemPolicy_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_NetFilterDirection(Visitor *v, const char *name, ++ NetFilterDirection *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &NetFilterDirection_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_GrabToggleKeys(Visitor *v, const char *name, ++ GrabToggleKeys *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &GrabToggleKeys_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_HumanReadableText_members(Visitor *v, HumanReadableText *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "human-readable-text", &obj->human_readable_text, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_HumanReadableText(Visitor *v, const char *name, ++ HumanReadableText **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(HumanReadableText), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_HumanReadableText_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_HumanReadableText(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_visit_common_c; +diff --git a/qcow2/lib/qapi/qapi-visit-core.c b/qcow2/lib/qapi/qapi-visit-core.c +new file mode 100644 +index 00000000..6c13510a +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-core.c +@@ -0,0 +1,444 @@ ++/* ++ * Core Definitions for QAPI Visitor Classes ++ * ++ * Copyright (C) 2012-2016 Red Hat, Inc. ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/compat-policy.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qerror.h" ++#include "qapi/visitor.h" ++#include "qapi/visitor-impl.h" ++#include "trace.h" ++ ++/* Zero-initialization must result in default policy */ ++QEMU_BUILD_BUG_ON(COMPAT_POLICY_INPUT_ACCEPT || COMPAT_POLICY_OUTPUT_ACCEPT); ++ ++ ++void visit_complete(Visitor *v, void *opaque) ++{ ++ assert(v->type != VISITOR_OUTPUT || v->complete); ++ trace_visit_complete(v, opaque); ++ if (v->complete) { ++ v->complete(v, opaque); ++ } ++} ++ ++void visit_free(Visitor *v) ++{ ++ trace_visit_free(v); ++ if (v) { ++ v->free(v); ++ } ++} ++ ++bool visit_start_struct(Visitor *v, const char *name, void **obj, ++ size_t size, Error **errp) ++{ ++ bool ok; ++ ++ trace_visit_start_struct(v, name, obj, size); ++ if (obj) { ++ assert(size); ++ assert(!(v->type & VISITOR_OUTPUT) || *obj); ++ } ++ ok = v->start_struct(v, name, obj, size, errp); ++ if (obj && (v->type & VISITOR_INPUT)) { ++ assert(ok != !*obj); ++ } ++ return ok; ++} ++ ++bool visit_check_struct(Visitor *v, Error **errp) ++{ ++ trace_visit_check_struct(v); ++ return v->check_struct ? v->check_struct(v, errp) : true; ++} ++ ++void visit_end_struct(Visitor *v, void **obj) ++{ ++ trace_visit_end_struct(v, obj); ++ v->end_struct(v, obj); ++} ++ ++bool visit_start_list(Visitor *v, const char *name, GenericList **list, ++ size_t size, Error **errp) ++{ ++ bool ok; ++ ++ assert(!list || size >= sizeof(GenericList)); ++ trace_visit_start_list(v, name, list, size); ++ ok = v->start_list(v, name, list, size, errp); ++ if (list && (v->type & VISITOR_INPUT)) { ++ assert(ok || !*list); ++ } ++ return ok; ++} ++ ++GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size) ++{ ++ assert(tail && size >= sizeof(GenericList)); ++ trace_visit_next_list(v, tail, size); ++ return v->next_list(v, tail, size); ++} ++ ++bool visit_check_list(Visitor *v, Error **errp) ++{ ++ trace_visit_check_list(v); ++ return v->check_list ? v->check_list(v, errp) : true; ++} ++ ++void visit_end_list(Visitor *v, void **obj) ++{ ++ trace_visit_end_list(v, obj); ++ v->end_list(v, obj); ++} ++ ++bool visit_start_alternate(Visitor *v, const char *name, ++ GenericAlternate **obj, size_t size, ++ Error **errp) ++{ ++ bool ok; ++ ++ assert(obj && size >= sizeof(GenericAlternate)); ++ assert(!(v->type & VISITOR_OUTPUT) || *obj); ++ trace_visit_start_alternate(v, name, obj, size); ++ if (!v->start_alternate) { ++ assert(!(v->type & VISITOR_INPUT)); ++ return true; ++ } ++ ok = v->start_alternate(v, name, obj, size, errp); ++ if (v->type & VISITOR_INPUT) { ++ assert(ok != !*obj); ++ } ++ return ok; ++} ++ ++void visit_end_alternate(Visitor *v, void **obj) ++{ ++ trace_visit_end_alternate(v, obj); ++ if (v->end_alternate) { ++ v->end_alternate(v, obj); ++ } ++} ++ ++bool visit_optional(Visitor *v, const char *name, bool *present) ++{ ++ trace_visit_optional(v, name, present); ++ if (v->optional) { ++ v->optional(v, name, present); ++ } ++ return *present; ++} ++ ++bool visit_policy_reject(Visitor *v, const char *name, ++ unsigned special_features, Error **errp) ++{ ++ trace_visit_policy_reject(v, name); ++ if (v->policy_reject) { ++ return v->policy_reject(v, name, special_features, errp); ++ } ++ return false; ++} ++ ++bool visit_policy_skip(Visitor *v, const char *name, ++ unsigned special_features) ++{ ++ trace_visit_policy_skip(v, name); ++ if (v->policy_skip) { ++ return v->policy_skip(v, name, special_features); ++ } ++ return false; ++} ++ ++void visit_set_policy(Visitor *v, CompatPolicy *policy) ++{ ++ v->compat_policy = *policy; ++} ++ ++bool visit_is_input(Visitor *v) ++{ ++ return v->type == VISITOR_INPUT; ++} ++ ++bool visit_is_dealloc(Visitor *v) ++{ ++ return v->type == VISITOR_DEALLOC; ++} ++ ++bool visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp) ++{ ++ assert(obj); ++ trace_visit_type_int(v, name, obj); ++ return v->type_int64(v, name, obj, errp); ++} ++ ++static bool visit_type_uintN(Visitor *v, uint64_t *obj, const char *name, ++ uint64_t max, const char *type, Error **errp) ++{ ++ uint64_t value = *obj; ++ ++ assert(v->type == VISITOR_INPUT || value <= max); ++ ++ if (!v->type_uint64(v, name, &value, errp)) { ++ return false; ++ } ++ if (value > max) { ++ assert(v->type == VISITOR_INPUT); ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ name ? name : "null", type); ++ return false; ++ } ++ *obj = value; ++ return true; ++} ++ ++bool visit_type_uint8(Visitor *v, const char *name, uint8_t *obj, ++ Error **errp) ++{ ++ uint64_t value; ++ bool ok; ++ ++ trace_visit_type_uint8(v, name, obj); ++ value = *obj; ++ ok = visit_type_uintN(v, &value, name, UINT8_MAX, "uint8_t", errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_uint16(Visitor *v, const char *name, uint16_t *obj, ++ Error **errp) ++{ ++ uint64_t value; ++ bool ok; ++ ++ trace_visit_type_uint16(v, name, obj); ++ value = *obj; ++ ok = visit_type_uintN(v, &value, name, UINT16_MAX, "uint16_t", errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_uint32(Visitor *v, const char *name, uint32_t *obj, ++ Error **errp) ++{ ++ uint64_t value; ++ bool ok; ++ ++ trace_visit_type_uint32(v, name, obj); ++ value = *obj; ++ ok = visit_type_uintN(v, &value, name, UINT32_MAX, "uint32_t", errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_uint64(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp) ++{ ++ assert(obj); ++ trace_visit_type_uint64(v, name, obj); ++ return v->type_uint64(v, name, obj, errp); ++} ++ ++static bool visit_type_intN(Visitor *v, int64_t *obj, const char *name, ++ int64_t min, int64_t max, const char *type, ++ Error **errp) ++{ ++ int64_t value = *obj; ++ ++ assert(v->type == VISITOR_INPUT || (value >= min && value <= max)); ++ ++ if (!v->type_int64(v, name, &value, errp)) { ++ return false; ++ } ++ if (value < min || value > max) { ++ assert(v->type == VISITOR_INPUT); ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ name ? name : "null", type); ++ return false; ++ } ++ *obj = value; ++ return true; ++} ++ ++bool visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp) ++{ ++ int64_t value; ++ bool ok; ++ ++ trace_visit_type_int8(v, name, obj); ++ value = *obj; ++ ok = visit_type_intN(v, &value, name, INT8_MIN, INT8_MAX, "int8_t", errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_int16(Visitor *v, const char *name, int16_t *obj, ++ Error **errp) ++{ ++ int64_t value; ++ bool ok; ++ ++ trace_visit_type_int16(v, name, obj); ++ value = *obj; ++ ok = visit_type_intN(v, &value, name, INT16_MIN, INT16_MAX, "int16_t", ++ errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_int32(Visitor *v, const char *name, int32_t *obj, ++ Error **errp) ++{ ++ int64_t value; ++ bool ok; ++ ++ trace_visit_type_int32(v, name, obj); ++ value = *obj; ++ ok = visit_type_intN(v, &value, name, INT32_MIN, INT32_MAX, "int32_t", ++ errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp) ++{ ++ assert(obj); ++ trace_visit_type_int64(v, name, obj); ++ return v->type_int64(v, name, obj, errp); ++} ++ ++bool visit_type_size(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp) ++{ ++ assert(obj); ++ trace_visit_type_size(v, name, obj); ++ if (v->type_size) { ++ return v->type_size(v, name, obj, errp); ++ } ++ return v->type_uint64(v, name, obj, errp); ++} ++ ++bool visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) ++{ ++ assert(obj); ++ trace_visit_type_bool(v, name, obj); ++ return v->type_bool(v, name, obj, errp); ++} ++ ++bool visit_type_str(Visitor *v, const char *name, char **obj, Error **errp) ++{ ++ bool ok; ++ ++ assert(obj); ++ /* TODO: Fix callers to not pass NULL when they mean "", so that we ++ * can enable: ++ assert(!(v->type & VISITOR_OUTPUT) || *obj); ++ */ ++ trace_visit_type_str(v, name, obj); ++ ok = v->type_str(v, name, obj, errp); ++ if (v->type & VISITOR_INPUT) { ++ assert(ok != !*obj); ++ } ++ return ok; ++} ++ ++bool visit_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp) ++{ ++ assert(obj); ++ trace_visit_type_number(v, name, obj); ++ return v->type_number(v, name, obj, errp); ++} ++ ++bool visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) ++{ ++ bool ok; ++ ++ assert(obj); ++ assert(v->type != VISITOR_OUTPUT || *obj); ++ trace_visit_type_any(v, name, obj); ++ ok = v->type_any(v, name, obj, errp); ++ if (v->type == VISITOR_INPUT) { ++ assert(ok != !*obj); ++ } ++ return ok; ++} ++ ++bool visit_type_null(Visitor *v, const char *name, QNull **obj, ++ Error **errp) ++{ ++ trace_visit_type_null(v, name, obj); ++ return v->type_null(v, name, obj, errp); ++} ++ ++static bool output_type_enum(Visitor *v, const char *name, int *obj, ++ const QEnumLookup *lookup, Error **errp) ++{ ++ int value = *obj; ++ char *enum_str; ++ ++ enum_str = (char *)qapi_enum_lookup(lookup, value); ++ return visit_type_str(v, name, &enum_str, errp); ++} ++ ++static bool input_type_enum(Visitor *v, const char *name, int *obj, ++ const QEnumLookup *lookup, Error **errp) ++{ ++ int64_t value; ++ g_autofree char *enum_str = NULL; ++ ++ if (!visit_type_str(v, name, &enum_str, errp)) { ++ return false; ++ } ++ ++ value = qapi_enum_parse(lookup, enum_str, -1, NULL); ++ if (value < 0) { ++ error_setg(errp, "Parameter '%s' does not accept value '%s'", ++ name ? name : "null", enum_str); ++ return false; ++ } ++ ++ if (lookup->special_features ++ && !compat_policy_input_ok(lookup->special_features[value], ++ &v->compat_policy, ++ ERROR_CLASS_GENERIC_ERROR, ++ "value", enum_str, errp)) { ++ return false; ++ } ++ ++ *obj = value; ++ return true; ++} ++ ++bool visit_type_enum(Visitor *v, const char *name, int *obj, ++ const QEnumLookup *lookup, Error **errp) ++{ ++ assert(obj && lookup); ++ trace_visit_type_enum(v, name, obj); ++ switch (v->type) { ++ case VISITOR_INPUT: ++ return input_type_enum(v, name, obj, lookup, errp); ++ case VISITOR_OUTPUT: ++ return output_type_enum(v, name, obj, lookup, errp); ++ case VISITOR_CLONE: ++ /* nothing further to do, scalar value was already copied by ++ * g_memdup() during visit_start_*() */ ++ return true; ++ case VISITOR_DEALLOC: ++ /* nothing to deallocate for a scalar */ ++ return true; ++ default: ++ abort(); ++ } ++} +diff --git a/qcow2/lib/qapi/qapi-visit-crypto.c b/qcow2/lib/qapi/qapi-visit-crypto.c +new file mode 100644 +index 00000000..09eab7e3 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-crypto.c +@@ -0,0 +1,1150 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi-visit-crypto.h" ++ ++bool visit_type_QCryptoTLSCredsEndpoint(Visitor *v, const char *name, ++ QCryptoTLSCredsEndpoint *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoTLSCredsEndpoint_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoSecretFormat(Visitor *v, const char *name, ++ QCryptoSecretFormat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoSecretFormat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoHashAlgorithm(Visitor *v, const char *name, ++ QCryptoHashAlgorithm *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoHashAlgorithm_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoCipherAlgorithm(Visitor *v, const char *name, ++ QCryptoCipherAlgorithm *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoCipherAlgorithm_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoCipherMode(Visitor *v, const char *name, ++ QCryptoCipherMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoCipherMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoIVGenAlgorithm(Visitor *v, const char *name, ++ QCryptoIVGenAlgorithm *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoIVGenAlgorithm_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockFormat(Visitor *v, const char *name, ++ QCryptoBlockFormat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoBlockFormat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockOptionsBase_members(Visitor *v, QCryptoBlockOptionsBase *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockOptionsBase(Visitor *v, const char *name, ++ QCryptoBlockOptionsBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockOptionsBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockOptionsBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockOptionsBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockOptionsQCow_members(Visitor *v, QCryptoBlockOptionsQCow *obj, Error **errp) ++{ ++ bool has_key_secret = !!obj->key_secret; ++ ++ if (visit_optional(v, "key-secret", &has_key_secret)) { ++ if (!visit_type_str(v, "key-secret", &obj->key_secret, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockOptionsQCow(Visitor *v, const char *name, ++ QCryptoBlockOptionsQCow **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockOptionsQCow), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockOptionsQCow_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockOptionsQCow(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockOptionsLUKS_members(Visitor *v, QCryptoBlockOptionsLUKS *obj, Error **errp) ++{ ++ bool has_key_secret = !!obj->key_secret; ++ ++ if (visit_optional(v, "key-secret", &has_key_secret)) { ++ if (!visit_type_str(v, "key-secret", &obj->key_secret, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockOptionsLUKS(Visitor *v, const char *name, ++ QCryptoBlockOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockCreateOptionsLUKS_members(Visitor *v, QCryptoBlockCreateOptionsLUKS *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockOptionsLUKS_members(v, (QCryptoBlockOptionsLUKS *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cipher-alg", &obj->has_cipher_alg)) { ++ if (!visit_type_QCryptoCipherAlgorithm(v, "cipher-alg", &obj->cipher_alg, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cipher-mode", &obj->has_cipher_mode)) { ++ if (!visit_type_QCryptoCipherMode(v, "cipher-mode", &obj->cipher_mode, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "ivgen-alg", &obj->has_ivgen_alg)) { ++ if (!visit_type_QCryptoIVGenAlgorithm(v, "ivgen-alg", &obj->ivgen_alg, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "ivgen-hash-alg", &obj->has_ivgen_hash_alg)) { ++ if (!visit_type_QCryptoHashAlgorithm(v, "ivgen-hash-alg", &obj->ivgen_hash_alg, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "hash-alg", &obj->has_hash_alg)) { ++ if (!visit_type_QCryptoHashAlgorithm(v, "hash-alg", &obj->hash_alg, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iter-time", &obj->has_iter_time)) { ++ if (!visit_type_int(v, "iter-time", &obj->iter_time, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockCreateOptionsLUKS(Visitor *v, const char *name, ++ QCryptoBlockCreateOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockCreateOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockCreateOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockCreateOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockOpenOptions_members(Visitor *v, QCryptoBlockOpenOptions *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockOptionsBase_members(v, (QCryptoBlockOptionsBase *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case Q_CRYPTO_BLOCK_FORMAT_QCOW: ++ return visit_type_QCryptoBlockOptionsQCow_members(v, &obj->u.qcow, errp); ++ case Q_CRYPTO_BLOCK_FORMAT_LUKS: ++ return visit_type_QCryptoBlockOptionsLUKS_members(v, &obj->u.luks, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockOpenOptions(Visitor *v, const char *name, ++ QCryptoBlockOpenOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockOpenOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockOpenOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockOpenOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockCreateOptions_members(Visitor *v, QCryptoBlockCreateOptions *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockOptionsBase_members(v, (QCryptoBlockOptionsBase *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case Q_CRYPTO_BLOCK_FORMAT_QCOW: ++ return visit_type_QCryptoBlockOptionsQCow_members(v, &obj->u.qcow, errp); ++ case Q_CRYPTO_BLOCK_FORMAT_LUKS: ++ return visit_type_QCryptoBlockCreateOptionsLUKS_members(v, &obj->u.luks, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockCreateOptions(Visitor *v, const char *name, ++ QCryptoBlockCreateOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockCreateOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockCreateOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockCreateOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockInfoBase_members(Visitor *v, QCryptoBlockInfoBase *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockInfoBase(Visitor *v, const char *name, ++ QCryptoBlockInfoBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockInfoBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockInfoBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockInfoBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockInfoLUKSSlot_members(Visitor *v, QCryptoBlockInfoLUKSSlot *obj, Error **errp) ++{ ++ if (!visit_type_bool(v, "active", &obj->active, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "iters", &obj->has_iters)) { ++ if (!visit_type_int(v, "iters", &obj->iters, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "stripes", &obj->has_stripes)) { ++ if (!visit_type_int(v, "stripes", &obj->stripes, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "key-offset", &obj->key_offset, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockInfoLUKSSlot(Visitor *v, const char *name, ++ QCryptoBlockInfoLUKSSlot **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockInfoLUKSSlot), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockInfoLUKSSlot_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockInfoLUKSSlot(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockInfoLUKSSlotList(Visitor *v, const char *name, ++ QCryptoBlockInfoLUKSSlotList **obj, Error **errp) ++{ ++ bool ok = false; ++ QCryptoBlockInfoLUKSSlotList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (QCryptoBlockInfoLUKSSlotList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_QCryptoBlockInfoLUKSSlot(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockInfoLUKSSlotList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockInfoLUKS_members(Visitor *v, QCryptoBlockInfoLUKS *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoCipherAlgorithm(v, "cipher-alg", &obj->cipher_alg, errp)) { ++ return false; ++ } ++ if (!visit_type_QCryptoCipherMode(v, "cipher-mode", &obj->cipher_mode, errp)) { ++ return false; ++ } ++ if (!visit_type_QCryptoIVGenAlgorithm(v, "ivgen-alg", &obj->ivgen_alg, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "ivgen-hash-alg", &obj->has_ivgen_hash_alg)) { ++ if (!visit_type_QCryptoHashAlgorithm(v, "ivgen-hash-alg", &obj->ivgen_hash_alg, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_QCryptoHashAlgorithm(v, "hash-alg", &obj->hash_alg, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "detached-header", &obj->detached_header, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "payload-offset", &obj->payload_offset, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "master-key-iters", &obj->master_key_iters, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "uuid", &obj->uuid, errp)) { ++ return false; ++ } ++ if (!visit_type_QCryptoBlockInfoLUKSSlotList(v, "slots", &obj->slots, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockInfoLUKS(Visitor *v, const char *name, ++ QCryptoBlockInfoLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockInfoLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockInfoLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockInfoLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockInfo_members(Visitor *v, QCryptoBlockInfo *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockInfoBase_members(v, (QCryptoBlockInfoBase *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case Q_CRYPTO_BLOCK_FORMAT_LUKS: ++ return visit_type_QCryptoBlockInfoLUKS_members(v, &obj->u.luks, errp); ++ case Q_CRYPTO_BLOCK_FORMAT_QCOW: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockInfo(Visitor *v, const char *name, ++ QCryptoBlockInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockLUKSKeyslotState(Visitor *v, const char *name, ++ QCryptoBlockLUKSKeyslotState *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoBlockLUKSKeyslotState_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockAmendOptionsLUKS_members(Visitor *v, QCryptoBlockAmendOptionsLUKS *obj, Error **errp) ++{ ++ bool has_new_secret = !!obj->new_secret; ++ bool has_old_secret = !!obj->old_secret; ++ bool has_secret = !!obj->secret; ++ ++ if (!visit_type_QCryptoBlockLUKSKeyslotState(v, "state", &obj->state, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "new-secret", &has_new_secret)) { ++ if (!visit_type_str(v, "new-secret", &obj->new_secret, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "old-secret", &has_old_secret)) { ++ if (!visit_type_str(v, "old-secret", &obj->old_secret, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "keyslot", &obj->has_keyslot)) { ++ if (!visit_type_int(v, "keyslot", &obj->keyslot, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iter-time", &obj->has_iter_time)) { ++ if (!visit_type_int(v, "iter-time", &obj->iter_time, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "secret", &has_secret)) { ++ if (!visit_type_str(v, "secret", &obj->secret, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockAmendOptionsLUKS(Visitor *v, const char *name, ++ QCryptoBlockAmendOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockAmendOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockAmendOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockAmendOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockAmendOptions_members(Visitor *v, QCryptoBlockAmendOptions *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockOptionsBase_members(v, (QCryptoBlockOptionsBase *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case Q_CRYPTO_BLOCK_FORMAT_LUKS: ++ return visit_type_QCryptoBlockAmendOptionsLUKS_members(v, &obj->u.luks, errp); ++ case Q_CRYPTO_BLOCK_FORMAT_QCOW: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockAmendOptions(Visitor *v, const char *name, ++ QCryptoBlockAmendOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockAmendOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockAmendOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockAmendOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_SecretCommonProperties_members(Visitor *v, SecretCommonProperties *obj, Error **errp) ++{ ++ bool has_keyid = !!obj->keyid; ++ bool has_iv = !!obj->iv; ++ ++ if (visit_optional(v, "loaded", &obj->has_loaded)) { ++ if (visit_policy_reject(v, "loaded", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "loaded", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_bool(v, "loaded", &obj->loaded, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "format", &obj->has_format)) { ++ if (!visit_type_QCryptoSecretFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "keyid", &has_keyid)) { ++ if (!visit_type_str(v, "keyid", &obj->keyid, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iv", &has_iv)) { ++ if (!visit_type_str(v, "iv", &obj->iv, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_SecretCommonProperties(Visitor *v, const char *name, ++ SecretCommonProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SecretCommonProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SecretCommonProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SecretCommonProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_SecretProperties_members(Visitor *v, SecretProperties *obj, Error **errp) ++{ ++ bool has_data = !!obj->data; ++ bool has_file = !!obj->file; ++ ++ if (!visit_type_SecretCommonProperties_members(v, (SecretCommonProperties *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "data", &has_data)) { ++ if (!visit_type_str(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "file", &has_file)) { ++ if (!visit_type_str(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_SecretProperties(Visitor *v, const char *name, ++ SecretProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SecretProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SecretProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SecretProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++#if defined(CONFIG_SECRET_KEYRING) ++bool visit_type_SecretKeyringProperties_members(Visitor *v, SecretKeyringProperties *obj, Error **errp) ++{ ++ if (!visit_type_SecretCommonProperties_members(v, (SecretCommonProperties *)obj, errp)) { ++ return false; ++ } ++ if (!visit_type_int32(v, "serial", &obj->serial, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_SecretKeyringProperties(Visitor *v, const char *name, ++ SecretKeyringProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SecretKeyringProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SecretKeyringProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SecretKeyringProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_SECRET_KEYRING) */ ++ ++bool visit_type_TlsCredsProperties_members(Visitor *v, TlsCredsProperties *obj, Error **errp) ++{ ++ bool has_dir = !!obj->dir; ++ bool has_priority = !!obj->priority; ++ ++ if (visit_optional(v, "verify-peer", &obj->has_verify_peer)) { ++ if (!visit_type_bool(v, "verify-peer", &obj->verify_peer, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "dir", &has_dir)) { ++ if (!visit_type_str(v, "dir", &obj->dir, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "endpoint", &obj->has_endpoint)) { ++ if (!visit_type_QCryptoTLSCredsEndpoint(v, "endpoint", &obj->endpoint, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "priority", &has_priority)) { ++ if (!visit_type_str(v, "priority", &obj->priority, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_TlsCredsProperties(Visitor *v, const char *name, ++ TlsCredsProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(TlsCredsProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_TlsCredsProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_TlsCredsProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_TlsCredsAnonProperties_members(Visitor *v, TlsCredsAnonProperties *obj, Error **errp) ++{ ++ if (!visit_type_TlsCredsProperties_members(v, (TlsCredsProperties *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "loaded", &obj->has_loaded)) { ++ if (visit_policy_reject(v, "loaded", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "loaded", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_bool(v, "loaded", &obj->loaded, errp)) { ++ return false; ++ } ++ } ++ } ++ return true; ++} ++ ++bool visit_type_TlsCredsAnonProperties(Visitor *v, const char *name, ++ TlsCredsAnonProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(TlsCredsAnonProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_TlsCredsAnonProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_TlsCredsAnonProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_TlsCredsPskProperties_members(Visitor *v, TlsCredsPskProperties *obj, Error **errp) ++{ ++ bool has_username = !!obj->username; ++ ++ if (!visit_type_TlsCredsProperties_members(v, (TlsCredsProperties *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "loaded", &obj->has_loaded)) { ++ if (visit_policy_reject(v, "loaded", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "loaded", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_bool(v, "loaded", &obj->loaded, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "username", &has_username)) { ++ if (!visit_type_str(v, "username", &obj->username, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_TlsCredsPskProperties(Visitor *v, const char *name, ++ TlsCredsPskProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(TlsCredsPskProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_TlsCredsPskProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_TlsCredsPskProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_TlsCredsX509Properties_members(Visitor *v, TlsCredsX509Properties *obj, Error **errp) ++{ ++ bool has_passwordid = !!obj->passwordid; ++ ++ if (!visit_type_TlsCredsProperties_members(v, (TlsCredsProperties *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "loaded", &obj->has_loaded)) { ++ if (visit_policy_reject(v, "loaded", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "loaded", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_bool(v, "loaded", &obj->loaded, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "sanity-check", &obj->has_sanity_check)) { ++ if (!visit_type_bool(v, "sanity-check", &obj->sanity_check, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "passwordid", &has_passwordid)) { ++ if (!visit_type_str(v, "passwordid", &obj->passwordid, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_TlsCredsX509Properties(Visitor *v, const char *name, ++ TlsCredsX509Properties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(TlsCredsX509Properties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_TlsCredsX509Properties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_TlsCredsX509Properties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoAkCipherAlgorithm(Visitor *v, const char *name, ++ QCryptoAkCipherAlgorithm *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoAkCipherAlgorithm_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoAkCipherKeyType(Visitor *v, const char *name, ++ QCryptoAkCipherKeyType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoAkCipherKeyType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoRSAPaddingAlgorithm(Visitor *v, const char *name, ++ QCryptoRSAPaddingAlgorithm *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoRSAPaddingAlgorithm_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoAkCipherOptionsRSA_members(Visitor *v, QCryptoAkCipherOptionsRSA *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoHashAlgorithm(v, "hash-alg", &obj->hash_alg, errp)) { ++ return false; ++ } ++ if (!visit_type_QCryptoRSAPaddingAlgorithm(v, "padding-alg", &obj->padding_alg, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoAkCipherOptionsRSA(Visitor *v, const char *name, ++ QCryptoAkCipherOptionsRSA **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoAkCipherOptionsRSA), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoAkCipherOptionsRSA_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoAkCipherOptionsRSA(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_QCryptoAkCipherOptions_base_members(Visitor *v, q_obj_QCryptoAkCipherOptions_base *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoAkCipherAlgorithm(v, "alg", &obj->alg, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoAkCipherOptions_members(Visitor *v, QCryptoAkCipherOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_QCryptoAkCipherOptions_base_members(v, (q_obj_QCryptoAkCipherOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->alg) { ++ case QCRYPTO_AKCIPHER_ALG_RSA: ++ return visit_type_QCryptoAkCipherOptionsRSA_members(v, &obj->u.rsa, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoAkCipherOptions(Visitor *v, const char *name, ++ QCryptoAkCipherOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoAkCipherOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoAkCipherOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoAkCipherOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_visit_crypto_c; +diff --git a/qcow2/lib/qapi/qapi-visit-job.c b/qcow2/lib/qapi/qapi-visit-job.c +new file mode 100644 +index 00000000..092f9a03 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-job.c +@@ -0,0 +1,186 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi-visit-job.h" ++ ++bool visit_type_JobType(Visitor *v, const char *name, ++ JobType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &JobType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_JobStatus(Visitor *v, const char *name, ++ JobStatus *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &JobStatus_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_JobVerb(Visitor *v, const char *name, ++ JobVerb *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &JobVerb_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_JOB_STATUS_CHANGE_arg_members(Visitor *v, q_obj_JOB_STATUS_CHANGE_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ if (!visit_type_JobStatus(v, "status", &obj->status, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_job_pause_arg_members(Visitor *v, q_obj_job_pause_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_job_resume_arg_members(Visitor *v, q_obj_job_resume_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_job_cancel_arg_members(Visitor *v, q_obj_job_cancel_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_job_complete_arg_members(Visitor *v, q_obj_job_complete_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_job_dismiss_arg_members(Visitor *v, q_obj_job_dismiss_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_job_finalize_arg_members(Visitor *v, q_obj_job_finalize_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_JobInfo_members(Visitor *v, JobInfo *obj, Error **errp) ++{ ++ bool has_error = !!obj->error; ++ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_JobStatus(v, "status", &obj->status, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "current-progress", &obj->current_progress, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "total-progress", &obj->total_progress, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "error", &has_error)) { ++ if (!visit_type_str(v, "error", &obj->error, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_JobInfo(Visitor *v, const char *name, ++ JobInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(JobInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_JobInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_JobInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_JobInfoList(Visitor *v, const char *name, ++ JobInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ JobInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (JobInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_JobInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_JobInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_visit_job_c; +diff --git a/qcow2/lib/qapi/qapi-visit-sockets.c b/qcow2/lib/qapi/qapi-visit-sockets.c +new file mode 100644 +index 00000000..34d0b894 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-sockets.c +@@ -0,0 +1,569 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi-visit-sockets.h" ++ ++bool visit_type_NetworkAddressFamily(Visitor *v, const char *name, ++ NetworkAddressFamily *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &NetworkAddressFamily_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_InetSocketAddressBase_members(Visitor *v, InetSocketAddressBase *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "host", &obj->host, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "port", &obj->port, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_InetSocketAddressBase(Visitor *v, const char *name, ++ InetSocketAddressBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(InetSocketAddressBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_InetSocketAddressBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_InetSocketAddressBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_InetSocketAddress_members(Visitor *v, InetSocketAddress *obj, Error **errp) ++{ ++ if (!visit_type_InetSocketAddressBase_members(v, (InetSocketAddressBase *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "numeric", &obj->has_numeric)) { ++ if (!visit_type_bool(v, "numeric", &obj->numeric, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "to", &obj->has_to)) { ++ if (!visit_type_uint16(v, "to", &obj->to, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "ipv4", &obj->has_ipv4)) { ++ if (!visit_type_bool(v, "ipv4", &obj->ipv4, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "ipv6", &obj->has_ipv6)) { ++ if (!visit_type_bool(v, "ipv6", &obj->ipv6, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "keep-alive", &obj->has_keep_alive)) { ++ if (!visit_type_bool(v, "keep-alive", &obj->keep_alive, errp)) { ++ return false; ++ } ++ } ++#if defined(HAVE_IPPROTO_MPTCP) ++ if (visit_optional(v, "mptcp", &obj->has_mptcp)) { ++ if (!visit_type_bool(v, "mptcp", &obj->mptcp, errp)) { ++ return false; ++ } ++ } ++#endif /* defined(HAVE_IPPROTO_MPTCP) */ ++ return true; ++} ++ ++bool visit_type_InetSocketAddress(Visitor *v, const char *name, ++ InetSocketAddress **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(InetSocketAddress), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_InetSocketAddress_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_InetSocketAddress(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_UnixSocketAddress_members(Visitor *v, UnixSocketAddress *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++#if defined(CONFIG_LINUX) ++ if (visit_optional(v, "abstract", &obj->has_abstract)) { ++ if (!visit_type_bool(v, "abstract", &obj->abstract, errp)) { ++ return false; ++ } ++ } ++#endif /* defined(CONFIG_LINUX) */ ++#if defined(CONFIG_LINUX) ++ if (visit_optional(v, "tight", &obj->has_tight)) { ++ if (!visit_type_bool(v, "tight", &obj->tight, errp)) { ++ return false; ++ } ++ } ++#endif /* defined(CONFIG_LINUX) */ ++ return true; ++} ++ ++bool visit_type_UnixSocketAddress(Visitor *v, const char *name, ++ UnixSocketAddress **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(UnixSocketAddress), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_UnixSocketAddress_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_UnixSocketAddress(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_VsockSocketAddress_members(Visitor *v, VsockSocketAddress *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "cid", &obj->cid, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "port", &obj->port, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_VsockSocketAddress(Visitor *v, const char *name, ++ VsockSocketAddress **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(VsockSocketAddress), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_VsockSocketAddress_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_VsockSocketAddress(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_FdSocketAddress_members(Visitor *v, FdSocketAddress *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "str", &obj->str, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_FdSocketAddress(Visitor *v, const char *name, ++ FdSocketAddress **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(FdSocketAddress), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_FdSocketAddress_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_FdSocketAddress(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_InetSocketAddressWrapper_members(Visitor *v, InetSocketAddressWrapper *obj, Error **errp) ++{ ++ if (!visit_type_InetSocketAddress(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_InetSocketAddressWrapper(Visitor *v, const char *name, ++ InetSocketAddressWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(InetSocketAddressWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_InetSocketAddressWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_InetSocketAddressWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_UnixSocketAddressWrapper_members(Visitor *v, UnixSocketAddressWrapper *obj, Error **errp) ++{ ++ if (!visit_type_UnixSocketAddress(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_UnixSocketAddressWrapper(Visitor *v, const char *name, ++ UnixSocketAddressWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(UnixSocketAddressWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_UnixSocketAddressWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_UnixSocketAddressWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_VsockSocketAddressWrapper_members(Visitor *v, VsockSocketAddressWrapper *obj, Error **errp) ++{ ++ if (!visit_type_VsockSocketAddress(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_VsockSocketAddressWrapper(Visitor *v, const char *name, ++ VsockSocketAddressWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(VsockSocketAddressWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_VsockSocketAddressWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_VsockSocketAddressWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_FdSocketAddressWrapper_members(Visitor *v, FdSocketAddressWrapper *obj, Error **errp) ++{ ++ if (!visit_type_FdSocketAddress(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_FdSocketAddressWrapper(Visitor *v, const char *name, ++ FdSocketAddressWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(FdSocketAddressWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_FdSocketAddressWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_FdSocketAddressWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_SocketAddressLegacy_base_members(Visitor *v, q_obj_SocketAddressLegacy_base *obj, Error **errp) ++{ ++ if (!visit_type_SocketAddressType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_SocketAddressLegacy_members(Visitor *v, SocketAddressLegacy *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_SocketAddressLegacy_base_members(v, (q_obj_SocketAddressLegacy_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ return visit_type_InetSocketAddressWrapper_members(v, &obj->u.inet, errp); ++ case SOCKET_ADDRESS_TYPE_UNIX: ++ return visit_type_UnixSocketAddressWrapper_members(v, &obj->u.q_unix, errp); ++ case SOCKET_ADDRESS_TYPE_VSOCK: ++ return visit_type_VsockSocketAddressWrapper_members(v, &obj->u.vsock, errp); ++ case SOCKET_ADDRESS_TYPE_FD: ++ return visit_type_FdSocketAddressWrapper_members(v, &obj->u.fd, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_SocketAddressLegacy(Visitor *v, const char *name, ++ SocketAddressLegacy **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SocketAddressLegacy), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SocketAddressLegacy_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SocketAddressLegacy(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_SocketAddressType(Visitor *v, const char *name, ++ SocketAddressType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &SocketAddressType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_SocketAddress_base_members(Visitor *v, q_obj_SocketAddress_base *obj, Error **errp) ++{ ++ if (!visit_type_SocketAddressType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_SocketAddress_members(Visitor *v, SocketAddress *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_SocketAddress_base_members(v, (q_obj_SocketAddress_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ return visit_type_InetSocketAddress_members(v, &obj->u.inet, errp); ++ case SOCKET_ADDRESS_TYPE_UNIX: ++ return visit_type_UnixSocketAddress_members(v, &obj->u.q_unix, errp); ++ case SOCKET_ADDRESS_TYPE_VSOCK: ++ return visit_type_VsockSocketAddress_members(v, &obj->u.vsock, errp); ++ case SOCKET_ADDRESS_TYPE_FD: ++ return visit_type_FdSocketAddress_members(v, &obj->u.fd, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_SocketAddress(Visitor *v, const char *name, ++ SocketAddress **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SocketAddress), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SocketAddress_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SocketAddress(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_SocketAddressList(Visitor *v, const char *name, ++ SocketAddressList **obj, Error **errp) ++{ ++ bool ok = false; ++ SocketAddressList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (SocketAddressList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_SocketAddress(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SocketAddressList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_InetSocketAddressBaseList(Visitor *v, const char *name, ++ InetSocketAddressBaseList **obj, Error **errp) ++{ ++ bool ok = false; ++ InetSocketAddressBaseList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (InetSocketAddressBaseList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_InetSocketAddressBase(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_InetSocketAddressBaseList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_visit_sockets_c; +diff --git a/qcow2/lib/qapi/qapi-visit-yank.c b/qcow2/lib/qapi/qapi-visit-yank.c +new file mode 100644 +index 00000000..f5d5bf25 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-yank.c +@@ -0,0 +1,186 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi-visit-yank.h" ++ ++bool visit_type_YankInstanceType(Visitor *v, const char *name, ++ YankInstanceType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &YankInstanceType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_YankInstanceBlockNode_members(Visitor *v, YankInstanceBlockNode *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_YankInstanceBlockNode(Visitor *v, const char *name, ++ YankInstanceBlockNode **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(YankInstanceBlockNode), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_YankInstanceBlockNode_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_YankInstanceBlockNode(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_YankInstanceChardev_members(Visitor *v, YankInstanceChardev *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_YankInstanceChardev(Visitor *v, const char *name, ++ YankInstanceChardev **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(YankInstanceChardev), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_YankInstanceChardev_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_YankInstanceChardev(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_YankInstance_base_members(Visitor *v, q_obj_YankInstance_base *obj, Error **errp) ++{ ++ if (!visit_type_YankInstanceType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_YankInstance_members(Visitor *v, YankInstance *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_YankInstance_base_members(v, (q_obj_YankInstance_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->type) { ++ case YANK_INSTANCE_TYPE_BLOCK_NODE: ++ return visit_type_YankInstanceBlockNode_members(v, &obj->u.block_node, errp); ++ case YANK_INSTANCE_TYPE_CHARDEV: ++ return visit_type_YankInstanceChardev_members(v, &obj->u.chardev, errp); ++ case YANK_INSTANCE_TYPE_MIGRATION: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_YankInstance(Visitor *v, const char *name, ++ YankInstance **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(YankInstance), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_YankInstance_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_YankInstance(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_YankInstanceList(Visitor *v, const char *name, ++ YankInstanceList **obj, Error **errp) ++{ ++ bool ok = false; ++ YankInstanceList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (YankInstanceList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_YankInstance(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_YankInstanceList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_yank_arg_members(Visitor *v, q_obj_yank_arg *obj, Error **errp) ++{ ++ if (!visit_type_YankInstanceList(v, "instances", &obj->instances, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_visit_yank_c; +diff --git a/qcow2/lib/qapi/qmp-dispatch.c b/qcow2/lib/qapi/qmp-dispatch.c +new file mode 100644 +index 00000000..176b5494 +--- /dev/null ++++ b/qcow2/lib/qapi/qmp-dispatch.c +@@ -0,0 +1,296 @@ ++/* ++ * Core Definitions for QAPI/QMP Dispatch ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "block/aio.h" ++#include "qapi/compat-policy.h" ++#include "qapi/error.h" ++#include "qapi/qmp/dispatch.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qjson.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qapi/qobject-output-visitor.h" ++#include "qapi/qmp/qbool.h" ++#include "qemu/coroutine.h" ++#include "qemu/main-loop.h" ++ ++Visitor *qobject_input_visitor_new_qmp(QObject *obj) ++{ ++ Visitor *v = qobject_input_visitor_new(obj); ++ ++ visit_set_policy(v, &compat_policy); ++ return v; ++} ++ ++Visitor *qobject_output_visitor_new_qmp(QObject **result) ++{ ++ Visitor *v = qobject_output_visitor_new(result); ++ ++ visit_set_policy(v, &compat_policy); ++ return v; ++} ++ ++static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob, ++ Error **errp) ++{ ++ const char *exec_key = NULL; ++ const QDictEntry *ent; ++ const char *arg_name; ++ const QObject *arg_obj; ++ ++ for (ent = qdict_first(dict); ent; ++ ent = qdict_next(dict, ent)) { ++ arg_name = qdict_entry_key(ent); ++ arg_obj = qdict_entry_value(ent); ++ ++ if (!strcmp(arg_name, "execute") ++ || (!strcmp(arg_name, "exec-oob") && allow_oob)) { ++ if (qobject_type(arg_obj) != QTYPE_QSTRING) { ++ error_setg(errp, "QMP input member '%s' must be a string", ++ arg_name); ++ return NULL; ++ } ++ if (exec_key) { ++ error_setg(errp, "QMP input member '%s' clashes with '%s'", ++ arg_name, exec_key); ++ return NULL; ++ } ++ exec_key = arg_name; ++ } else if (!strcmp(arg_name, "arguments")) { ++ if (qobject_type(arg_obj) != QTYPE_QDICT) { ++ error_setg(errp, ++ "QMP input member 'arguments' must be an object"); ++ return NULL; ++ } ++ } else if (!strcmp(arg_name, "id")) { ++ continue; ++ } else { ++ error_setg(errp, "QMP input member '%s' is unexpected", ++ arg_name); ++ return NULL; ++ } ++ } ++ ++ if (!exec_key) { ++ error_setg(errp, "QMP input lacks member 'execute'"); ++ return NULL; ++ } ++ ++ return dict; ++} ++ ++QDict *qmp_error_response(Error *err) ++{ ++ QDict *rsp; ++ ++ rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }", ++ QapiErrorClass_str(error_get_class(err)), ++ error_get_pretty(err)); ++ error_free(err); ++ return rsp; ++} ++ ++/* ++ * Does @qdict look like a command to be run out-of-band? ++ */ ++bool qmp_is_oob(const QDict *dict) ++{ ++ return qdict_haskey(dict, "exec-oob") ++ && !qdict_haskey(dict, "execute"); ++} ++ ++typedef struct QmpDispatchBH { ++ const QmpCommand *cmd; ++ Monitor *cur_mon; ++ QDict *args; ++ QObject **ret; ++ Error **errp; ++ Coroutine *co; ++} QmpDispatchBH; ++ ++static void do_qmp_dispatch_bh(void *opaque) ++{ ++ QmpDispatchBH *data = opaque; ++ ++ assert(monitor_cur() == NULL); ++ monitor_set_cur(qemu_coroutine_self(), data->cur_mon); ++ data->cmd->fn(data->args, data->ret, data->errp); ++ monitor_set_cur(qemu_coroutine_self(), NULL); ++ aio_co_wake(data->co); ++} ++ ++/* ++ * Runs outside of coroutine context for OOB commands, but in coroutine ++ * context for everything else. ++ */ ++QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *request, ++ bool allow_oob, Monitor *cur_mon) ++{ ++ Error *err = NULL; ++ bool oob; ++ const char *command; ++ QDict *args; ++ const QmpCommand *cmd; ++ QDict *dict; ++ QObject *id; ++ QObject *ret = NULL; ++ QDict *rsp = NULL; ++ ++ dict = qobject_to(QDict, request); ++ if (!dict) { ++ id = NULL; ++ error_setg(&err, "QMP input must be a JSON object"); ++ goto out; ++ } ++ ++ id = qdict_get(dict, "id"); ++ ++ if (!qmp_dispatch_check_obj(dict, allow_oob, &err)) { ++ goto out; ++ } ++ ++ command = qdict_get_try_str(dict, "execute"); ++ oob = false; ++ if (!command) { ++ assert(allow_oob); ++ command = qdict_get_str(dict, "exec-oob"); ++ oob = true; ++ } ++ cmd = qmp_find_command(cmds, command); ++ if (cmd == NULL) { ++ error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, ++ "The command %s has not been found", command); ++ goto out; ++ } ++ if (!compat_policy_input_ok(cmd->special_features, &compat_policy, ++ ERROR_CLASS_COMMAND_NOT_FOUND, ++ "command", command, &err)) { ++ goto out; ++ } ++ if (!cmd->enabled) { ++ error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, ++ "Command %s has been disabled%s%s", ++ command, ++ cmd->disable_reason ? ": " : "", ++ cmd->disable_reason ?: ""); ++ goto out; ++ } ++ if (oob && !(cmd->options & QCO_ALLOW_OOB)) { ++ error_setg(&err, "The command %s does not support OOB", ++ command); ++ goto out; ++ } ++ ++ if (!qmp_command_available(cmd, &err)) { ++ goto out; ++ } ++ ++ if (!qdict_haskey(dict, "arguments")) { ++ args = qdict_new(); ++ } else { ++ args = qdict_get_qdict(dict, "arguments"); ++ qobject_ref(args); ++ } ++ ++ assert(!(oob && qemu_in_coroutine())); ++ assert(monitor_cur() == NULL); ++ if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) { ++ if (qemu_in_coroutine()) { ++ /* ++ * Move the coroutine from iohandler_ctx to qemu_aio_context for ++ * executing the command handler so that it can make progress if it ++ * involves an AIO_WAIT_WHILE(). ++ */ ++ aio_co_schedule(qemu_get_aio_context(), qemu_coroutine_self()); ++ qemu_coroutine_yield(); ++ } ++ ++ monitor_set_cur(qemu_coroutine_self(), cur_mon); ++ cmd->fn(args, &ret, &err); ++ monitor_set_cur(qemu_coroutine_self(), NULL); ++ ++ if (qemu_in_coroutine()) { ++ /* ++ * Yield and reschedule so the main loop stays responsive. ++ * ++ * Move back to iohandler_ctx so that nested event loops for ++ * qemu_aio_context don't start new monitor commands. ++ */ ++ aio_co_schedule(iohandler_get_aio_context(), ++ qemu_coroutine_self()); ++ qemu_coroutine_yield(); ++ } ++ } else { ++ /* ++ * Actual context doesn't match the one the command needs. ++ * ++ * Case 1: we are in coroutine context, but command does not ++ * have QCO_COROUTINE. We need to drop out of coroutine ++ * context for executing it. ++ * ++ * Case 2: we are outside coroutine context, but command has ++ * QCO_COROUTINE. Can't actually happen, because we get here ++ * outside coroutine context only when executing a command ++ * out of band, and OOB commands never have QCO_COROUTINE. ++ */ ++ assert(!oob && qemu_in_coroutine() && !(cmd->options & QCO_COROUTINE)); ++ ++ QmpDispatchBH data = { ++ .cur_mon = cur_mon, ++ .cmd = cmd, ++ .args = args, ++ .ret = &ret, ++ .errp = &err, ++ .co = qemu_coroutine_self(), ++ }; ++ aio_bh_schedule_oneshot(iohandler_get_aio_context(), do_qmp_dispatch_bh, ++ &data); ++ qemu_coroutine_yield(); ++ } ++ qobject_unref(args); ++ if (err) { ++ /* or assert(!ret) after reviewing all handlers: */ ++ qobject_unref(ret); ++ goto out; ++ } ++ ++ if (cmd->options & QCO_NO_SUCCESS_RESP) { ++ g_assert(!ret); ++ return NULL; ++ } else if (!ret) { ++ /* ++ * When the command's schema has no 'returns', cmd->fn() ++ * leaves @ret null. The QMP spec calls for an empty object ++ * then; supply it. ++ */ ++ ret = QOBJECT(qdict_new()); ++ } ++ ++ rsp = qdict_new(); ++ qdict_put_obj(rsp, "return", ret); ++ ++out: ++ if (err) { ++ assert(!rsp); ++ rsp = qmp_error_response(err); ++ } ++ ++ assert(rsp); ++ ++ if (id) { ++ qdict_put_obj(rsp, "id", qobject_ref(id)); ++ } ++ ++ return rsp; ++} +diff --git a/qcow2/lib/qapi/qmp-event.c b/qcow2/lib/qapi/qmp-event.c +new file mode 100644 +index 00000000..0fe0d0a5 +--- /dev/null ++++ b/qcow2/lib/qapi/qmp-event.c +@@ -0,0 +1,42 @@ ++/* ++ * QMP Event related ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * ++ * Authors: ++ * Wenchao Xia ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "qapi/qmp-event.h" ++#include "qapi/qmp/qstring.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qjson.h" ++ ++static void timestamp_put(QDict *qdict) ++{ ++ QDict *ts; ++ int64_t rt = g_get_real_time(); ++ ++ ts = qdict_from_jsonf_nofail("{ 'seconds': %lld, 'microseconds': %lld }", ++ (long long)rt / G_USEC_PER_SEC, ++ (long long)rt % G_USEC_PER_SEC); ++ qdict_put(qdict, "timestamp", ts); ++} ++ ++/* ++ * Build a QDict, then fill event name and time stamp, caller should free the ++ * QDict after usage. ++ */ ++QDict *qmp_event_build_dict(const char *event_name) ++{ ++ QDict *dict = qdict_new(); ++ qdict_put_str(dict, "event", event_name); ++ timestamp_put(dict); ++ return dict; ++} +diff --git a/qcow2/lib/qapi/qobject-input-visitor.c b/qcow2/lib/qapi/qobject-input-visitor.c +new file mode 100644 +index 00000000..f110a804 +--- /dev/null ++++ b/qcow2/lib/qapi/qobject-input-visitor.c +@@ -0,0 +1,776 @@ ++/* ++ * Input Visitor ++ * ++ * Copyright (C) 2012-2017 Red Hat, Inc. ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include ++#include "qapi/compat-policy.h" ++#include "qapi/error.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qapi/visitor-impl.h" ++#include "qemu/queue.h" ++#include "qapi/qmp/qjson.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qerror.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qemu/cutils.h" ++#include "qemu/keyval.h" ++ ++typedef struct StackObject { ++ const char *name; /* Name of @obj in its parent, if any */ ++ QObject *obj; /* QDict or QList being visited */ ++ void *qapi; /* sanity check that caller uses same pointer */ ++ ++ GHashTable *h; /* If @obj is QDict: unvisited keys */ ++ const QListEntry *entry; /* If @obj is QList: unvisited tail */ ++ unsigned index; /* If @obj is QList: list index of @entry */ ++ ++ QSLIST_ENTRY(StackObject) node; /* parent */ ++} StackObject; ++ ++struct QObjectInputVisitor { ++ Visitor visitor; ++ ++ /* Root of visit at visitor creation. */ ++ QObject *root; ++ bool keyval; /* Assume @root made with keyval_parse() */ ++ ++ /* Stack of objects being visited (all entries will be either ++ * QDict or QList). */ ++ QSLIST_HEAD(, StackObject) stack; ++ ++ GString *errname; /* Accumulator for full_name() */ ++}; ++ ++static QObjectInputVisitor *to_qiv(Visitor *v) ++{ ++ return container_of(v, QObjectInputVisitor, visitor); ++} ++ ++/* ++ * Find the full name of something @qiv is currently visiting. ++ * @qiv is visiting something named @name in the stack of containers ++ * @qiv->stack. ++ * If @n is zero, return its full name. ++ * If @n is positive, return the full name of the @n-th container ++ * counting from the top. The stack of containers must have at least ++ * @n elements. ++ * The returned string is valid until the next full_name_nth(@v) or ++ * destruction of @v. ++ */ ++static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name, ++ int n) ++{ ++ StackObject *so; ++ char buf[32]; ++ ++ if (qiv->errname) { ++ g_string_truncate(qiv->errname, 0); ++ } else { ++ qiv->errname = g_string_new(""); ++ } ++ ++ QSLIST_FOREACH(so , &qiv->stack, node) { ++ if (n) { ++ n--; ++ } else if (qobject_type(so->obj) == QTYPE_QDICT) { ++ g_string_prepend(qiv->errname, name ?: ""); ++ g_string_prepend_c(qiv->errname, '.'); ++ } else { ++ snprintf(buf, sizeof(buf), ++ qiv->keyval ? ".%u" : "[%u]", ++ so->index); ++ g_string_prepend(qiv->errname, buf); ++ } ++ name = so->name; ++ } ++ assert(!n); ++ ++ if (name) { ++ g_string_prepend(qiv->errname, name); ++ } else if (qiv->errname->str[0] == '.') { ++ g_string_erase(qiv->errname, 0, 1); ++ } else if (!qiv->errname->str[0]) { ++ return ""; ++ } ++ ++ return qiv->errname->str; ++} ++ ++static const char *full_name(QObjectInputVisitor *qiv, const char *name) ++{ ++ return full_name_nth(qiv, name, 0); ++} ++ ++static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv, ++ const char *name, ++ bool consume) ++{ ++ StackObject *tos; ++ QObject *qobj; ++ QObject *ret; ++ ++ if (QSLIST_EMPTY(&qiv->stack)) { ++ /* Starting at root, name is ignored. */ ++ assert(qiv->root); ++ return qiv->root; ++ } ++ ++ /* We are in a container; find the next element. */ ++ tos = QSLIST_FIRST(&qiv->stack); ++ qobj = tos->obj; ++ assert(qobj); ++ ++ if (qobject_type(qobj) == QTYPE_QDICT) { ++ assert(name); ++ ret = qdict_get(qobject_to(QDict, qobj), name); ++ if (tos->h && consume && ret) { ++ bool removed = g_hash_table_remove(tos->h, name); ++ assert(removed); ++ } ++ } else { ++ assert(qobject_type(qobj) == QTYPE_QLIST); ++ assert(!name); ++ if (tos->entry) { ++ ret = qlist_entry_obj(tos->entry); ++ if (consume) { ++ tos->entry = qlist_next(tos->entry); ++ } ++ } else { ++ ret = NULL; ++ } ++ if (consume) { ++ tos->index++; ++ } ++ } ++ ++ return ret; ++} ++ ++static QObject *qobject_input_get_object(QObjectInputVisitor *qiv, ++ const char *name, ++ bool consume, Error **errp) ++{ ++ QObject *obj = qobject_input_try_get_object(qiv, name, consume); ++ ++ if (!obj) { ++ error_setg(errp, QERR_MISSING_PARAMETER, full_name(qiv, name)); ++ } ++ return obj; ++} ++ ++static const char *qobject_input_get_keyval(QObjectInputVisitor *qiv, ++ const char *name, ++ Error **errp) ++{ ++ QObject *qobj; ++ QString *qstr; ++ ++ qobj = qobject_input_get_object(qiv, name, true, errp); ++ if (!qobj) { ++ return NULL; ++ } ++ ++ qstr = qobject_to(QString, qobj); ++ if (!qstr) { ++ switch (qobject_type(qobj)) { ++ case QTYPE_QDICT: ++ case QTYPE_QLIST: ++ error_setg(errp, "Parameters '%s.*' are unexpected", ++ full_name(qiv, name)); ++ return NULL; ++ default: ++ /* Non-string scalar (should this be an assertion?) */ ++ error_setg(errp, "Internal error: parameter %s invalid", ++ full_name(qiv, name)); ++ return NULL; ++ } ++ } ++ ++ return qstring_get_str(qstr); ++} ++ ++static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv, ++ const char *name, ++ QObject *obj, void *qapi) ++{ ++ GHashTable *h; ++ StackObject *tos = g_new0(StackObject, 1); ++ QDict *qdict = qobject_to(QDict, obj); ++ QList *qlist = qobject_to(QList, obj); ++ const QDictEntry *entry; ++ ++ assert(obj); ++ tos->name = name; ++ tos->obj = obj; ++ tos->qapi = qapi; ++ ++ if (qdict) { ++ h = g_hash_table_new(g_str_hash, g_str_equal); ++ for (entry = qdict_first(qdict); ++ entry; ++ entry = qdict_next(qdict, entry)) { ++ g_hash_table_insert(h, (void *)qdict_entry_key(entry), NULL); ++ } ++ tos->h = h; ++ } else { ++ assert(qlist); ++ tos->entry = qlist_first(qlist); ++ tos->index = -1; ++ } ++ ++ QSLIST_INSERT_HEAD(&qiv->stack, tos, node); ++ return tos->entry; ++} ++ ++ ++static bool qobject_input_check_struct(Visitor *v, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ GHashTableIter iter; ++ const char *key; ++ ++ assert(tos && !tos->entry); ++ ++ g_hash_table_iter_init(&iter, tos->h); ++ if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) { ++ error_setg(errp, "Parameter '%s' is unexpected", ++ full_name(qiv, key)); ++ return false; ++ } ++ return true; ++} ++ ++static void qobject_input_stack_object_free(StackObject *tos) ++{ ++ if (tos->h) { ++ g_hash_table_unref(tos->h); ++ } ++ ++ g_free(tos); ++} ++ ++static void qobject_input_pop(Visitor *v, void **obj) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ ++ assert(tos && tos->qapi == obj); ++ QSLIST_REMOVE_HEAD(&qiv->stack, node); ++ qobject_input_stack_object_free(tos); ++} ++ ++static bool qobject_input_start_struct(Visitor *v, const char *name, void **obj, ++ size_t size, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ ++ if (obj) { ++ *obj = NULL; ++ } ++ if (!qobj) { ++ return false; ++ } ++ if (qobject_type(qobj) != QTYPE_QDICT) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: object", ++ full_name(qiv, name)); ++ return false; ++ } ++ ++ qobject_input_push(qiv, name, qobj, obj); ++ ++ if (obj) { ++ *obj = g_malloc0(size); ++ } ++ return true; ++} ++ ++static void qobject_input_end_struct(Visitor *v, void **obj) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ ++ assert(qobject_type(tos->obj) == QTYPE_QDICT && tos->h); ++ qobject_input_pop(v, obj); ++} ++ ++ ++static bool qobject_input_start_list(Visitor *v, const char *name, ++ GenericList **list, size_t size, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ const QListEntry *entry; ++ ++ if (list) { ++ *list = NULL; ++ } ++ if (!qobj) { ++ return false; ++ } ++ if (qobject_type(qobj) != QTYPE_QLIST) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: array", ++ full_name(qiv, name)); ++ return false; ++ } ++ ++ entry = qobject_input_push(qiv, name, qobj, list); ++ if (entry && list) { ++ *list = g_malloc0(size); ++ } ++ return true; ++} ++ ++static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail, ++ size_t size) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ ++ assert(tos && qobject_to(QList, tos->obj)); ++ ++ if (!tos->entry) { ++ return NULL; ++ } ++ tail->next = g_malloc0(size); ++ return tail->next; ++} ++ ++static bool qobject_input_check_list(Visitor *v, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ ++ assert(tos && qobject_to(QList, tos->obj)); ++ ++ if (tos->entry) { ++ error_setg(errp, "Only %u list elements expected in %s", ++ tos->index + 1, full_name_nth(qiv, NULL, 1)); ++ return false; ++ } ++ return true; ++} ++ ++static void qobject_input_end_list(Visitor *v, void **obj) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ ++ assert(qobject_type(tos->obj) == QTYPE_QLIST && !tos->h); ++ qobject_input_pop(v, obj); ++} ++ ++static bool qobject_input_start_alternate(Visitor *v, const char *name, ++ GenericAlternate **obj, size_t size, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, false, errp); ++ ++ if (!qobj) { ++ *obj = NULL; ++ return false; ++ } ++ *obj = g_malloc0(size); ++ (*obj)->type = qobject_type(qobj); ++ return true; ++} ++ ++static bool qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ QNum *qnum; ++ ++ if (!qobj) { ++ return false; ++ } ++ qnum = qobject_to(QNum, qobj); ++ if (!qnum || !qnum_get_try_int(qnum, obj)) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: integer", ++ full_name(qiv, name)); ++ return false; ++ } ++ return true; ++} ++ ++static bool qobject_input_type_int64_keyval(Visitor *v, const char *name, ++ int64_t *obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ const char *str = qobject_input_get_keyval(qiv, name, errp); ++ ++ if (!str) { ++ return false; ++ } ++ ++ if (qemu_strtoi64(str, NULL, 0, obj) < 0) { ++ /* TODO report -ERANGE more nicely */ ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ full_name(qiv, name), "integer"); ++ return false; ++ } ++ return true; ++} ++ ++static bool qobject_input_type_uint64(Visitor *v, const char *name, ++ uint64_t *obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ QNum *qnum; ++ int64_t val; ++ ++ if (!qobj) { ++ return false; ++ } ++ qnum = qobject_to(QNum, qobj); ++ if (!qnum) { ++ goto err; ++ } ++ ++ if (qnum_get_try_uint(qnum, obj)) { ++ return true; ++ } ++ ++ /* Need to accept negative values for backward compatibility */ ++ if (qnum_get_try_int(qnum, &val)) { ++ *obj = val; ++ return true; ++ } ++ ++err: ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ full_name(qiv, name), "uint64"); ++ return false; ++} ++ ++static bool qobject_input_type_uint64_keyval(Visitor *v, const char *name, ++ uint64_t *obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ const char *str = qobject_input_get_keyval(qiv, name, errp); ++ ++ if (!str) { ++ return false; ++ } ++ ++ if (qemu_strtou64(str, NULL, 0, obj) < 0) { ++ /* TODO report -ERANGE more nicely */ ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ full_name(qiv, name), "integer"); ++ return false; ++ } ++ return true; ++} ++ ++static bool qobject_input_type_bool(Visitor *v, const char *name, bool *obj, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ QBool *qbool; ++ ++ if (!qobj) { ++ return false; ++ } ++ qbool = qobject_to(QBool, qobj); ++ if (!qbool) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: boolean", ++ full_name(qiv, name)); ++ return false; ++ } ++ ++ *obj = qbool_get_bool(qbool); ++ return true; ++} ++ ++static bool qobject_input_type_bool_keyval(Visitor *v, const char *name, ++ bool *obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ const char *str = qobject_input_get_keyval(qiv, name, errp); ++ ++ if (!str) { ++ return false; ++ } ++ ++ if (!qapi_bool_parse(name, str, obj, NULL)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ full_name(qiv, name), "'on' or 'off'"); ++ return false; ++ } ++ return true; ++} ++ ++static bool qobject_input_type_str(Visitor *v, const char *name, char **obj, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ QString *qstr; ++ ++ *obj = NULL; ++ if (!qobj) { ++ return false; ++ } ++ qstr = qobject_to(QString, qobj); ++ if (!qstr) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: string", ++ full_name(qiv, name)); ++ return false; ++ } ++ ++ *obj = g_strdup(qstring_get_str(qstr)); ++ return true; ++} ++ ++static bool qobject_input_type_str_keyval(Visitor *v, const char *name, ++ char **obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ const char *str = qobject_input_get_keyval(qiv, name, errp); ++ ++ *obj = g_strdup(str); ++ return !!str; ++} ++ ++static bool qobject_input_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ QNum *qnum; ++ ++ if (!qobj) { ++ return false; ++ } ++ qnum = qobject_to(QNum, qobj); ++ if (!qnum) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: number", ++ full_name(qiv, name)); ++ return false; ++ } ++ ++ *obj = qnum_get_double(qnum); ++ return true; ++} ++ ++static bool qobject_input_type_number_keyval(Visitor *v, const char *name, ++ double *obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ const char *str = qobject_input_get_keyval(qiv, name, errp); ++ double val; ++ ++ if (!str) { ++ return false; ++ } ++ ++ if (qemu_strtod_finite(str, NULL, &val)) { ++ /* TODO report -ERANGE more nicely */ ++ error_setg(errp, "Invalid parameter type for '%s', expected: number", ++ full_name(qiv, name)); ++ return false; ++ } ++ ++ *obj = val; ++ return true; ++} ++ ++static bool qobject_input_type_any(Visitor *v, const char *name, QObject **obj, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ ++ *obj = NULL; ++ if (!qobj) { ++ return false; ++ } ++ ++ *obj = qobject_ref(qobj); ++ return true; ++} ++ ++static bool qobject_input_type_null(Visitor *v, const char *name, ++ QNull **obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ ++ *obj = NULL; ++ if (!qobj) { ++ return false; ++ } ++ ++ if (qobject_type(qobj) != QTYPE_QNULL) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: null", ++ full_name(qiv, name)); ++ return false; ++ } ++ *obj = qnull(); ++ return true; ++} ++ ++static bool qobject_input_type_size_keyval(Visitor *v, const char *name, ++ uint64_t *obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ const char *str = qobject_input_get_keyval(qiv, name, errp); ++ ++ if (!str) { ++ return false; ++ } ++ ++ if (qemu_strtosz(str, NULL, obj) < 0) { ++ /* TODO report -ERANGE more nicely */ ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ full_name(qiv, name), "size"); ++ return false; ++ } ++ return true; ++} ++ ++static void qobject_input_optional(Visitor *v, const char *name, bool *present) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_try_get_object(qiv, name, false); ++ ++ if (!qobj) { ++ *present = false; ++ return; ++ } ++ ++ *present = true; ++} ++ ++static bool qobject_input_policy_reject(Visitor *v, const char *name, ++ unsigned special_features, ++ Error **errp) ++{ ++ return !compat_policy_input_ok(special_features, &v->compat_policy, ++ ERROR_CLASS_GENERIC_ERROR, ++ "parameter", name, errp); ++} ++ ++static void qobject_input_free(Visitor *v) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ ++ while (!QSLIST_EMPTY(&qiv->stack)) { ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ ++ QSLIST_REMOVE_HEAD(&qiv->stack, node); ++ qobject_input_stack_object_free(tos); ++ } ++ ++ qobject_unref(qiv->root); ++ if (qiv->errname) { ++ g_string_free(qiv->errname, TRUE); ++ } ++ g_free(qiv); ++} ++ ++static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj) ++{ ++ QObjectInputVisitor *v = g_malloc0(sizeof(*v)); ++ ++ assert(obj); ++ ++ v->visitor.type = VISITOR_INPUT; ++ v->visitor.start_struct = qobject_input_start_struct; ++ v->visitor.check_struct = qobject_input_check_struct; ++ v->visitor.end_struct = qobject_input_end_struct; ++ v->visitor.start_list = qobject_input_start_list; ++ v->visitor.next_list = qobject_input_next_list; ++ v->visitor.check_list = qobject_input_check_list; ++ v->visitor.end_list = qobject_input_end_list; ++ v->visitor.start_alternate = qobject_input_start_alternate; ++ v->visitor.optional = qobject_input_optional; ++ v->visitor.policy_reject = qobject_input_policy_reject; ++ v->visitor.free = qobject_input_free; ++ ++ v->root = qobject_ref(obj); ++ ++ return v; ++} ++ ++Visitor *qobject_input_visitor_new(QObject *obj) ++{ ++ QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); ++ ++ v->visitor.type_int64 = qobject_input_type_int64; ++ v->visitor.type_uint64 = qobject_input_type_uint64; ++ v->visitor.type_bool = qobject_input_type_bool; ++ v->visitor.type_str = qobject_input_type_str; ++ v->visitor.type_number = qobject_input_type_number; ++ v->visitor.type_any = qobject_input_type_any; ++ v->visitor.type_null = qobject_input_type_null; ++ ++ return &v->visitor; ++} ++ ++Visitor *qobject_input_visitor_new_keyval(QObject *obj) ++{ ++ QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); ++ ++ v->visitor.type_int64 = qobject_input_type_int64_keyval; ++ v->visitor.type_uint64 = qobject_input_type_uint64_keyval; ++ v->visitor.type_bool = qobject_input_type_bool_keyval; ++ v->visitor.type_str = qobject_input_type_str_keyval; ++ v->visitor.type_number = qobject_input_type_number_keyval; ++ v->visitor.type_any = qobject_input_type_any; ++ v->visitor.type_null = qobject_input_type_null; ++ v->visitor.type_size = qobject_input_type_size_keyval; ++ v->keyval = true; ++ ++ return &v->visitor; ++} ++ ++Visitor *qobject_input_visitor_new_str(const char *str, ++ const char *implied_key, ++ Error **errp) ++{ ++ bool is_json = str[0] == '{'; ++ QObject *obj; ++ QDict *args; ++ Visitor *v; ++ ++ if (is_json) { ++ obj = qobject_from_json(str, errp); ++ if (!obj) { ++ return NULL; ++ } ++ args = qobject_to(QDict, obj); ++ assert(args); ++ v = qobject_input_visitor_new(QOBJECT(args)); ++ } else { ++ args = keyval_parse(str, implied_key, NULL, errp); ++ if (!args) { ++ return NULL; ++ } ++ v = qobject_input_visitor_new_keyval(QOBJECT(args)); ++ } ++ qobject_unref(args); ++ ++ return v; ++} +diff --git a/qcow2/lib/qapi/qobject-output-visitor.c b/qcow2/lib/qapi/qobject-output-visitor.c +new file mode 100644 +index 00000000..74770edd +--- /dev/null ++++ b/qcow2/lib/qapi/qobject-output-visitor.c +@@ -0,0 +1,280 @@ ++/* ++ * Core Definitions for QAPI/QMP Command Registry ++ * ++ * Copyright (C) 2012-2016 Red Hat, Inc. ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/compat-policy.h" ++#include "qapi/qobject-output-visitor.h" ++#include "qapi/visitor-impl.h" ++#include "qemu/queue.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++ ++typedef struct QStackEntry { ++ QObject *value; ++ void *qapi; /* sanity check that caller uses same pointer */ ++ QSLIST_ENTRY(QStackEntry) node; ++} QStackEntry; ++ ++struct QObjectOutputVisitor { ++ Visitor visitor; ++ ++ QSLIST_HEAD(, QStackEntry) stack; /* Stack of unfinished containers */ ++ QObject *root; /* Root of the output visit */ ++ QObject **result; /* User's storage location for result */ ++}; ++ ++#define qobject_output_add(qov, name, value) \ ++ qobject_output_add_obj(qov, name, QOBJECT(value)) ++#define qobject_output_push(qov, value, qapi) \ ++ qobject_output_push_obj(qov, QOBJECT(value), qapi) ++ ++static QObjectOutputVisitor *to_qov(Visitor *v) ++{ ++ return container_of(v, QObjectOutputVisitor, visitor); ++} ++ ++/* Push @value onto the stack of current QObjects being built */ ++static void qobject_output_push_obj(QObjectOutputVisitor *qov, QObject *value, ++ void *qapi) ++{ ++ QStackEntry *e = g_malloc0(sizeof(*e)); ++ ++ assert(qov->root); ++ assert(value); ++ e->value = value; ++ e->qapi = qapi; ++ QSLIST_INSERT_HEAD(&qov->stack, e, node); ++} ++ ++/* Pop a value off the stack of QObjects being built, and return it. */ ++static QObject *qobject_output_pop(QObjectOutputVisitor *qov, void *qapi) ++{ ++ QStackEntry *e = QSLIST_FIRST(&qov->stack); ++ QObject *value; ++ ++ assert(e); ++ assert(e->qapi == qapi); ++ QSLIST_REMOVE_HEAD(&qov->stack, node); ++ value = e->value; ++ assert(value); ++ g_free(e); ++ return value; ++} ++ ++/* Add @value to the current QObject being built. ++ * If the stack is visiting a dictionary or list, @value is now owned ++ * by that container. Otherwise, @value is now the root. */ ++static void qobject_output_add_obj(QObjectOutputVisitor *qov, const char *name, ++ QObject *value) ++{ ++ QStackEntry *e = QSLIST_FIRST(&qov->stack); ++ QObject *cur = e ? e->value : NULL; ++ ++ if (!cur) { ++ /* Don't allow reuse of visitor on more than one root */ ++ assert(!qov->root); ++ qov->root = value; ++ } else { ++ switch (qobject_type(cur)) { ++ case QTYPE_QDICT: ++ assert(name); ++ qdict_put_obj(qobject_to(QDict, cur), name, value); ++ break; ++ case QTYPE_QLIST: ++ assert(!name); ++ qlist_append_obj(qobject_to(QList, cur), value); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++} ++ ++static bool qobject_output_start_struct(Visitor *v, const char *name, ++ void **obj, size_t unused, Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ QDict *dict = qdict_new(); ++ ++ qobject_output_add(qov, name, dict); ++ qobject_output_push(qov, dict, obj); ++ return true; ++} ++ ++static void qobject_output_end_struct(Visitor *v, void **obj) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ QObject *value = qobject_output_pop(qov, obj); ++ assert(qobject_type(value) == QTYPE_QDICT); ++} ++ ++static bool qobject_output_start_list(Visitor *v, const char *name, ++ GenericList **listp, size_t size, ++ Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ QList *list = qlist_new(); ++ ++ qobject_output_add(qov, name, list); ++ qobject_output_push(qov, list, listp); ++ return true; ++} ++ ++static GenericList *qobject_output_next_list(Visitor *v, GenericList *tail, ++ size_t size) ++{ ++ return tail->next; ++} ++ ++static void qobject_output_end_list(Visitor *v, void **obj) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ QObject *value = qobject_output_pop(qov, obj); ++ assert(qobject_type(value) == QTYPE_QLIST); ++} ++ ++static bool qobject_output_type_int64(Visitor *v, const char *name, ++ int64_t *obj, Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ qobject_output_add(qov, name, qnum_from_int(*obj)); ++ return true; ++} ++ ++static bool qobject_output_type_uint64(Visitor *v, const char *name, ++ uint64_t *obj, Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ qobject_output_add(qov, name, qnum_from_uint(*obj)); ++ return true; ++} ++ ++static bool qobject_output_type_bool(Visitor *v, const char *name, bool *obj, ++ Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ qobject_output_add(qov, name, qbool_from_bool(*obj)); ++ return true; ++} ++ ++static bool qobject_output_type_str(Visitor *v, const char *name, char **obj, ++ Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ if (*obj) { ++ qobject_output_add(qov, name, qstring_from_str(*obj)); ++ } else { ++ qobject_output_add(qov, name, qstring_from_str("")); ++ } ++ return true; ++} ++ ++static bool qobject_output_type_number(Visitor *v, const char *name, ++ double *obj, Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ qobject_output_add(qov, name, qnum_from_double(*obj)); ++ return true; ++} ++ ++static bool qobject_output_type_any(Visitor *v, const char *name, ++ QObject **obj, Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ ++ qobject_output_add_obj(qov, name, qobject_ref(*obj)); ++ return true; ++} ++ ++static bool qobject_output_type_null(Visitor *v, const char *name, ++ QNull **obj, Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ qobject_output_add(qov, name, qnull()); ++ return true; ++} ++ ++static bool qobject_output_policy_skip(Visitor *v, const char *name, ++ unsigned special_features) ++{ ++ CompatPolicy *pol = &v->compat_policy; ++ ++ return ((special_features & 1u << QAPI_DEPRECATED) ++ && pol->deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) ++ || ((special_features & 1u << QAPI_UNSTABLE) ++ && pol->unstable_output == COMPAT_POLICY_OUTPUT_HIDE); ++} ++ ++/* Finish building, and return the root object. ++ * The root object is never null. The caller becomes the object's ++ * owner, and should use qobject_unref() when done with it. */ ++static void qobject_output_complete(Visitor *v, void *opaque) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ ++ /* A visit must have occurred, with each start paired with end. */ ++ assert(qov->root && QSLIST_EMPTY(&qov->stack)); ++ assert(opaque == qov->result); ++ ++ *qov->result = qobject_ref(qov->root); ++ qov->result = NULL; ++} ++ ++static void qobject_output_free(Visitor *v) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ QStackEntry *e; ++ ++ while (!QSLIST_EMPTY(&qov->stack)) { ++ e = QSLIST_FIRST(&qov->stack); ++ QSLIST_REMOVE_HEAD(&qov->stack, node); ++ g_free(e); ++ } ++ ++ qobject_unref(qov->root); ++ g_free(qov); ++} ++ ++Visitor *qobject_output_visitor_new(QObject **result) ++{ ++ QObjectOutputVisitor *v; ++ ++ v = g_malloc0(sizeof(*v)); ++ ++ v->visitor.type = VISITOR_OUTPUT; ++ v->visitor.start_struct = qobject_output_start_struct; ++ v->visitor.end_struct = qobject_output_end_struct; ++ v->visitor.start_list = qobject_output_start_list; ++ v->visitor.next_list = qobject_output_next_list; ++ v->visitor.end_list = qobject_output_end_list; ++ v->visitor.type_int64 = qobject_output_type_int64; ++ v->visitor.type_uint64 = qobject_output_type_uint64; ++ v->visitor.type_bool = qobject_output_type_bool; ++ v->visitor.type_str = qobject_output_type_str; ++ v->visitor.type_number = qobject_output_type_number; ++ v->visitor.type_any = qobject_output_type_any; ++ v->visitor.type_null = qobject_output_type_null; ++ v->visitor.policy_skip = qobject_output_policy_skip; ++ v->visitor.complete = qobject_output_complete; ++ v->visitor.free = qobject_output_free; ++ ++ *result = NULL; ++ v->result = result; ++ ++ return &v->visitor; ++} +diff --git a/qcow2/lib/qapi/string-input-visitor.c b/qcow2/lib/qapi/string-input-visitor.c +new file mode 100644 +index 00000000..3f1b9e9b +--- /dev/null ++++ b/qcow2/lib/qapi/string-input-visitor.c +@@ -0,0 +1,414 @@ ++/* ++ * String parsing visitor ++ * ++ * Copyright Red Hat, Inc. 2012-2016 ++ * ++ * Author: Paolo Bonzini ++ * David Hildenbrand ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi/string-input-visitor.h" ++#include "qapi/visitor-impl.h" ++#include "qapi/qmp/qerror.h" ++#include "qapi/qmp/qnull.h" ++#include "qemu/option.h" ++#include "qemu/cutils.h" ++ ++typedef enum ListMode { ++ /* no list parsing active / no list expected */ ++ LM_NONE, ++ /* we have an unparsed string remaining */ ++ LM_UNPARSED, ++ /* we have an unfinished int64 range */ ++ LM_INT64_RANGE, ++ /* we have an unfinished uint64 range */ ++ LM_UINT64_RANGE, ++ /* we have parsed the string completely and no range is remaining */ ++ LM_END, ++} ListMode; ++ ++/* protect against DOS attacks, limit the amount of elements per range */ ++#define RANGE_MAX_ELEMENTS 65536 ++ ++typedef union RangeElement { ++ int64_t i64; ++ uint64_t u64; ++} RangeElement; ++ ++struct StringInputVisitor ++{ ++ Visitor visitor; ++ ++ /* List parsing state */ ++ ListMode lm; ++ RangeElement rangeNext; ++ RangeElement rangeEnd; ++ const char *unparsed_string; ++ void *list; ++ ++ /* The original string to parse */ ++ const char *string; ++}; ++ ++static StringInputVisitor *to_siv(Visitor *v) ++{ ++ return container_of(v, StringInputVisitor, visitor); ++} ++ ++static bool start_list(Visitor *v, const char *name, GenericList **list, ++ size_t size, Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ assert(siv->lm == LM_NONE); ++ siv->list = list; ++ siv->unparsed_string = siv->string; ++ ++ if (!siv->string[0]) { ++ if (list) { ++ *list = NULL; ++ } ++ siv->lm = LM_END; ++ } else { ++ if (list) { ++ *list = g_malloc0(size); ++ } ++ siv->lm = LM_UNPARSED; ++ } ++ return true; ++} ++ ++static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ switch (siv->lm) { ++ case LM_END: ++ return NULL; ++ case LM_INT64_RANGE: ++ case LM_UINT64_RANGE: ++ case LM_UNPARSED: ++ /* we have an unparsed string or something left in a range */ ++ break; ++ default: ++ abort(); ++ } ++ ++ tail->next = g_malloc0(size); ++ return tail->next; ++} ++ ++static bool check_list(Visitor *v, Error **errp) ++{ ++ const StringInputVisitor *siv = to_siv(v); ++ ++ switch (siv->lm) { ++ case LM_INT64_RANGE: ++ case LM_UINT64_RANGE: ++ case LM_UNPARSED: ++ error_setg(errp, "Fewer list elements expected"); ++ return false; ++ case LM_END: ++ return true; ++ default: ++ abort(); ++ } ++} ++ ++static void end_list(Visitor *v, void **obj) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ assert(siv->lm != LM_NONE); ++ assert(siv->list == obj); ++ siv->list = NULL; ++ siv->unparsed_string = NULL; ++ siv->lm = LM_NONE; ++} ++ ++static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj) ++{ ++ const char *endptr; ++ int64_t start, end; ++ ++ /* parse a simple int64 or range */ ++ if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) { ++ return -EINVAL; ++ } ++ end = start; ++ ++ switch (endptr[0]) { ++ case '\0': ++ siv->unparsed_string = endptr; ++ break; ++ case ',': ++ siv->unparsed_string = endptr + 1; ++ break; ++ case '-': ++ /* parse the end of the range */ ++ if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) { ++ return -EINVAL; ++ } ++ if (start > end || end - start >= RANGE_MAX_ELEMENTS) { ++ return -EINVAL; ++ } ++ switch (endptr[0]) { ++ case '\0': ++ siv->unparsed_string = endptr; ++ break; ++ case ',': ++ siv->unparsed_string = endptr + 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* we have a proper range (with maybe only one element) */ ++ siv->lm = LM_INT64_RANGE; ++ siv->rangeNext.i64 = start; ++ siv->rangeEnd.i64 = end; ++ return 0; ++} ++ ++static bool parse_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ int64_t val; ++ ++ switch (siv->lm) { ++ case LM_NONE: ++ /* just parse a simple int64, bail out if not completely consumed */ ++ if (qemu_strtoi64(siv->string, NULL, 0, &val)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ name ? name : "null", "int64"); ++ return false; ++ } ++ *obj = val; ++ return true; ++ case LM_UNPARSED: ++ if (try_parse_int64_list_entry(siv, obj)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", ++ "list of int64 values or ranges"); ++ return false; ++ } ++ assert(siv->lm == LM_INT64_RANGE); ++ /* fall through */ ++ case LM_INT64_RANGE: ++ /* return the next element in the range */ ++ assert(siv->rangeNext.i64 <= siv->rangeEnd.i64); ++ *obj = siv->rangeNext.i64++; ++ ++ if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) { ++ /* end of range, check if there is more to parse */ ++ siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END; ++ } ++ return true; ++ case LM_END: ++ error_setg(errp, "Fewer list elements expected"); ++ return false; ++ default: ++ abort(); ++ } ++} ++ ++static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj) ++{ ++ const char *endptr; ++ uint64_t start, end; ++ ++ /* parse a simple uint64 or range */ ++ if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) { ++ return -EINVAL; ++ } ++ end = start; ++ ++ switch (endptr[0]) { ++ case '\0': ++ siv->unparsed_string = endptr; ++ break; ++ case ',': ++ siv->unparsed_string = endptr + 1; ++ break; ++ case '-': ++ /* parse the end of the range */ ++ if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) { ++ return -EINVAL; ++ } ++ if (start > end || end - start >= RANGE_MAX_ELEMENTS) { ++ return -EINVAL; ++ } ++ switch (endptr[0]) { ++ case '\0': ++ siv->unparsed_string = endptr; ++ break; ++ case ',': ++ siv->unparsed_string = endptr + 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* we have a proper range (with maybe only one element) */ ++ siv->lm = LM_UINT64_RANGE; ++ siv->rangeNext.u64 = start; ++ siv->rangeEnd.u64 = end; ++ return 0; ++} ++ ++static bool parse_type_uint64(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ uint64_t val; ++ ++ switch (siv->lm) { ++ case LM_NONE: ++ /* just parse a simple uint64, bail out if not completely consumed */ ++ if (qemu_strtou64(siv->string, NULL, 0, &val)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", ++ "uint64"); ++ return false; ++ } ++ *obj = val; ++ return true; ++ case LM_UNPARSED: ++ if (try_parse_uint64_list_entry(siv, obj)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", ++ "list of uint64 values or ranges"); ++ return false; ++ } ++ assert(siv->lm == LM_UINT64_RANGE); ++ /* fall through */ ++ case LM_UINT64_RANGE: ++ /* return the next element in the range */ ++ assert(siv->rangeNext.u64 <= siv->rangeEnd.u64); ++ *obj = siv->rangeNext.u64++; ++ ++ if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) { ++ /* end of range, check if there is more to parse */ ++ siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END; ++ } ++ return true; ++ case LM_END: ++ error_setg(errp, "Fewer list elements expected"); ++ return false; ++ default: ++ abort(); ++ } ++} ++ ++static bool parse_type_size(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ uint64_t val; ++ ++ assert(siv->lm == LM_NONE); ++ if (!parse_option_size(name, siv->string, &val, errp)) { ++ return false; ++ } ++ ++ *obj = val; ++ return true; ++} ++ ++static bool parse_type_bool(Visitor *v, const char *name, bool *obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ assert(siv->lm == LM_NONE); ++ return qapi_bool_parse(name ? name : "null", siv->string, obj, errp); ++} ++ ++static bool parse_type_str(Visitor *v, const char *name, char **obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ assert(siv->lm == LM_NONE); ++ *obj = g_strdup(siv->string); ++ return true; ++} ++ ++static bool parse_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ double val; ++ ++ assert(siv->lm == LM_NONE); ++ if (qemu_strtod_finite(siv->string, NULL, &val)) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: number", ++ name ? name : "null"); ++ return false; ++ } ++ ++ *obj = val; ++ return true; ++} ++ ++static bool parse_type_null(Visitor *v, const char *name, QNull **obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ assert(siv->lm == LM_NONE); ++ *obj = NULL; ++ ++ if (siv->string[0]) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: null", ++ name ? name : "null"); ++ return false; ++ } ++ ++ *obj = qnull(); ++ return true; ++} ++ ++static void string_input_free(Visitor *v) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ g_free(siv); ++} ++ ++Visitor *string_input_visitor_new(const char *str) ++{ ++ StringInputVisitor *v; ++ ++ assert(str); ++ v = g_malloc0(sizeof(*v)); ++ ++ v->visitor.type = VISITOR_INPUT; ++ v->visitor.type_int64 = parse_type_int64; ++ v->visitor.type_uint64 = parse_type_uint64; ++ v->visitor.type_size = parse_type_size; ++ v->visitor.type_bool = parse_type_bool; ++ v->visitor.type_str = parse_type_str; ++ v->visitor.type_number = parse_type_number; ++ v->visitor.type_null = parse_type_null; ++ v->visitor.start_list = start_list; ++ v->visitor.next_list = next_list; ++ v->visitor.check_list = check_list; ++ v->visitor.end_list = end_list; ++ v->visitor.free = string_input_free; ++ ++ v->string = str; ++ v->lm = LM_NONE; ++ return &v->visitor; ++} +diff --git a/qcow2/lib/qapi/string-output-visitor.c b/qcow2/lib/qapi/string-output-visitor.c +new file mode 100644 +index 00000000..5115536b +--- /dev/null ++++ b/qcow2/lib/qapi/string-output-visitor.c +@@ -0,0 +1,451 @@ ++/* ++ * String printing Visitor ++ * ++ * Copyright Red Hat, Inc. 2012-2016 ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++#include "qapi/string-output-visitor.h" ++#include "qapi/visitor-impl.h" ++#include ++#include "qemu/range.h" ++ ++enum ListMode { ++ LM_NONE, /* not traversing a list of repeated options */ ++ LM_STARTED, /* next_list() ready to be called */ ++ ++ LM_IN_PROGRESS, /* next_list() has been called. ++ * ++ * Generating the next list link will consume the most ++ * recently parsed QemuOpt instance of the repeated ++ * option. ++ * ++ * Parsing a value into the list link will examine the ++ * next QemuOpt instance of the repeated option, and ++ * possibly enter LM_SIGNED_INTERVAL or ++ * LM_UNSIGNED_INTERVAL. ++ */ ++ ++ LM_SIGNED_INTERVAL, /* next_list() has been called. ++ * ++ * Generating the next list link will consume the most ++ * recently stored element from the signed interval, ++ * parsed from the most recent QemuOpt instance of the ++ * repeated option. This may consume QemuOpt itself ++ * and return to LM_IN_PROGRESS. ++ * ++ * Parsing a value into the list link will store the ++ * next element of the signed interval. ++ */ ++ ++ LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */ ++ ++ LM_END, /* next_list() called, about to see last element. */ ++}; ++ ++typedef enum ListMode ListMode; ++ ++struct StringOutputVisitor ++{ ++ Visitor visitor; ++ bool human; ++ GString *string; ++ char **result; ++ ListMode list_mode; ++ union { ++ int64_t s; ++ uint64_t u; ++ } range_start, range_end; ++ GList *ranges; ++ void *list; /* Only needed for sanity checking the caller */ ++ unsigned int struct_nesting; ++}; ++ ++static StringOutputVisitor *to_sov(Visitor *v) ++{ ++ return container_of(v, StringOutputVisitor, visitor); ++} ++ ++static void string_output_set(StringOutputVisitor *sov, char *string) ++{ ++ switch (sov->list_mode) { ++ case LM_STARTED: ++ sov->list_mode = LM_IN_PROGRESS; ++ /* fall through */ ++ case LM_NONE: ++ if (sov->string) { ++ g_string_free(sov->string, true); ++ } ++ sov->string = g_string_new(string); ++ g_free(string); ++ break; ++ ++ case LM_IN_PROGRESS: ++ case LM_END: ++ g_string_append(sov->string, ", "); ++ g_string_append(sov->string, string); ++ break; ++ ++ default: ++ abort(); ++ } ++} ++ ++static void string_output_append(StringOutputVisitor *sov, int64_t a) ++{ ++ Range *r = g_malloc0(sizeof(*r)); ++ ++ range_set_bounds(r, a, a); ++ sov->ranges = range_list_insert(sov->ranges, r); ++} ++ ++static void string_output_append_range(StringOutputVisitor *sov, ++ int64_t s, int64_t e) ++{ ++ Range *r = g_malloc0(sizeof(*r)); ++ ++ range_set_bounds(r, s, e); ++ sov->ranges = range_list_insert(sov->ranges, r); ++} ++ ++static void format_string(StringOutputVisitor *sov, Range *r, bool next, ++ bool human) ++{ ++ if (range_lob(r) != range_upb(r)) { ++ if (human) { ++ g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64, ++ range_lob(r), range_upb(r)); ++ ++ } else { ++ g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64, ++ range_lob(r), range_upb(r)); ++ } ++ } else { ++ if (human) { ++ g_string_append_printf(sov->string, "0x%" PRIx64, range_lob(r)); ++ } else { ++ g_string_append_printf(sov->string, "%" PRId64, range_lob(r)); ++ } ++ } ++ if (next) { ++ g_string_append(sov->string, ","); ++ } ++} ++ ++static bool print_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ GList *l; ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ switch (sov->list_mode) { ++ case LM_NONE: ++ string_output_append(sov, *obj); ++ break; ++ ++ case LM_STARTED: ++ sov->range_start.s = *obj; ++ sov->range_end.s = *obj; ++ sov->list_mode = LM_IN_PROGRESS; ++ return true; ++ ++ case LM_IN_PROGRESS: ++ if (sov->range_end.s + 1 == *obj) { ++ sov->range_end.s++; ++ } else { ++ if (sov->range_start.s == sov->range_end.s) { ++ string_output_append(sov, sov->range_end.s); ++ } else { ++ assert(sov->range_start.s < sov->range_end.s); ++ string_output_append_range(sov, sov->range_start.s, ++ sov->range_end.s); ++ } ++ ++ sov->range_start.s = *obj; ++ sov->range_end.s = *obj; ++ } ++ return true; ++ ++ case LM_END: ++ if (sov->range_end.s + 1 == *obj) { ++ sov->range_end.s++; ++ assert(sov->range_start.s < sov->range_end.s); ++ string_output_append_range(sov, sov->range_start.s, ++ sov->range_end.s); ++ } else { ++ if (sov->range_start.s == sov->range_end.s) { ++ string_output_append(sov, sov->range_end.s); ++ } else { ++ assert(sov->range_start.s < sov->range_end.s); ++ ++ string_output_append_range(sov, sov->range_start.s, ++ sov->range_end.s); ++ } ++ string_output_append(sov, *obj); ++ } ++ break; ++ ++ default: ++ abort(); ++ } ++ ++ l = sov->ranges; ++ while (l) { ++ Range *r = l->data; ++ format_string(sov, r, l->next != NULL, false); ++ l = l->next; ++ } ++ ++ if (sov->human) { ++ l = sov->ranges; ++ g_string_append(sov->string, " ("); ++ while (l) { ++ Range *r = l->data; ++ format_string(sov, r, l->next != NULL, true); ++ l = l->next; ++ } ++ g_string_append(sov->string, ")"); ++ } ++ ++ return true; ++} ++ ++static bool print_type_uint64(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp) ++{ ++ /* FIXME: print_type_int64 mishandles values over INT64_MAX */ ++ int64_t i = *obj; ++ return print_type_int64(v, name, &i, errp); ++} ++ ++static bool print_type_size(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ uint64_t val; ++ char *out, *psize; ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ if (!sov->human) { ++ out = g_strdup_printf("%"PRIu64, *obj); ++ string_output_set(sov, out); ++ return true; ++ } ++ ++ val = *obj; ++ psize = size_to_str(val); ++ out = g_strdup_printf("%"PRIu64" (%s)", val, psize); ++ string_output_set(sov, out); ++ ++ g_free(psize); ++ return true; ++} ++ ++static bool print_type_bool(Visitor *v, const char *name, bool *obj, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ string_output_set(sov, g_strdup(*obj ? "true" : "false")); ++ return true; ++} ++ ++static bool print_type_str(Visitor *v, const char *name, char **obj, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ char *out; ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ if (sov->human) { ++ out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup(""); ++ } else { ++ out = g_strdup(*obj ? *obj : ""); ++ } ++ string_output_set(sov, out); ++ return true; ++} ++ ++static bool print_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ string_output_set(sov, g_strdup_printf("%.17g", *obj)); ++ return true; ++} ++ ++static bool print_type_null(Visitor *v, const char *name, QNull **obj, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ char *out; ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ if (sov->human) { ++ out = g_strdup(""); ++ } else { ++ out = g_strdup(""); ++ } ++ string_output_set(sov, out); ++ return true; ++} ++ ++static bool start_struct(Visitor *v, const char *name, void **obj, ++ size_t size, Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ sov->struct_nesting++; ++ return true; ++} ++ ++static void end_struct(Visitor *v, void **obj) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ if (--sov->struct_nesting) { ++ return; ++ } ++ ++ /* TODO actually print struct fields */ ++ string_output_set(sov, g_strdup("")); ++} ++ ++static bool ++start_list(Visitor *v, const char *name, GenericList **list, size_t size, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ /* we can't traverse a list in a list */ ++ assert(sov->list_mode == LM_NONE); ++ /* We don't support visits without a list */ ++ assert(list); ++ sov->list = list; ++ /* List handling is only needed if there are at least two elements */ ++ if (*list && (*list)->next) { ++ sov->list_mode = LM_STARTED; ++ } ++ return true; ++} ++ ++static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ GenericList *ret = tail->next; ++ ++ if (sov->struct_nesting) { ++ return ret; ++ } ++ ++ if (ret && !ret->next) { ++ sov->list_mode = LM_END; ++ } ++ return ret; ++} ++ ++static void end_list(Visitor *v, void **obj) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ if (sov->struct_nesting) { ++ return; ++ } ++ ++ assert(sov->list == obj); ++ assert(sov->list_mode == LM_STARTED || ++ sov->list_mode == LM_END || ++ sov->list_mode == LM_NONE || ++ sov->list_mode == LM_IN_PROGRESS); ++ sov->list_mode = LM_NONE; ++} ++ ++static void string_output_complete(Visitor *v, void *opaque) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ assert(opaque == sov->result); ++ *sov->result = g_string_free(sov->string, false); ++ sov->string = NULL; ++} ++ ++static void free_range(void *range, void *dummy) ++{ ++ g_free(range); ++} ++ ++static void string_output_free(Visitor *v) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ if (sov->string) { ++ g_string_free(sov->string, true); ++ } ++ ++ g_list_foreach(sov->ranges, free_range, NULL); ++ g_list_free(sov->ranges); ++ g_free(sov); ++} ++ ++Visitor *string_output_visitor_new(bool human, char **result) ++{ ++ StringOutputVisitor *v; ++ ++ v = g_malloc0(sizeof(*v)); ++ ++ v->string = g_string_new(NULL); ++ v->human = human; ++ v->result = result; ++ *result = NULL; ++ ++ v->visitor.type = VISITOR_OUTPUT; ++ v->visitor.type_int64 = print_type_int64; ++ v->visitor.type_uint64 = print_type_uint64; ++ v->visitor.type_size = print_type_size; ++ v->visitor.type_bool = print_type_bool; ++ v->visitor.type_str = print_type_str; ++ v->visitor.type_number = print_type_number; ++ v->visitor.type_null = print_type_null; ++ v->visitor.start_struct = start_struct; ++ v->visitor.end_struct = end_struct; ++ v->visitor.start_list = start_list; ++ v->visitor.next_list = next_list; ++ v->visitor.end_list = end_list; ++ v->visitor.complete = string_output_complete; ++ v->visitor.free = string_output_free; ++ ++ return &v->visitor; ++} +diff --git a/qcow2/lib/qcow2-bitmap.c b/qcow2/lib/qcow2-bitmap.c +new file mode 100644 +index 00000000..256ec998 +--- /dev/null ++++ b/qcow2/lib/qcow2-bitmap.c +@@ -0,0 +1,1809 @@ ++/* ++ * Bitmaps for the QCOW version 2 format ++ * ++ * Copyright (c) 2014-2017 Vladimir Sementsov-Ogievskiy ++ * ++ * This file is derived from qcow2-snapshot.c, original copyright: ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/block-io.h" ++#include "block/dirty-bitmap.h" ++#include "qapi/error.h" ++#include "qemu/cutils.h" ++ ++#include "qcow2.h" ++ ++/* NOTICE: BME here means Bitmaps Extension and used as a namespace for ++ * _internal_ constants. Please do not use this _internal_ abbreviation for ++ * other needs and/or outside of this file. */ ++ ++/* Bitmap directory entry constraints */ ++#define BME_MAX_TABLE_SIZE 0x8000000 ++#define BME_MAX_PHYS_SIZE 0x20000000 /* restrict BdrvDirtyBitmap size in RAM */ ++#define BME_MAX_GRANULARITY_BITS 31 ++#define BME_MIN_GRANULARITY_BITS 9 ++#define BME_MAX_NAME_SIZE 1023 ++ ++/* Size of bitmap table entries */ ++#define BME_TABLE_ENTRY_SIZE (sizeof(uint64_t)) ++ ++QEMU_BUILD_BUG_ON(BME_MAX_NAME_SIZE != BDRV_BITMAP_MAX_NAME_SIZE); ++ ++#if BME_MAX_TABLE_SIZE * 8ULL > INT_MAX ++#error In the code bitmap table physical size assumed to fit into int ++#endif ++ ++/* Bitmap directory entry flags */ ++#define BME_RESERVED_FLAGS 0xfffffffcU ++#define BME_FLAG_IN_USE (1U << 0) ++#define BME_FLAG_AUTO (1U << 1) ++ ++/* bits [1, 8] U [56, 63] are reserved */ ++#define BME_TABLE_ENTRY_RESERVED_MASK 0xff000000000001feULL ++#define BME_TABLE_ENTRY_OFFSET_MASK 0x00fffffffffffe00ULL ++#define BME_TABLE_ENTRY_FLAG_ALL_ONES (1ULL << 0) ++ ++typedef struct QEMU_PACKED Qcow2BitmapDirEntry { ++ /* header is 8 byte aligned */ ++ uint64_t bitmap_table_offset; ++ ++ uint32_t bitmap_table_size; ++ uint32_t flags; ++ ++ uint8_t type; ++ uint8_t granularity_bits; ++ uint16_t name_size; ++ uint32_t extra_data_size; ++ /* extra data follows */ ++ /* name follows */ ++} Qcow2BitmapDirEntry; ++ ++typedef struct Qcow2BitmapTable { ++ uint64_t offset; ++ uint32_t size; /* number of 64bit entries */ ++ QSIMPLEQ_ENTRY(Qcow2BitmapTable) entry; ++} Qcow2BitmapTable; ++ ++typedef struct Qcow2Bitmap { ++ Qcow2BitmapTable table; ++ uint32_t flags; ++ uint8_t granularity_bits; ++ char *name; ++ ++ BdrvDirtyBitmap *dirty_bitmap; ++ ++ QSIMPLEQ_ENTRY(Qcow2Bitmap) entry; ++} Qcow2Bitmap; ++typedef QSIMPLEQ_HEAD(Qcow2BitmapList, Qcow2Bitmap) Qcow2BitmapList; ++ ++typedef enum BitmapType { ++ BT_DIRTY_TRACKING_BITMAP = 1 ++} BitmapType; ++ ++static inline bool can_write(BlockDriverState *bs) ++{ ++ return !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE); ++} ++ ++static int GRAPH_RDLOCK update_header_sync(BlockDriverState *bs) ++{ ++ int ret; ++ ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return bdrv_flush(bs->file->bs); ++} ++ ++static inline void bitmap_table_bswap_be(uint64_t *bitmap_table, size_t size) ++{ ++ size_t i; ++ ++ for (i = 0; i < size; ++i) { ++ bitmap_table[i] = cpu_to_be64(bitmap_table[i]); ++ } ++} ++ ++static int check_table_entry(uint64_t entry, int cluster_size) ++{ ++ uint64_t offset; ++ ++ if (entry & BME_TABLE_ENTRY_RESERVED_MASK) { ++ return -EINVAL; ++ } ++ ++ offset = entry & BME_TABLE_ENTRY_OFFSET_MASK; ++ if (offset != 0) { ++ /* if offset specified, bit 0 is reserved */ ++ if (entry & BME_TABLE_ENTRY_FLAG_ALL_ONES) { ++ return -EINVAL; ++ } ++ ++ if (offset % cluster_size != 0) { ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int64_t get_bitmap_bytes_needed(int64_t len, uint32_t granularity) ++{ ++ int64_t num_bits = DIV_ROUND_UP(len, granularity); ++ ++ return DIV_ROUND_UP(num_bits, 8); ++} ++ ++static int GRAPH_RDLOCK ++check_constraints_on_bitmap(BlockDriverState *bs, const char *name, ++ uint32_t granularity, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int granularity_bits = ctz32(granularity); ++ int64_t len = bdrv_getlength(bs); ++ int64_t bitmap_bytes; ++ ++ assert(granularity > 0); ++ assert((granularity & (granularity - 1)) == 0); ++ ++ if (len < 0) { ++ error_setg_errno(errp, -len, "Failed to get size of '%s'", ++ bdrv_get_device_or_node_name(bs)); ++ return len; ++ } ++ ++ if (granularity_bits > BME_MAX_GRANULARITY_BITS) { ++ error_setg(errp, "Granularity exceeds maximum (%llu bytes)", ++ 1ULL << BME_MAX_GRANULARITY_BITS); ++ return -EINVAL; ++ } ++ if (granularity_bits < BME_MIN_GRANULARITY_BITS) { ++ error_setg(errp, "Granularity is under minimum (%llu bytes)", ++ 1ULL << BME_MIN_GRANULARITY_BITS); ++ return -EINVAL; ++ } ++ ++ bitmap_bytes = get_bitmap_bytes_needed(len, granularity); ++ if ((bitmap_bytes > (uint64_t)BME_MAX_PHYS_SIZE) || ++ (bitmap_bytes > (uint64_t)BME_MAX_TABLE_SIZE * s->cluster_size)) ++ { ++ error_setg(errp, "Too much space will be occupied by the bitmap. " ++ "Use larger granularity"); ++ return -EINVAL; ++ } ++ ++ if (strlen(name) > BME_MAX_NAME_SIZE) { ++ error_setg(errp, "Name length exceeds maximum (%u characters)", ++ BME_MAX_NAME_SIZE); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void GRAPH_RDLOCK ++clear_bitmap_table(BlockDriverState *bs, uint64_t *bitmap_table, ++ uint32_t bitmap_table_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i; ++ ++ for (i = 0; i < bitmap_table_size; ++i) { ++ uint64_t addr = bitmap_table[i] & BME_TABLE_ENTRY_OFFSET_MASK; ++ if (!addr) { ++ continue; ++ } ++ ++ qcow2_free_clusters(bs, addr, s->cluster_size, QCOW2_DISCARD_ALWAYS); ++ bitmap_table[i] = 0; ++ } ++} ++ ++static int GRAPH_RDLOCK ++bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, ++ uint64_t **bitmap_table) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ uint32_t i; ++ uint64_t *table; ++ ++ assert(tb->size != 0); ++ table = g_try_new(uint64_t, tb->size); ++ if (table == NULL) { ++ return -ENOMEM; ++ } ++ ++ assert(tb->size <= BME_MAX_TABLE_SIZE); ++ ret = bdrv_pread(bs->file, tb->offset, tb->size * BME_TABLE_ENTRY_SIZE, ++ table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (i = 0; i < tb->size; ++i) { ++ table[i] = be64_to_cpu(table[i]); ++ ret = check_table_entry(table[i], s->cluster_size); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ *bitmap_table = table; ++ return 0; ++ ++fail: ++ g_free(table); ++ ++ return ret; ++} ++ ++static int GRAPH_RDLOCK ++free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) ++{ ++ int ret; ++ uint64_t *bitmap_table; ++ ++ ret = bitmap_table_load(bs, tb, &bitmap_table); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ clear_bitmap_table(bs, bitmap_table, tb->size); ++ qcow2_free_clusters(bs, tb->offset, tb->size * BME_TABLE_ENTRY_SIZE, ++ QCOW2_DISCARD_OTHER); ++ g_free(bitmap_table); ++ ++ tb->offset = 0; ++ tb->size = 0; ++ ++ return 0; ++} ++ ++/* load_bitmap_data ++ * @bitmap_table entries must satisfy specification constraints. ++ * @bitmap must be cleared */ ++static int coroutine_fn GRAPH_RDLOCK ++load_bitmap_data(BlockDriverState *bs, const uint64_t *bitmap_table, ++ uint32_t bitmap_table_size, BdrvDirtyBitmap *bitmap) ++{ ++ int ret = 0; ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t offset, limit; ++ uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); ++ uint8_t *buf = NULL; ++ uint64_t i, tab_size = ++ size_to_clusters(s, ++ bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size)); ++ ++ if (tab_size != bitmap_table_size || tab_size > BME_MAX_TABLE_SIZE) { ++ return -EINVAL; ++ } ++ ++ buf = g_malloc(s->cluster_size); ++ limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap); ++ for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) { ++ uint64_t count = MIN(bm_size - offset, limit); ++ uint64_t entry = bitmap_table[i]; ++ uint64_t data_offset = entry & BME_TABLE_ENTRY_OFFSET_MASK; ++ ++ assert(check_table_entry(entry, s->cluster_size) == 0); ++ ++ if (data_offset == 0) { ++ if (entry & BME_TABLE_ENTRY_FLAG_ALL_ONES) { ++ bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count, ++ false); ++ } else { ++ /* No need to deserialize zeros because the dirty bitmap is ++ * already cleared */ ++ } ++ } else { ++ ret = bdrv_co_pread(bs->file, data_offset, s->cluster_size, buf, 0); ++ if (ret < 0) { ++ goto finish; ++ } ++ bdrv_dirty_bitmap_deserialize_part(bitmap, buf, offset, count, ++ false); ++ } ++ } ++ ret = 0; ++ ++ bdrv_dirty_bitmap_deserialize_finish(bitmap); ++ ++finish: ++ g_free(buf); ++ ++ return ret; ++} ++ ++static coroutine_fn GRAPH_RDLOCK ++BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs, ++ Qcow2Bitmap *bm, Error **errp) ++{ ++ int ret; ++ uint64_t *bitmap_table = NULL; ++ uint32_t granularity; ++ BdrvDirtyBitmap *bitmap = NULL; ++ ++ granularity = 1U << bm->granularity_bits; ++ bitmap = bdrv_create_dirty_bitmap(bs, granularity, bm->name, errp); ++ if (bitmap == NULL) { ++ goto fail; ++ } ++ ++ if (bm->flags & BME_FLAG_IN_USE) { ++ /* Data is unusable, skip loading it */ ++ return bitmap; ++ } ++ ++ ret = bitmap_table_load(bs, &bm->table, &bitmap_table); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Could not read bitmap_table table from image for " ++ "bitmap '%s'", bm->name); ++ goto fail; ++ } ++ ++ ret = load_bitmap_data(bs, bitmap_table, bm->table.size, bitmap); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not read bitmap '%s' from image", ++ bm->name); ++ goto fail; ++ } ++ ++ g_free(bitmap_table); ++ return bitmap; ++ ++fail: ++ g_free(bitmap_table); ++ if (bitmap != NULL) { ++ bdrv_release_dirty_bitmap(bitmap); ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Bitmap List ++ */ ++ ++/* ++ * Bitmap List private functions ++ * Only Bitmap List knows about bitmap directory structure in Qcow2. ++ */ ++ ++static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry) ++{ ++ entry->bitmap_table_offset = be64_to_cpu(entry->bitmap_table_offset); ++ entry->bitmap_table_size = be32_to_cpu(entry->bitmap_table_size); ++ entry->flags = be32_to_cpu(entry->flags); ++ entry->name_size = be16_to_cpu(entry->name_size); ++ entry->extra_data_size = be32_to_cpu(entry->extra_data_size); ++} ++ ++static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry) ++{ ++ entry->bitmap_table_offset = cpu_to_be64(entry->bitmap_table_offset); ++ entry->bitmap_table_size = cpu_to_be32(entry->bitmap_table_size); ++ entry->flags = cpu_to_be32(entry->flags); ++ entry->name_size = cpu_to_be16(entry->name_size); ++ entry->extra_data_size = cpu_to_be32(entry->extra_data_size); ++} ++ ++static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size) ++{ ++ int size = sizeof(Qcow2BitmapDirEntry) + name_size + extra_data_size; ++ return ROUND_UP(size, 8); ++} ++ ++static inline int dir_entry_size(Qcow2BitmapDirEntry *entry) ++{ ++ return calc_dir_entry_size(entry->name_size, entry->extra_data_size); ++} ++ ++static inline const char *dir_entry_name_field(Qcow2BitmapDirEntry *entry) ++{ ++ return (const char *)(entry + 1) + entry->extra_data_size; ++} ++ ++static inline char *dir_entry_copy_name(Qcow2BitmapDirEntry *entry) ++{ ++ const char *name_field = dir_entry_name_field(entry); ++ return g_strndup(name_field, entry->name_size); ++} ++ ++static inline Qcow2BitmapDirEntry *next_dir_entry(Qcow2BitmapDirEntry *entry) ++{ ++ return (Qcow2BitmapDirEntry *)((uint8_t *)entry + dir_entry_size(entry)); ++} ++ ++static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t phys_bitmap_bytes; ++ int64_t len; ++ ++ bool fail = (entry->bitmap_table_size == 0) || ++ (entry->bitmap_table_offset == 0) || ++ (entry->bitmap_table_offset % s->cluster_size) || ++ (entry->bitmap_table_size > BME_MAX_TABLE_SIZE) || ++ (entry->granularity_bits > BME_MAX_GRANULARITY_BITS) || ++ (entry->granularity_bits < BME_MIN_GRANULARITY_BITS) || ++ (entry->flags & BME_RESERVED_FLAGS) || ++ (entry->name_size > BME_MAX_NAME_SIZE) || ++ (entry->type != BT_DIRTY_TRACKING_BITMAP); ++ ++ if (fail) { ++ return -EINVAL; ++ } ++ ++ phys_bitmap_bytes = (uint64_t)entry->bitmap_table_size * s->cluster_size; ++ len = bdrv_getlength(bs); ++ ++ if (len < 0) { ++ return len; ++ } ++ ++ if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) { ++ return -EINVAL; ++ } ++ ++ if (!(entry->flags & BME_FLAG_IN_USE) && ++ (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits))) ++ { ++ /* ++ * We've loaded a valid bitmap (IN_USE not set) or we are going to ++ * store a valid bitmap, but the allocated bitmap table size is not ++ * enough to store this bitmap. ++ * ++ * Note, that it's OK to have an invalid bitmap with invalid size due ++ * to a bitmap that was not correctly saved after image resize. ++ */ ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static inline void bitmap_directory_to_be(uint8_t *dir, size_t size) ++{ ++ uint8_t *end = dir + size; ++ while (dir < end) { ++ Qcow2BitmapDirEntry *e = (Qcow2BitmapDirEntry *)dir; ++ dir += dir_entry_size(e); ++ ++ bitmap_dir_entry_to_be(e); ++ } ++} ++ ++/* ++ * Bitmap List public functions ++ */ ++ ++static void bitmap_free(Qcow2Bitmap *bm) ++{ ++ if (bm == NULL) { ++ return; ++ } ++ ++ g_free(bm->name); ++ g_free(bm); ++} ++ ++static void bitmap_list_free(Qcow2BitmapList *bm_list) ++{ ++ Qcow2Bitmap *bm; ++ ++ if (bm_list == NULL) { ++ return; ++ } ++ ++ while ((bm = QSIMPLEQ_FIRST(bm_list)) != NULL) { ++ QSIMPLEQ_REMOVE_HEAD(bm_list, entry); ++ bitmap_free(bm); ++ } ++ ++ g_free(bm_list); ++} ++ ++static Qcow2BitmapList *bitmap_list_new(void) ++{ ++ Qcow2BitmapList *bm_list = g_new(Qcow2BitmapList, 1); ++ QSIMPLEQ_INIT(bm_list); ++ ++ return bm_list; ++} ++ ++static uint32_t bitmap_list_count(Qcow2BitmapList *bm_list) ++{ ++ Qcow2Bitmap *bm; ++ uint32_t nb_bitmaps = 0; ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ nb_bitmaps++; ++ } ++ ++ return nb_bitmaps; ++} ++ ++/* bitmap_list_load ++ * Get bitmap list from qcow2 image. Actually reads bitmap directory, ++ * checks it and convert to bitmap list. ++ */ ++static Qcow2BitmapList * GRAPH_RDLOCK ++bitmap_list_load(BlockDriverState *bs, uint64_t offset, uint64_t size, ++ Error **errp) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ uint8_t *dir, *dir_end; ++ Qcow2BitmapDirEntry *e; ++ uint32_t nb_dir_entries = 0; ++ Qcow2BitmapList *bm_list = NULL; ++ ++ if (size == 0) { ++ error_setg(errp, "Requested bitmap directory size is zero"); ++ return NULL; ++ } ++ ++ if (size > QCOW2_MAX_BITMAP_DIRECTORY_SIZE) { ++ error_setg(errp, "Requested bitmap directory size is too big"); ++ return NULL; ++ } ++ ++ dir = g_try_malloc(size); ++ if (dir == NULL) { ++ error_setg(errp, "Failed to allocate space for bitmap directory"); ++ return NULL; ++ } ++ dir_end = dir + size; ++ ++ ret = bdrv_pread(bs->file, offset, size, dir, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to read bitmap directory"); ++ goto fail; ++ } ++ ++ bm_list = bitmap_list_new(); ++ for (e = (Qcow2BitmapDirEntry *)dir; ++ e < (Qcow2BitmapDirEntry *)dir_end; ++ e = next_dir_entry(e)) ++ { ++ Qcow2Bitmap *bm; ++ ++ if ((uint8_t *)(e + 1) > dir_end) { ++ goto broken_dir; ++ } ++ ++ if (++nb_dir_entries > s->nb_bitmaps) { ++ error_setg(errp, "More bitmaps found than specified in header" ++ " extension"); ++ goto fail; ++ } ++ bitmap_dir_entry_to_cpu(e); ++ ++ if ((uint8_t *)next_dir_entry(e) > dir_end) { ++ goto broken_dir; ++ } ++ ++ if (e->extra_data_size != 0) { ++ error_setg(errp, "Bitmap extra data is not supported"); ++ goto fail; ++ } ++ ++ ret = check_dir_entry(bs, e); ++ if (ret < 0) { ++ error_setg(errp, "Bitmap '%.*s' doesn't satisfy the constraints", ++ e->name_size, dir_entry_name_field(e)); ++ goto fail; ++ } ++ ++ bm = g_new0(Qcow2Bitmap, 1); ++ bm->table.offset = e->bitmap_table_offset; ++ bm->table.size = e->bitmap_table_size; ++ bm->flags = e->flags; ++ bm->granularity_bits = e->granularity_bits; ++ bm->name = dir_entry_copy_name(e); ++ QSIMPLEQ_INSERT_TAIL(bm_list, bm, entry); ++ } ++ ++ if (nb_dir_entries != s->nb_bitmaps) { ++ error_setg(errp, "Less bitmaps found than specified in header" ++ " extension"); ++ goto fail; ++ } ++ ++ if ((uint8_t *)e != dir_end) { ++ goto broken_dir; ++ } ++ ++ g_free(dir); ++ return bm_list; ++ ++broken_dir: ++ error_setg(errp, "Broken bitmap directory"); ++ ++fail: ++ g_free(dir); ++ bitmap_list_free(bm_list); ++ ++ return NULL; ++} ++ ++int coroutine_fn ++qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, ++ int64_t *refcount_table_size) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2BitmapList *bm_list; ++ Qcow2Bitmap *bm; ++ ++ if (s->nb_bitmaps == 0) { ++ return 0; ++ } ++ ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, refcount_table_size, ++ s->bitmap_directory_offset, ++ s->bitmap_directory_size); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, NULL); ++ if (bm_list == NULL) { ++ res->corruptions++; ++ return -EINVAL; ++ } ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ uint64_t *bitmap_table = NULL; ++ int i; ++ ++ ret = qcow2_inc_refcounts_imrt(bs, res, ++ refcount_table, refcount_table_size, ++ bm->table.offset, ++ bm->table.size * BME_TABLE_ENTRY_SIZE); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = bitmap_table_load(bs, &bm->table, &bitmap_table); ++ if (ret < 0) { ++ res->corruptions++; ++ goto out; ++ } ++ ++ for (i = 0; i < bm->table.size; ++i) { ++ uint64_t entry = bitmap_table[i]; ++ uint64_t offset = entry & BME_TABLE_ENTRY_OFFSET_MASK; ++ ++ if (check_table_entry(entry, s->cluster_size) < 0) { ++ res->corruptions++; ++ continue; ++ } ++ ++ if (offset == 0) { ++ continue; ++ } ++ ++ ret = qcow2_inc_refcounts_imrt(bs, res, ++ refcount_table, refcount_table_size, ++ offset, s->cluster_size); ++ if (ret < 0) { ++ g_free(bitmap_table); ++ goto out; ++ } ++ } ++ ++ g_free(bitmap_table); ++ } ++ ++out: ++ bitmap_list_free(bm_list); ++ ++ return ret; ++} ++ ++/* bitmap_list_store ++ * Store bitmap list to qcow2 image as a bitmap directory. ++ * Everything is checked. ++ */ ++static int GRAPH_RDLOCK ++bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, ++ uint64_t *offset, uint64_t *size, bool in_place) ++{ ++ int ret; ++ uint8_t *dir; ++ int64_t dir_offset = 0; ++ uint64_t dir_size = 0; ++ Qcow2Bitmap *bm; ++ Qcow2BitmapDirEntry *e; ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ dir_size += calc_dir_entry_size(strlen(bm->name), 0); ++ } ++ ++ if (dir_size == 0 || dir_size > QCOW2_MAX_BITMAP_DIRECTORY_SIZE) { ++ return -EINVAL; ++ } ++ ++ if (in_place) { ++ if (*size != dir_size || *offset == 0) { ++ return -EINVAL; ++ } ++ ++ dir_offset = *offset; ++ } ++ ++ dir = g_try_malloc0(dir_size); ++ if (dir == NULL) { ++ return -ENOMEM; ++ } ++ ++ e = (Qcow2BitmapDirEntry *)dir; ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ e->bitmap_table_offset = bm->table.offset; ++ e->bitmap_table_size = bm->table.size; ++ e->flags = bm->flags; ++ e->type = BT_DIRTY_TRACKING_BITMAP; ++ e->granularity_bits = bm->granularity_bits; ++ e->name_size = strlen(bm->name); ++ e->extra_data_size = 0; ++ memcpy(e + 1, bm->name, e->name_size); ++ ++ if (check_dir_entry(bs, e) < 0) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ e = next_dir_entry(e); ++ } ++ ++ bitmap_directory_to_be(dir, dir_size); ++ ++ if (!in_place) { ++ dir_offset = qcow2_alloc_clusters(bs, dir_size); ++ if (dir_offset < 0) { ++ ret = dir_offset; ++ goto fail; ++ } ++ } ++ ++ /* Actually, even in the in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY ++ * is not necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating ++ * bitmap directory in-place (actually, turn-off the extension), which is ++ * checked in qcow2_check_metadata_overlap() */ ++ ret = qcow2_pre_write_overlap_check( ++ bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size, ++ false); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = bdrv_pwrite(bs->file, dir_offset, dir_size, dir, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ g_free(dir); ++ ++ if (!in_place) { ++ *size = dir_size; ++ *offset = dir_offset; ++ } ++ ++ return 0; ++ ++fail: ++ g_free(dir); ++ ++ if (!in_place && dir_offset > 0) { ++ qcow2_free_clusters(bs, dir_offset, dir_size, QCOW2_DISCARD_OTHER); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Bitmap List end ++ */ ++ ++static int GRAPH_RDLOCK ++update_ext_header_and_dir_in_place(BlockDriverState *bs, ++ Qcow2BitmapList *bm_list) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ if (!(s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS) || ++ bm_list == NULL || QSIMPLEQ_EMPTY(bm_list) || ++ bitmap_list_count(bm_list) != s->nb_bitmaps) ++ { ++ return -EINVAL; ++ } ++ ++ s->autoclear_features &= ~(uint64_t)QCOW2_AUTOCLEAR_BITMAPS; ++ ret = update_header_sync(bs); ++ if (ret < 0) { ++ /* Two variants are possible here: ++ * 1. Autoclear flag is dropped, all bitmaps will be lost. ++ * 2. Autoclear flag is not dropped, old state is left. ++ */ ++ return ret; ++ } ++ ++ /* autoclear bit is not set, so we can safely update bitmap directory */ ++ ++ ret = bitmap_list_store(bs, bm_list, &s->bitmap_directory_offset, ++ &s->bitmap_directory_size, true); ++ if (ret < 0) { ++ /* autoclear bit is cleared, so all leaked clusters would be removed on ++ * qemu-img check */ ++ return ret; ++ } ++ ++ ret = update_header_sync(bs); ++ if (ret < 0) { ++ /* autoclear bit is cleared, so all leaked clusters would be removed on ++ * qemu-img check */ ++ return ret; ++ } ++ ++ s->autoclear_features |= QCOW2_AUTOCLEAR_BITMAPS; ++ return update_header_sync(bs); ++ /* If final update_header_sync() fails, two variants are possible: ++ * 1. Autoclear flag is not set, all bitmaps will be lost. ++ * 2. Autoclear flag is set, header and directory are successfully updated. ++ */ ++} ++ ++static int GRAPH_RDLOCK ++update_ext_header_and_dir(BlockDriverState *bs, Qcow2BitmapList *bm_list) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ uint64_t new_offset = 0; ++ uint64_t new_size = 0; ++ uint32_t new_nb_bitmaps = 0; ++ uint64_t old_offset = s->bitmap_directory_offset; ++ uint64_t old_size = s->bitmap_directory_size; ++ uint32_t old_nb_bitmaps = s->nb_bitmaps; ++ uint64_t old_autocl = s->autoclear_features; ++ ++ if (bm_list != NULL && !QSIMPLEQ_EMPTY(bm_list)) { ++ new_nb_bitmaps = bitmap_list_count(bm_list); ++ ++ if (new_nb_bitmaps > QCOW2_MAX_BITMAPS) { ++ return -EINVAL; ++ } ++ ++ ret = bitmap_list_store(bs, bm_list, &new_offset, &new_size, false); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ ret = qcow2_flush_caches(bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ s->autoclear_features |= QCOW2_AUTOCLEAR_BITMAPS; ++ } else { ++ s->autoclear_features &= ~(uint64_t)QCOW2_AUTOCLEAR_BITMAPS; ++ } ++ ++ s->bitmap_directory_offset = new_offset; ++ s->bitmap_directory_size = new_size; ++ s->nb_bitmaps = new_nb_bitmaps; ++ ++ ret = update_header_sync(bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if (old_size > 0) { ++ qcow2_free_clusters(bs, old_offset, old_size, QCOW2_DISCARD_OTHER); ++ } ++ ++ return 0; ++ ++fail: ++ if (new_offset > 0) { ++ qcow2_free_clusters(bs, new_offset, new_size, QCOW2_DISCARD_OTHER); ++ } ++ ++ s->bitmap_directory_offset = old_offset; ++ s->bitmap_directory_size = old_size; ++ s->nb_bitmaps = old_nb_bitmaps; ++ s->autoclear_features = old_autocl; ++ ++ return ret; ++} ++ ++/* for g_slist_foreach for GSList of BdrvDirtyBitmap* elements */ ++static void release_dirty_bitmap_helper(gpointer bitmap, ++ gpointer bs) ++{ ++ bdrv_release_dirty_bitmap(bitmap); ++} ++ ++/* for g_slist_foreach for GSList of BdrvDirtyBitmap* elements */ ++static void set_readonly_helper(gpointer bitmap, gpointer value) ++{ ++ bdrv_dirty_bitmap_set_readonly(bitmap, (bool)value); ++} ++ ++/* ++ * Return true on success, false on failure. ++ * If header_updated is not NULL then it is set appropriately regardless of ++ * the return value. ++ */ ++bool coroutine_fn ++qcow2_load_dirty_bitmaps(BlockDriverState *bs, ++ bool *header_updated, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2BitmapList *bm_list; ++ Qcow2Bitmap *bm; ++ GSList *created_dirty_bitmaps = NULL; ++ bool needs_update = false; ++ ++ if (header_updated) { ++ *header_updated = false; ++ } ++ ++ if (s->nb_bitmaps == 0) { ++ /* No bitmaps - nothing to do */ ++ return true; ++ } ++ ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, errp); ++ if (bm_list == NULL) { ++ return false; ++ } ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ BdrvDirtyBitmap *bitmap; ++ ++ if ((bm->flags & BME_FLAG_IN_USE) && ++ bdrv_find_dirty_bitmap(bs, bm->name)) ++ { ++ /* ++ * We already have corresponding BdrvDirtyBitmap, and bitmap in the ++ * image is marked IN_USE. Firstly, this state is valid, no reason ++ * to consider existing BdrvDirtyBitmap to be bad. Secondly it's ++ * absolutely possible, when we do migration with shared storage ++ * with dirty-bitmaps capability enabled: if the bitmap was loaded ++ * from this storage before migration start, the storage will ++ * of-course contain IN_USE outdated version of the bitmap, and we ++ * should not load it on migration target, as we already have this ++ * bitmap, being migrated. ++ */ ++ continue; ++ } ++ ++ bitmap = load_bitmap(bs, bm, errp); ++ if (bitmap == NULL) { ++ goto fail; ++ } ++ ++ bdrv_dirty_bitmap_set_persistence(bitmap, true); ++ if (bm->flags & BME_FLAG_IN_USE) { ++ bdrv_dirty_bitmap_set_inconsistent(bitmap); ++ } else { ++ /* NB: updated flags only get written if can_write(bs) is true. */ ++ bm->flags |= BME_FLAG_IN_USE; ++ needs_update = true; ++ } ++ if (!(bm->flags & BME_FLAG_AUTO)) { ++ bdrv_disable_dirty_bitmap(bitmap); ++ } ++ created_dirty_bitmaps = ++ g_slist_append(created_dirty_bitmaps, bitmap); ++ } ++ ++ if (needs_update && can_write(bs)) { ++ /* in_use flags must be updated */ ++ int ret = update_ext_header_and_dir_in_place(bs, bm_list); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Can't update bitmap directory"); ++ goto fail; ++ } ++ if (header_updated) { ++ *header_updated = true; ++ } ++ } ++ ++ if (!can_write(bs)) { ++ g_slist_foreach(created_dirty_bitmaps, set_readonly_helper, ++ (gpointer)true); ++ } ++ ++ g_slist_free(created_dirty_bitmaps); ++ bitmap_list_free(bm_list); ++ ++ return true; ++ ++fail: ++ g_slist_foreach(created_dirty_bitmaps, release_dirty_bitmap_helper, bs); ++ g_slist_free(created_dirty_bitmaps); ++ bitmap_list_free(bm_list); ++ ++ return false; ++} ++ ++ ++static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags) ++{ ++ Qcow2BitmapInfoFlagsList *list = NULL; ++ Qcow2BitmapInfoFlagsList **tail = &list; ++ int i; ++ ++ static const struct { ++ int bme; /* Bitmap directory entry flags */ ++ int info; /* The flags to report to the user */ ++ } map[] = { ++ { BME_FLAG_IN_USE, QCOW2_BITMAP_INFO_FLAGS_IN_USE }, ++ { BME_FLAG_AUTO, QCOW2_BITMAP_INFO_FLAGS_AUTO }, ++ }; ++ ++ int map_size = ARRAY_SIZE(map); ++ ++ for (i = 0; i < map_size; ++i) { ++ if (flags & map[i].bme) { ++ QAPI_LIST_APPEND(tail, map[i].info); ++ flags &= ~map[i].bme; ++ } ++ } ++ /* Check if the BME_* mapping above is complete */ ++ assert(!flags); ++ ++ return list; ++} ++ ++/* ++ * qcow2_get_bitmap_info_list() ++ * Returns a list of QCOW2 bitmap details. ++ * On success return true with info_list set (note, that if there are no ++ * bitmaps, info_list is set to NULL). ++ * On failure return false with errp set. ++ */ ++bool qcow2_get_bitmap_info_list(BlockDriverState *bs, ++ Qcow2BitmapInfoList **info_list, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2BitmapList *bm_list; ++ Qcow2Bitmap *bm; ++ Qcow2BitmapInfoList **tail; ++ ++ if (s->nb_bitmaps == 0) { ++ *info_list = NULL; ++ return true; ++ } ++ ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, errp); ++ if (!bm_list) { ++ return false; ++ } ++ ++ *info_list = NULL; ++ tail = info_list; ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ Qcow2BitmapInfo *info = g_new0(Qcow2BitmapInfo, 1); ++ info->granularity = 1U << bm->granularity_bits; ++ info->name = g_strdup(bm->name); ++ info->flags = get_bitmap_info_flags(bm->flags & ~BME_RESERVED_FLAGS); ++ QAPI_LIST_APPEND(tail, info); ++ } ++ ++ bitmap_list_free(bm_list); ++ ++ return true; ++} ++ ++int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2BitmapList *bm_list; ++ Qcow2Bitmap *bm; ++ GSList *ro_dirty_bitmaps = NULL; ++ int ret = -EINVAL; ++ bool need_header_update = false; ++ ++ if (s->nb_bitmaps == 0) { ++ /* No bitmaps - nothing to do */ ++ return 0; ++ } ++ ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, errp); ++ if (bm_list == NULL) { ++ return -EINVAL; ++ } ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name); ++ ++ if (!bitmap) { ++ error_setg(errp, "Unexpected bitmap '%s' in image '%s'", ++ bm->name, bs->filename); ++ goto out; ++ } ++ ++ if (!(bm->flags & BME_FLAG_IN_USE)) { ++ if (!bdrv_dirty_bitmap_readonly(bitmap)) { ++ error_setg(errp, "Corruption: bitmap '%s' is not marked IN_USE " ++ "in the image '%s' and not marked readonly in RAM", ++ bm->name, bs->filename); ++ goto out; ++ } ++ if (bdrv_dirty_bitmap_inconsistent(bitmap)) { ++ error_setg(errp, "Corruption: bitmap '%s' is inconsistent but " ++ "is not marked IN_USE in the image '%s'", bm->name, ++ bs->filename); ++ goto out; ++ } ++ ++ bm->flags |= BME_FLAG_IN_USE; ++ need_header_update = true; ++ } else { ++ /* ++ * What if flags already has BME_FLAG_IN_USE ? ++ * ++ * 1. if we are reopening RW -> RW it's OK, of course. ++ * 2. if we are reopening RO -> RW: ++ * 2.1 if @bitmap is inconsistent, it's OK. It means that it was ++ * inconsistent (IN_USE) when we loaded it ++ * 2.2 if @bitmap is not inconsistent. This seems to be impossible ++ * and implies third party interaction. Let's error-out for ++ * safety. ++ */ ++ if (bdrv_dirty_bitmap_readonly(bitmap) && ++ !bdrv_dirty_bitmap_inconsistent(bitmap)) ++ { ++ error_setg(errp, "Corruption: bitmap '%s' is marked IN_USE " ++ "in the image '%s' but it is readonly and " ++ "consistent in RAM", ++ bm->name, bs->filename); ++ goto out; ++ } ++ } ++ ++ if (bdrv_dirty_bitmap_readonly(bitmap)) { ++ ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap); ++ } ++ } ++ ++ if (need_header_update) { ++ if (!can_write(bs->file->bs) || !(bs->file->perm & BLK_PERM_WRITE)) { ++ error_setg(errp, "Failed to reopen bitmaps rw: no write access " ++ "the protocol file"); ++ goto out; ++ } ++ ++ /* in_use flags must be updated */ ++ ret = update_ext_header_and_dir_in_place(bs, bm_list); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Cannot update bitmap directory"); ++ goto out; ++ } ++ } ++ ++ g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, (gpointer)false); ++ ret = 0; ++ ++out: ++ g_slist_free(ro_dirty_bitmaps); ++ bitmap_list_free(bm_list); ++ ++ return ret; ++} ++ ++/* Checks to see if it's safe to resize bitmaps */ ++int coroutine_fn qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2BitmapList *bm_list; ++ Qcow2Bitmap *bm; ++ int ret = 0; ++ ++ if (s->nb_bitmaps == 0) { ++ return 0; ++ } ++ ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, errp); ++ if (bm_list == NULL) { ++ return -EINVAL; ++ } ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name); ++ if (bitmap == NULL) { ++ /* ++ * We rely on all bitmaps being in-memory to be able to resize them, ++ * Otherwise, we'd need to resize them on disk explicitly ++ */ ++ error_setg(errp, "Cannot resize qcow2 with persistent bitmaps that " ++ "were not loaded into memory"); ++ ret = -ENOTSUP; ++ goto out; ++ } ++ ++ /* ++ * The checks against readonly and busy are redundant, but certainly ++ * do no harm. checks against inconsistent are crucial: ++ */ ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { ++ ret = -ENOTSUP; ++ goto out; ++ } ++ } ++ ++out: ++ bitmap_list_free(bm_list); ++ return ret; ++} ++ ++/* store_bitmap_data() ++ * Store bitmap to image, filling bitmap table accordingly. ++ */ ++static uint64_t * GRAPH_RDLOCK ++store_bitmap_data(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, ++ uint32_t *bitmap_table_size, Error **errp) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ int64_t offset; ++ uint64_t limit; ++ uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); ++ const char *bm_name = bdrv_dirty_bitmap_name(bitmap); ++ uint8_t *buf = NULL; ++ uint64_t *tb; ++ uint64_t tb_size = ++ size_to_clusters(s, ++ bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size)); ++ ++ if (tb_size > BME_MAX_TABLE_SIZE || ++ tb_size * s->cluster_size > BME_MAX_PHYS_SIZE) ++ { ++ error_setg(errp, "Bitmap '%s' is too big", bm_name); ++ return NULL; ++ } ++ ++ tb = g_try_new0(uint64_t, tb_size); ++ if (tb == NULL) { ++ error_setg(errp, "No memory"); ++ return NULL; ++ } ++ ++ buf = g_malloc(s->cluster_size); ++ limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap); ++ assert(DIV_ROUND_UP(bm_size, limit) == tb_size); ++ ++ offset = 0; ++ while ((offset = bdrv_dirty_bitmap_next_dirty(bitmap, offset, INT64_MAX)) ++ >= 0) ++ { ++ uint64_t cluster = offset / limit; ++ uint64_t end, write_size; ++ int64_t off; ++ ++ /* ++ * We found the first dirty offset, but want to write out the ++ * entire cluster of the bitmap that includes that offset, ++ * including any leading zero bits. ++ */ ++ offset = QEMU_ALIGN_DOWN(offset, limit); ++ end = MIN(bm_size, offset + limit); ++ write_size = bdrv_dirty_bitmap_serialization_size(bitmap, offset, ++ end - offset); ++ assert(write_size <= s->cluster_size); ++ ++ off = qcow2_alloc_clusters(bs, s->cluster_size); ++ if (off < 0) { ++ error_setg_errno(errp, -off, ++ "Failed to allocate clusters for bitmap '%s'", ++ bm_name); ++ goto fail; ++ } ++ tb[cluster] = off; ++ ++ bdrv_dirty_bitmap_serialize_part(bitmap, buf, offset, end - offset); ++ if (write_size < s->cluster_size) { ++ memset(buf + write_size, 0, s->cluster_size - write_size); ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, off, s->cluster_size, false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Qcow2 overlap check failed"); ++ goto fail; ++ } ++ ++ ret = bdrv_pwrite(bs->file, off, s->cluster_size, buf, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to write bitmap '%s' to file", ++ bm_name); ++ goto fail; ++ } ++ ++ offset = end; ++ } ++ ++ *bitmap_table_size = tb_size; ++ g_free(buf); ++ ++ return tb; ++ ++fail: ++ clear_bitmap_table(bs, tb, tb_size); ++ g_free(buf); ++ g_free(tb); ++ ++ return NULL; ++} ++ ++/* store_bitmap() ++ * Store bm->dirty_bitmap to qcow2. ++ * Set bm->table_offset and bm->table_size accordingly. ++ */ ++static int GRAPH_RDLOCK ++store_bitmap(BlockDriverState *bs, Qcow2Bitmap *bm, Error **errp) ++{ ++ int ret; ++ uint64_t *tb; ++ int64_t tb_offset; ++ uint32_t tb_size; ++ BdrvDirtyBitmap *bitmap = bm->dirty_bitmap; ++ const char *bm_name; ++ ++ assert(bitmap != NULL); ++ ++ bm_name = bdrv_dirty_bitmap_name(bitmap); ++ ++ tb = store_bitmap_data(bs, bitmap, &tb_size, errp); ++ if (tb == NULL) { ++ return -EINVAL; ++ } ++ ++ assert(tb_size <= BME_MAX_TABLE_SIZE); ++ tb_offset = qcow2_alloc_clusters(bs, tb_size * sizeof(tb[0])); ++ if (tb_offset < 0) { ++ error_setg_errno(errp, -tb_offset, ++ "Failed to allocate clusters for bitmap '%s'", ++ bm_name); ++ ret = tb_offset; ++ goto fail; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, tb_offset, ++ tb_size * sizeof(tb[0]), false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Qcow2 overlap check failed"); ++ goto fail; ++ } ++ ++ bitmap_table_bswap_be(tb, tb_size); ++ ret = bdrv_pwrite(bs->file, tb_offset, tb_size * sizeof(tb[0]), tb, 0); ++ if (ret < 0) { ++ bitmap_table_bswap_be(tb, tb_size); ++ error_setg_errno(errp, -ret, "Failed to write bitmap '%s' to file", ++ bm_name); ++ goto fail; ++ } ++ ++ g_free(tb); ++ ++ bm->table.offset = tb_offset; ++ bm->table.size = tb_size; ++ ++ return 0; ++ ++fail: ++ clear_bitmap_table(bs, tb, tb_size); ++ ++ if (tb_offset > 0) { ++ qcow2_free_clusters(bs, tb_offset, tb_size * sizeof(tb[0]), ++ QCOW2_DISCARD_OTHER); ++ } ++ ++ g_free(tb); ++ ++ return ret; ++} ++ ++static Qcow2Bitmap *find_bitmap_by_name(Qcow2BitmapList *bm_list, ++ const char *name) ++{ ++ Qcow2Bitmap *bm; ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ if (strcmp(name, bm->name) == 0) { ++ return bm; ++ } ++ } ++ ++ return NULL; ++} ++ ++int coroutine_fn qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, ++ const char *name, ++ Error **errp) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2Bitmap *bm = NULL; ++ Qcow2BitmapList *bm_list; ++ ++ if (s->nb_bitmaps == 0) { ++ /* ++ * Absence of the bitmap is not an error: see explanation above ++ * bdrv_co_remove_persistent_dirty_bitmap() definition. ++ */ ++ return 0; ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, errp); ++ if (bm_list == NULL) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ bm = find_bitmap_by_name(bm_list, name); ++ if (bm == NULL) { ++ /* Absence of the bitmap is not an error, see above. */ ++ ret = 0; ++ goto out; ++ } ++ ++ QSIMPLEQ_REMOVE(bm_list, bm, Qcow2Bitmap, entry); ++ ++ ret = update_ext_header_and_dir(bs, bm_list); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to update bitmap extension"); ++ goto out; ++ } ++ ++ free_bitmap_clusters(bs, &bm->table); ++ ++out: ++ qemu_co_mutex_unlock(&s->lock); ++ ++ bitmap_free(bm); ++ bitmap_list_free(bm_list); ++ ++ return ret; ++} ++ ++/* ++ * qcow2_store_persistent_dirty_bitmaps ++ * ++ * Stores persistent BdrvDirtyBitmap objects. ++ * ++ * @release_stored: if true, release BdrvDirtyBitmap's after storing to the ++ * image. This is used in two cases, both via qcow2_inactivate: ++ * 1. bdrv_close: It's correct to remove bitmaps on close. ++ * 2. migration: If bitmaps are migrated through migration channel via ++ * 'dirty-bitmaps' migration capability they are not handled by this code. ++ * Otherwise, it's OK to drop BdrvDirtyBitmap's and reload them on ++ * invalidation. ++ * ++ * Anyway, it's correct to remove BdrvDirtyBitmap's on inactivation, as ++ * inactivation means that we lose control on disk, and therefore on bitmaps, ++ * we should sync them and do not touch more. ++ * ++ * Contrariwise, we don't want to release any bitmaps on just reopen-to-ro, ++ * when we need to store them, as image is still under our control, and it's ++ * good to keep all the bitmaps in read-only mode. Moreover, keeping them ++ * read-only is correct because this is what would happen if we opened the node ++ * readonly to begin with, and whether we opened directly or reopened to that ++ * state shouldn't matter for the state we get afterward. ++ */ ++bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, ++ bool release_stored, Error **errp) ++{ ++ ERRP_GUARD(); ++ BdrvDirtyBitmap *bitmap; ++ BDRVQcow2State *s = bs->opaque; ++ uint32_t new_nb_bitmaps = s->nb_bitmaps; ++ uint64_t new_dir_size = s->bitmap_directory_size; ++ int ret; ++ Qcow2BitmapList *bm_list; ++ Qcow2Bitmap *bm; ++ QSIMPLEQ_HEAD(, Qcow2BitmapTable) drop_tables; ++ Qcow2BitmapTable *tb, *tb_next; ++ bool need_write = false; ++ ++ QSIMPLEQ_INIT(&drop_tables); ++ ++ if (s->nb_bitmaps == 0) { ++ bm_list = bitmap_list_new(); ++ } else { ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, errp); ++ if (bm_list == NULL) { ++ return false; ++ } ++ } ++ ++ /* check constraints and names */ ++ FOR_EACH_DIRTY_BITMAP(bs, bitmap) { ++ const char *name = bdrv_dirty_bitmap_name(bitmap); ++ uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap); ++ ++ if (!bdrv_dirty_bitmap_get_persistence(bitmap) || ++ bdrv_dirty_bitmap_inconsistent(bitmap)) { ++ continue; ++ } ++ ++ if (bdrv_dirty_bitmap_readonly(bitmap)) { ++ /* ++ * Store the bitmap in the associated Qcow2Bitmap so it ++ * can be released later ++ */ ++ bm = find_bitmap_by_name(bm_list, name); ++ if (bm) { ++ bm->dirty_bitmap = bitmap; ++ } ++ continue; ++ } ++ ++ need_write = true; ++ ++ if (check_constraints_on_bitmap(bs, name, granularity, errp) < 0) { ++ error_prepend(errp, "Bitmap '%s' doesn't satisfy the constraints: ", ++ name); ++ goto fail; ++ } ++ ++ bm = find_bitmap_by_name(bm_list, name); ++ if (bm == NULL) { ++ if (++new_nb_bitmaps > QCOW2_MAX_BITMAPS) { ++ error_setg(errp, "Too many persistent bitmaps"); ++ goto fail; ++ } ++ ++ new_dir_size += calc_dir_entry_size(strlen(name), 0); ++ if (new_dir_size > QCOW2_MAX_BITMAP_DIRECTORY_SIZE) { ++ error_setg(errp, "Bitmap directory is too large"); ++ goto fail; ++ } ++ ++ bm = g_new0(Qcow2Bitmap, 1); ++ bm->name = g_strdup(name); ++ QSIMPLEQ_INSERT_TAIL(bm_list, bm, entry); ++ } else { ++ if (!(bm->flags & BME_FLAG_IN_USE)) { ++ error_setg(errp, "Bitmap '%s' already exists in the image", ++ name); ++ goto fail; ++ } ++ tb = g_memdup2(&bm->table, sizeof(bm->table)); ++ bm->table.offset = 0; ++ bm->table.size = 0; ++ QSIMPLEQ_INSERT_TAIL(&drop_tables, tb, entry); ++ } ++ bm->flags = bdrv_dirty_bitmap_enabled(bitmap) ? BME_FLAG_AUTO : 0; ++ bm->granularity_bits = ctz32(bdrv_dirty_bitmap_granularity(bitmap)); ++ bm->dirty_bitmap = bitmap; ++ } ++ ++ if (!need_write) { ++ goto success; ++ } ++ ++ if (!can_write(bs)) { ++ error_setg(errp, "No write access"); ++ goto fail; ++ } ++ ++ /* allocate clusters and store bitmaps */ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ bitmap = bm->dirty_bitmap; ++ ++ if (bitmap == NULL || bdrv_dirty_bitmap_readonly(bitmap)) { ++ continue; ++ } ++ ++ ret = store_bitmap(bs, bm, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ ret = update_ext_header_and_dir(bs, bm_list); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to update bitmap extension"); ++ goto fail; ++ } ++ ++ /* Bitmap directory was successfully updated, so, old data can be dropped. ++ * TODO it is better to reuse these clusters */ ++ QSIMPLEQ_FOREACH_SAFE(tb, &drop_tables, entry, tb_next) { ++ free_bitmap_clusters(bs, tb); ++ g_free(tb); ++ } ++ ++success: ++ if (release_stored) { ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ if (bm->dirty_bitmap == NULL) { ++ continue; ++ } ++ ++ bdrv_release_dirty_bitmap(bm->dirty_bitmap); ++ } ++ } ++ ++ bitmap_list_free(bm_list); ++ return true; ++ ++fail: ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ if (bm->dirty_bitmap == NULL || bm->table.offset == 0 || ++ bdrv_dirty_bitmap_readonly(bm->dirty_bitmap)) ++ { ++ continue; ++ } ++ ++ free_bitmap_clusters(bs, &bm->table); ++ } ++ ++ QSIMPLEQ_FOREACH_SAFE(tb, &drop_tables, entry, tb_next) { ++ g_free(tb); ++ } ++ ++ bitmap_list_free(bm_list); ++ return false; ++} ++ ++int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp) ++{ ++ BdrvDirtyBitmap *bitmap; ++ ++ if (!qcow2_store_persistent_dirty_bitmaps(bs, false, errp)) { ++ return -EINVAL; ++ } ++ ++ FOR_EACH_DIRTY_BITMAP(bs, bitmap) { ++ if (bdrv_dirty_bitmap_get_persistence(bitmap)) { ++ bdrv_dirty_bitmap_set_readonly(bitmap, true); ++ } ++ } ++ ++ return 0; ++} ++ ++bool coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, ++ const char *name, ++ uint32_t granularity, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ BDRVQcow2State *s = bs->opaque; ++ BdrvDirtyBitmap *bitmap; ++ uint64_t bitmap_directory_size = 0; ++ uint32_t nb_bitmaps = 0; ++ ++ if (bdrv_find_dirty_bitmap(bs, name)) { ++ error_setg(errp, "Bitmap already exists: %s", name); ++ return false; ++ } ++ ++ if (s->qcow_version < 3) { ++ /* Without autoclear_features, we would always have to assume ++ * that a program without persistent dirty bitmap support has ++ * accessed this qcow2 file when opening it, and would thus ++ * have to drop all dirty bitmaps (defeating their purpose). ++ */ ++ error_setg(errp, "Cannot store dirty bitmaps in qcow2 v2 files"); ++ goto fail; ++ } ++ ++ if (check_constraints_on_bitmap(bs, name, granularity, errp) != 0) { ++ goto fail; ++ } ++ ++ FOR_EACH_DIRTY_BITMAP(bs, bitmap) { ++ if (bdrv_dirty_bitmap_get_persistence(bitmap)) { ++ nb_bitmaps++; ++ bitmap_directory_size += ++ calc_dir_entry_size(strlen(bdrv_dirty_bitmap_name(bitmap)), 0); ++ } ++ } ++ nb_bitmaps++; ++ bitmap_directory_size += calc_dir_entry_size(strlen(name), 0); ++ ++ if (nb_bitmaps > QCOW2_MAX_BITMAPS) { ++ error_setg(errp, ++ "Maximum number of persistent bitmaps is already reached"); ++ goto fail; ++ } ++ ++ if (bitmap_directory_size > QCOW2_MAX_BITMAP_DIRECTORY_SIZE) { ++ error_setg(errp, "Not enough space in the bitmap directory"); ++ goto fail; ++ } ++ ++ return true; ++ ++fail: ++ error_prepend(errp, "Can't make bitmap '%s' persistent in '%s': ", ++ name, bdrv_get_device_or_node_name(bs)); ++ return false; ++} ++ ++bool qcow2_supports_persistent_dirty_bitmap(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ return s->qcow_version >= 3; ++} ++ ++/* ++ * Compute the space required to copy bitmaps from @in_bs. ++ * ++ * The computation is based as if copying to a new image with the ++ * given @cluster_size, which may differ from the cluster size in ++ * @in_bs; in fact, @in_bs might be something other than qcow2. ++ */ ++uint64_t qcow2_get_persistent_dirty_bitmap_size(BlockDriverState *in_bs, ++ uint32_t cluster_size) ++{ ++ uint64_t bitmaps_size = 0; ++ BdrvDirtyBitmap *bm; ++ size_t bitmap_dir_size = 0; ++ ++ FOR_EACH_DIRTY_BITMAP(in_bs, bm) { ++ if (bdrv_dirty_bitmap_get_persistence(bm)) { ++ const char *name = bdrv_dirty_bitmap_name(bm); ++ uint32_t granularity = bdrv_dirty_bitmap_granularity(bm); ++ uint64_t bmbytes = ++ get_bitmap_bytes_needed(bdrv_dirty_bitmap_size(bm), ++ granularity); ++ uint64_t bmclusters = DIV_ROUND_UP(bmbytes, cluster_size); ++ ++ /* Assume the entire bitmap is allocated */ ++ bitmaps_size += bmclusters * cluster_size; ++ /* Also reserve space for the bitmap table entries */ ++ bitmaps_size += ROUND_UP(bmclusters * BME_TABLE_ENTRY_SIZE, ++ cluster_size); ++ /* And space for contribution to bitmap directory size */ ++ bitmap_dir_size += calc_dir_entry_size(strlen(name), 0); ++ } ++ } ++ bitmaps_size += ROUND_UP(bitmap_dir_size, cluster_size); ++ ++ return bitmaps_size; ++} +diff --git a/qcow2/lib/qcow2-cache.c b/qcow2/lib/qcow2-cache.c +new file mode 100644 +index 00000000..23d9588b +--- /dev/null ++++ b/qcow2/lib/qcow2-cache.c +@@ -0,0 +1,462 @@ ++/* ++ * L2/refcount table cache for the QCOW2 format ++ * ++ * Copyright (c) 2010 Kevin Wolf ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/block-io.h" ++#include "qemu/memalign.h" ++#include "qcow2.h" ++#include "trace.h" ++ ++typedef struct Qcow2CachedTable { ++ int64_t offset; ++ uint64_t lru_counter; ++ int ref; ++ bool dirty; ++} Qcow2CachedTable; ++ ++struct Qcow2Cache { ++ Qcow2CachedTable *entries; ++ struct Qcow2Cache *depends; ++ int size; ++ int table_size; ++ bool depends_on_flush; ++ void *table_array; ++ uint64_t lru_counter; ++ uint64_t cache_clean_lru_counter; ++}; ++ ++static inline void *qcow2_cache_get_table_addr(Qcow2Cache *c, int table) ++{ ++ return (uint8_t *) c->table_array + (size_t) table * c->table_size; ++} ++ ++static inline int qcow2_cache_get_table_idx(Qcow2Cache *c, void *table) ++{ ++ ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array; ++ int idx = table_offset / c->table_size; ++ assert(idx >= 0 && idx < c->size && table_offset % c->table_size == 0); ++ return idx; ++} ++ ++static inline const char *qcow2_cache_get_name(BDRVQcow2State *s, Qcow2Cache *c) ++{ ++ if (c == s->refcount_block_cache) { ++ return "refcount block"; ++ } else if (c == s->l2_table_cache) { ++ return "L2 table"; ++ } else { ++ /* Do not abort, because this is not critical */ ++ return "unknown"; ++ } ++} ++ ++static void qcow2_cache_table_release(Qcow2Cache *c, int i, int num_tables) ++{ ++/* Using MADV_DONTNEED to discard memory is a Linux-specific feature */ ++#ifdef CONFIG_LINUX ++ void *t = qcow2_cache_get_table_addr(c, i); ++ int align = qemu_real_host_page_size(); ++ size_t mem_size = (size_t) c->table_size * num_tables; ++ size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t; ++ size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align); ++ if (mem_size > offset && length > 0) { ++ madvise((uint8_t *) t + offset, length, MADV_DONTNEED); ++ } ++#endif ++} ++ ++static inline bool can_clean_entry(Qcow2Cache *c, int i) ++{ ++ Qcow2CachedTable *t = &c->entries[i]; ++ return t->ref == 0 && !t->dirty && t->offset != 0 && ++ t->lru_counter <= c->cache_clean_lru_counter; ++} ++ ++void qcow2_cache_clean_unused(Qcow2Cache *c) ++{ ++ int i = 0; ++ while (i < c->size) { ++ int to_clean = 0; ++ ++ /* Skip the entries that we don't need to clean */ ++ while (i < c->size && !can_clean_entry(c, i)) { ++ i++; ++ } ++ ++ /* And count how many we can clean in a row */ ++ while (i < c->size && can_clean_entry(c, i)) { ++ c->entries[i].offset = 0; ++ c->entries[i].lru_counter = 0; ++ i++; ++ to_clean++; ++ } ++ ++ if (to_clean > 0) { ++ qcow2_cache_table_release(c, i - to_clean, to_clean); ++ } ++ } ++ ++ c->cache_clean_lru_counter = c->lru_counter; ++} ++ ++Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, ++ unsigned table_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2Cache *c; ++ ++ assert(num_tables > 0); ++ assert(is_power_of_2(table_size)); ++ assert(table_size >= (1 << MIN_CLUSTER_BITS)); ++ assert(table_size <= s->cluster_size); ++ ++ c = g_new0(Qcow2Cache, 1); ++ c->size = num_tables; ++ c->table_size = table_size; ++ c->entries = g_try_new0(Qcow2CachedTable, num_tables); ++ c->table_array = qemu_try_blockalign(bs->file->bs, ++ (size_t) num_tables * c->table_size); ++ ++ if (!c->entries || !c->table_array) { ++ qemu_vfree(c->table_array); ++ g_free(c->entries); ++ g_free(c); ++ c = NULL; ++ } ++ ++ return c; ++} ++ ++int qcow2_cache_destroy(Qcow2Cache *c) ++{ ++ int i; ++ ++ for (i = 0; i < c->size; i++) { ++ assert(c->entries[i].ref == 0); ++ } ++ ++ qemu_vfree(c->table_array); ++ g_free(c->entries); ++ g_free(c); ++ ++ return 0; ++} ++ ++static int GRAPH_RDLOCK ++qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) ++{ ++ int ret; ++ ++ ret = qcow2_cache_flush(bs, c->depends); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ c->depends = NULL; ++ c->depends_on_flush = false; ++ ++ return 0; ++} ++ ++static int GRAPH_RDLOCK ++qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret = 0; ++ ++ if (!c->entries[i].dirty || !c->entries[i].offset) { ++ return 0; ++ } ++ ++ trace_qcow2_cache_entry_flush(qemu_coroutine_self(), ++ c == s->l2_table_cache, i); ++ ++ if (c->depends) { ++ ret = qcow2_cache_flush_dependency(bs, c); ++ } else if (c->depends_on_flush) { ++ ret = bdrv_flush(bs->file->bs); ++ if (ret >= 0) { ++ c->depends_on_flush = false; ++ } ++ } ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (c == s->refcount_block_cache) { ++ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK, ++ c->entries[i].offset, c->table_size, false); ++ } else if (c == s->l2_table_cache) { ++ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, ++ c->entries[i].offset, c->table_size, false); ++ } else { ++ ret = qcow2_pre_write_overlap_check(bs, 0, ++ c->entries[i].offset, c->table_size, false); ++ } ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (c == s->refcount_block_cache) { ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART); ++ } else if (c == s->l2_table_cache) { ++ BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); ++ } ++ ++ ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->table_size, ++ qcow2_cache_get_table_addr(c, i), 0); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ c->entries[i].dirty = false; ++ ++ return 0; ++} ++ ++int qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int result = 0; ++ int ret; ++ int i; ++ ++ trace_qcow2_cache_flush(qemu_coroutine_self(), c == s->l2_table_cache); ++ ++ for (i = 0; i < c->size; i++) { ++ ret = qcow2_cache_entry_flush(bs, c, i); ++ if (ret < 0 && result != -ENOSPC) { ++ result = ret; ++ } ++ } ++ ++ return result; ++} ++ ++int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c) ++{ ++ int result = qcow2_cache_write(bs, c); ++ ++ if (result == 0) { ++ int ret = bdrv_flush(bs->file->bs); ++ if (ret < 0) { ++ result = ret; ++ } ++ } ++ ++ return result; ++} ++ ++int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, ++ Qcow2Cache *dependency) ++{ ++ int ret; ++ ++ if (dependency->depends) { ++ ret = qcow2_cache_flush_dependency(bs, dependency); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ if (c->depends && (c->depends != dependency)) { ++ ret = qcow2_cache_flush_dependency(bs, c); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ c->depends = dependency; ++ return 0; ++} ++ ++void qcow2_cache_depends_on_flush(Qcow2Cache *c) ++{ ++ c->depends_on_flush = true; ++} ++ ++int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c) ++{ ++ int ret, i; ++ ++ ret = qcow2_cache_flush(bs, c); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ for (i = 0; i < c->size; i++) { ++ assert(c->entries[i].ref == 0); ++ c->entries[i].offset = 0; ++ c->entries[i].lru_counter = 0; ++ } ++ ++ qcow2_cache_table_release(c, 0, c->size); ++ ++ c->lru_counter = 0; ++ ++ return 0; ++} ++ ++static int GRAPH_RDLOCK ++qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, ++ void **table, bool read_from_disk) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i; ++ int ret; ++ int lookup_index; ++ uint64_t min_lru_counter = UINT64_MAX; ++ int min_lru_index = -1; ++ ++ assert(offset != 0); ++ ++ trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, ++ offset, read_from_disk); ++ ++ if (!QEMU_IS_ALIGNED(offset, c->table_size)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Cannot get entry from %s " ++ "cache: Offset %#" PRIx64 " is unaligned", ++ qcow2_cache_get_name(s, c), offset); ++ return -EIO; ++ } ++ ++ /* Check if the table is already cached */ ++ i = lookup_index = (offset / c->table_size * 4) % c->size; ++ do { ++ const Qcow2CachedTable *t = &c->entries[i]; ++ if (t->offset == offset) { ++ goto found; ++ } ++ if (t->ref == 0 && t->lru_counter < min_lru_counter) { ++ min_lru_counter = t->lru_counter; ++ min_lru_index = i; ++ } ++ if (++i == c->size) { ++ i = 0; ++ } ++ } while (i != lookup_index); ++ ++ if (min_lru_index == -1) { ++ /* This can't happen in current synchronous code, but leave the check ++ * here as a reminder for whoever starts using AIO with the cache */ ++ abort(); ++ } ++ ++ /* Cache miss: write a table back and replace it */ ++ i = min_lru_index; ++ trace_qcow2_cache_get_replace_entry(qemu_coroutine_self(), ++ c == s->l2_table_cache, i); ++ ++ ret = qcow2_cache_entry_flush(bs, c, i); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ trace_qcow2_cache_get_read(qemu_coroutine_self(), ++ c == s->l2_table_cache, i); ++ c->entries[i].offset = 0; ++ if (read_from_disk) { ++ if (c == s->l2_table_cache) { ++ BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); ++ } ++ ++ ret = bdrv_pread(bs->file, offset, c->table_size, ++ qcow2_cache_get_table_addr(c, i), 0); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ c->entries[i].offset = offset; ++ ++ /* And return the right table */ ++found: ++ c->entries[i].ref++; ++ *table = qcow2_cache_get_table_addr(c, i); ++ ++ trace_qcow2_cache_get_done(qemu_coroutine_self(), ++ c == s->l2_table_cache, i); ++ ++ return 0; ++} ++ ++int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, ++ void **table) ++{ ++ return qcow2_cache_do_get(bs, c, offset, table, true); ++} ++ ++int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, ++ void **table) ++{ ++ return qcow2_cache_do_get(bs, c, offset, table, false); ++} ++ ++void qcow2_cache_put(Qcow2Cache *c, void **table) ++{ ++ int i = qcow2_cache_get_table_idx(c, *table); ++ ++ c->entries[i].ref--; ++ *table = NULL; ++ ++ if (c->entries[i].ref == 0) { ++ c->entries[i].lru_counter = ++c->lru_counter; ++ } ++ ++ assert(c->entries[i].ref >= 0); ++} ++ ++void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table) ++{ ++ int i = qcow2_cache_get_table_idx(c, table); ++ assert(c->entries[i].offset != 0); ++ c->entries[i].dirty = true; ++} ++ ++void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset) ++{ ++ int i; ++ ++ for (i = 0; i < c->size; i++) { ++ if (c->entries[i].offset == offset) { ++ return qcow2_cache_get_table_addr(c, i); ++ } ++ } ++ return NULL; ++} ++ ++void qcow2_cache_discard(Qcow2Cache *c, void *table) ++{ ++ int i = qcow2_cache_get_table_idx(c, table); ++ ++ assert(c->entries[i].ref == 0); ++ ++ c->entries[i].offset = 0; ++ c->entries[i].lru_counter = 0; ++ c->entries[i].dirty = false; ++ ++ qcow2_cache_table_release(c, i, 1); ++} +diff --git a/qcow2/lib/qcow2-cluster.c b/qcow2/lib/qcow2-cluster.c +new file mode 100644 +index 00000000..ce8c0076 +--- /dev/null ++++ b/qcow2/lib/qcow2-cluster.c +@@ -0,0 +1,2562 @@ ++/* ++ * Block driver for the QCOW version 2 format ++ * ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include ++ ++#include "block/block-io.h" ++#include "qapi/error.h" ++#include "qcow2.h" ++#include "qemu/bswap.h" ++#include "qemu/memalign.h" ++#include "trace.h" ++ ++int coroutine_fn qcow2_shrink_l1_table(BlockDriverState *bs, ++ uint64_t exact_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int new_l1_size, i, ret; ++ ++ if (exact_size >= s->l1_size) { ++ return 0; ++ } ++ ++ new_l1_size = exact_size; ++ ++#ifdef DEBUG_ALLOC2 ++ fprintf(stderr, "shrink l1_table from %d to %d\n", s->l1_size, new_l1_size); ++#endif ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_L1_SHRINK_WRITE_TABLE); ++ ret = bdrv_co_pwrite_zeroes(bs->file, ++ s->l1_table_offset + new_l1_size * L1E_SIZE, ++ (s->l1_size - new_l1_size) * L1E_SIZE, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = bdrv_co_flush(bs->file->bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_L1_SHRINK_FREE_L2_CLUSTERS); ++ for (i = s->l1_size - 1; i > new_l1_size - 1; i--) { ++ if ((s->l1_table[i] & L1E_OFFSET_MASK) == 0) { ++ continue; ++ } ++ qcow2_free_clusters(bs, s->l1_table[i] & L1E_OFFSET_MASK, ++ s->cluster_size, QCOW2_DISCARD_ALWAYS); ++ s->l1_table[i] = 0; ++ } ++ return 0; ++ ++fail: ++ /* ++ * If the write in the l1_table failed the image may contain a partially ++ * overwritten l1_table. In this case it would be better to clear the ++ * l1_table in memory to avoid possible image corruption. ++ */ ++ memset(s->l1_table + new_l1_size, 0, ++ (s->l1_size - new_l1_size) * L1E_SIZE); ++ return ret; ++} ++ ++int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, ++ bool exact_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int new_l1_size2, ret, i; ++ uint64_t *new_l1_table; ++ int64_t old_l1_table_offset, old_l1_size; ++ int64_t new_l1_table_offset, new_l1_size; ++ uint8_t data[12]; ++ ++ if (min_size <= s->l1_size) ++ return 0; ++ ++ /* Do a sanity check on min_size before trying to calculate new_l1_size ++ * (this prevents overflows during the while loop for the calculation of ++ * new_l1_size) */ ++ if (min_size > INT_MAX / L1E_SIZE) { ++ return -EFBIG; ++ } ++ ++ if (exact_size) { ++ new_l1_size = min_size; ++ } else { ++ /* Bump size up to reduce the number of times we have to grow */ ++ new_l1_size = s->l1_size; ++ if (new_l1_size == 0) { ++ new_l1_size = 1; ++ } ++ while (min_size > new_l1_size) { ++ new_l1_size = DIV_ROUND_UP(new_l1_size * 3, 2); ++ } ++ } ++ ++ QEMU_BUILD_BUG_ON(QCOW_MAX_L1_SIZE > INT_MAX); ++ if (new_l1_size > QCOW_MAX_L1_SIZE / L1E_SIZE) { ++ return -EFBIG; ++ } ++ ++#ifdef DEBUG_ALLOC2 ++ fprintf(stderr, "grow l1_table from %d to %" PRId64 "\n", ++ s->l1_size, new_l1_size); ++#endif ++ ++ new_l1_size2 = L1E_SIZE * new_l1_size; ++ new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_size2); ++ if (new_l1_table == NULL) { ++ return -ENOMEM; ++ } ++ memset(new_l1_table, 0, new_l1_size2); ++ ++ if (s->l1_size) { ++ memcpy(new_l1_table, s->l1_table, s->l1_size * L1E_SIZE); ++ } ++ ++ /* write new table (align to cluster) */ ++ BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ALLOC_TABLE); ++ new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2); ++ if (new_l1_table_offset < 0) { ++ qemu_vfree(new_l1_table); ++ return new_l1_table_offset; ++ } ++ ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* the L1 position has not yet been updated, so these clusters must ++ * indeed be completely free */ ++ ret = qcow2_pre_write_overlap_check(bs, 0, new_l1_table_offset, ++ new_l1_size2, false); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); ++ for(i = 0; i < s->l1_size; i++) ++ new_l1_table[i] = cpu_to_be64(new_l1_table[i]); ++ ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_size2, ++ new_l1_table, 0); ++ if (ret < 0) ++ goto fail; ++ for(i = 0; i < s->l1_size; i++) ++ new_l1_table[i] = be64_to_cpu(new_l1_table[i]); ++ ++ /* set new table */ ++ BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE); ++ stl_be_p(data, new_l1_size); ++ stq_be_p(data + 4, new_l1_table_offset); ++ ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), ++ sizeof(data), data, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ qemu_vfree(s->l1_table); ++ old_l1_table_offset = s->l1_table_offset; ++ s->l1_table_offset = new_l1_table_offset; ++ s->l1_table = new_l1_table; ++ old_l1_size = s->l1_size; ++ s->l1_size = new_l1_size; ++ qcow2_free_clusters(bs, old_l1_table_offset, old_l1_size * L1E_SIZE, ++ QCOW2_DISCARD_OTHER); ++ return 0; ++ fail: ++ qemu_vfree(new_l1_table); ++ qcow2_free_clusters(bs, new_l1_table_offset, new_l1_size2, ++ QCOW2_DISCARD_OTHER); ++ return ret; ++} ++ ++/* ++ * l2_load ++ * ++ * @bs: The BlockDriverState ++ * @offset: A guest offset, used to calculate what slice of the L2 ++ * table to load. ++ * @l2_offset: Offset to the L2 table in the image file. ++ * @l2_slice: Location to store the pointer to the L2 slice. ++ * ++ * Loads a L2 slice into memory (L2 slices are the parts of L2 tables ++ * that are loaded by the qcow2 cache). If the slice is in the cache, ++ * the cache is used; otherwise the L2 slice is loaded from the image ++ * file. ++ */ ++static int GRAPH_RDLOCK ++l2_load(BlockDriverState *bs, uint64_t offset, ++ uint64_t l2_offset, uint64_t **l2_slice) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int start_of_slice = l2_entry_size(s) * ++ (offset_to_l2_index(s, offset) - offset_to_l2_slice_index(s, offset)); ++ ++ return qcow2_cache_get(bs, s->l2_table_cache, l2_offset + start_of_slice, ++ (void **)l2_slice); ++} ++ ++/* ++ * Writes an L1 entry to disk (note that depending on the alignment ++ * requirements this function may write more that just one entry in ++ * order to prevent bdrv_pwrite from performing a read-modify-write) ++ */ ++int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int l1_start_index; ++ int i, ret; ++ int bufsize = MAX(L1E_SIZE, ++ MIN(bs->file->bs->bl.request_alignment, s->cluster_size)); ++ int nentries = bufsize / L1E_SIZE; ++ g_autofree uint64_t *buf = g_try_new0(uint64_t, nentries); ++ ++ if (buf == NULL) { ++ return -ENOMEM; ++ } ++ ++ l1_start_index = QEMU_ALIGN_DOWN(l1_index, nentries); ++ for (i = 0; i < MIN(nentries, s->l1_size - l1_start_index); i++) { ++ buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]); ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1, ++ s->l1_table_offset + L1E_SIZE * l1_start_index, bufsize, false); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); ++ ret = bdrv_pwrite_sync(bs->file, ++ s->l1_table_offset + L1E_SIZE * l1_start_index, ++ bufsize, buf, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* ++ * l2_allocate ++ * ++ * Allocate a new l2 entry in the file. If l1_index points to an already ++ * used entry in the L2 table (i.e. we are doing a copy on write for the L2 ++ * table) copy the contents of the old L2 table into the newly allocated one. ++ * Otherwise the new table is initialized with zeros. ++ * ++ */ ++ ++static int GRAPH_RDLOCK l2_allocate(BlockDriverState *bs, int l1_index) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t old_l2_offset; ++ uint64_t *l2_slice = NULL; ++ unsigned slice, slice_size2, n_slices; ++ int64_t l2_offset; ++ int ret; ++ ++ old_l2_offset = s->l1_table[l1_index]; ++ ++ trace_qcow2_l2_allocate(bs, l1_index); ++ ++ /* allocate a new l2 entry */ ++ ++ l2_offset = qcow2_alloc_clusters(bs, s->l2_size * l2_entry_size(s)); ++ if (l2_offset < 0) { ++ ret = l2_offset; ++ goto fail; ++ } ++ ++ /* The offset must fit in the offset field of the L1 table entry */ ++ assert((l2_offset & L1E_OFFSET_MASK) == l2_offset); ++ ++ /* If we're allocating the table at offset 0 then something is wrong */ ++ if (l2_offset == 0) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " ++ "allocation of L2 table at offset 0"); ++ ret = -EIO; ++ goto fail; ++ } ++ ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* allocate a new entry in the l2 cache */ ++ ++ slice_size2 = s->l2_slice_size * l2_entry_size(s); ++ n_slices = s->cluster_size / slice_size2; ++ ++ trace_qcow2_l2_allocate_get_empty(bs, l1_index); ++ for (slice = 0; slice < n_slices; slice++) { ++ ret = qcow2_cache_get_empty(bs, s->l2_table_cache, ++ l2_offset + slice * slice_size2, ++ (void **) &l2_slice); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if ((old_l2_offset & L1E_OFFSET_MASK) == 0) { ++ /* if there was no old l2 table, clear the new slice */ ++ memset(l2_slice, 0, slice_size2); ++ } else { ++ uint64_t *old_slice; ++ uint64_t old_l2_slice_offset = ++ (old_l2_offset & L1E_OFFSET_MASK) + slice * slice_size2; ++ ++ /* if there was an old l2 table, read a slice from the disk */ ++ BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); ++ ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_slice_offset, ++ (void **) &old_slice); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ memcpy(l2_slice, old_slice, slice_size2); ++ ++ qcow2_cache_put(s->l2_table_cache, (void **) &old_slice); ++ } ++ ++ /* write the l2 slice to the file */ ++ BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); ++ ++ trace_qcow2_l2_allocate_write_l2(bs, l1_index); ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ } ++ ++ ret = qcow2_cache_flush(bs, s->l2_table_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* update the L1 entry */ ++ trace_qcow2_l2_allocate_write_l1(bs, l1_index); ++ s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; ++ ret = qcow2_write_l1_entry(bs, l1_index); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ trace_qcow2_l2_allocate_done(bs, l1_index, 0); ++ return 0; ++ ++fail: ++ trace_qcow2_l2_allocate_done(bs, l1_index, ret); ++ if (l2_slice != NULL) { ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ } ++ s->l1_table[l1_index] = old_l2_offset; ++ if (l2_offset > 0) { ++ qcow2_free_clusters(bs, l2_offset, s->l2_size * l2_entry_size(s), ++ QCOW2_DISCARD_ALWAYS); ++ } ++ return ret; ++} ++ ++/* ++ * For a given L2 entry, count the number of contiguous subclusters of ++ * the same type starting from @sc_from. Compressed clusters are ++ * treated as if they were divided into subclusters of size ++ * s->subcluster_size. ++ * ++ * Return the number of contiguous subclusters and set @type to the ++ * subcluster type. ++ * ++ * If the L2 entry is invalid return -errno and set @type to ++ * QCOW2_SUBCLUSTER_INVALID. ++ */ ++static int GRAPH_RDLOCK ++qcow2_get_subcluster_range_type(BlockDriverState *bs, uint64_t l2_entry, ++ uint64_t l2_bitmap, unsigned sc_from, ++ QCow2SubclusterType *type) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint32_t val; ++ ++ *type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_from); ++ ++ if (*type == QCOW2_SUBCLUSTER_INVALID) { ++ return -EINVAL; ++ } else if (!has_subclusters(s) || *type == QCOW2_SUBCLUSTER_COMPRESSED) { ++ return s->subclusters_per_cluster - sc_from; ++ } ++ ++ switch (*type) { ++ case QCOW2_SUBCLUSTER_NORMAL: ++ val = l2_bitmap | QCOW_OFLAG_SUB_ALLOC_RANGE(0, sc_from); ++ return cto32(val) - sc_from; ++ ++ case QCOW2_SUBCLUSTER_ZERO_PLAIN: ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ val = (l2_bitmap | QCOW_OFLAG_SUB_ZERO_RANGE(0, sc_from)) >> 32; ++ return cto32(val) - sc_from; ++ ++ case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ val = ((l2_bitmap >> 32) | l2_bitmap) ++ & ~QCOW_OFLAG_SUB_ALLOC_RANGE(0, sc_from); ++ return ctz32(val) - sc_from; ++ ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++/* ++ * Return the number of contiguous subclusters of the exact same type ++ * in a given L2 slice, starting from cluster @l2_index, subcluster ++ * @sc_index. Allocated subclusters are required to be contiguous in ++ * the image file. ++ * At most @nb_clusters are checked (note that this means clusters, ++ * not subclusters). ++ * Compressed clusters are always processed one by one but for the ++ * purpose of this count they are treated as if they were divided into ++ * subclusters of size s->subcluster_size. ++ * On failure return -errno and update @l2_index to point to the ++ * invalid entry. ++ */ ++static int GRAPH_RDLOCK ++count_contiguous_subclusters(BlockDriverState *bs, int nb_clusters, ++ unsigned sc_index, uint64_t *l2_slice, ++ unsigned *l2_index) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i, count = 0; ++ bool check_offset = false; ++ uint64_t expected_offset = 0; ++ QCow2SubclusterType expected_type = QCOW2_SUBCLUSTER_NORMAL, type; ++ ++ assert(*l2_index + nb_clusters <= s->l2_slice_size); ++ ++ for (i = 0; i < nb_clusters; i++) { ++ unsigned first_sc = (i == 0) ? sc_index : 0; ++ uint64_t l2_entry = get_l2_entry(s, l2_slice, *l2_index + i); ++ uint64_t l2_bitmap = get_l2_bitmap(s, l2_slice, *l2_index + i); ++ int ret = qcow2_get_subcluster_range_type(bs, l2_entry, l2_bitmap, ++ first_sc, &type); ++ if (ret < 0) { ++ *l2_index += i; /* Point to the invalid entry */ ++ return -EIO; ++ } ++ if (i == 0) { ++ if (type == QCOW2_SUBCLUSTER_COMPRESSED) { ++ /* Compressed clusters are always processed one by one */ ++ return ret; ++ } ++ expected_type = type; ++ expected_offset = l2_entry & L2E_OFFSET_MASK; ++ check_offset = (type == QCOW2_SUBCLUSTER_NORMAL || ++ type == QCOW2_SUBCLUSTER_ZERO_ALLOC || ++ type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC); ++ } else if (type != expected_type) { ++ break; ++ } else if (check_offset) { ++ expected_offset += s->cluster_size; ++ if (expected_offset != (l2_entry & L2E_OFFSET_MASK)) { ++ break; ++ } ++ } ++ count += ret; ++ /* Stop if there are type changes before the end of the cluster */ ++ if (first_sc + ret < s->subclusters_per_cluster) { ++ break; ++ } ++ } ++ ++ return count; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++do_perform_cow_read(BlockDriverState *bs, uint64_t src_cluster_offset, ++ unsigned offset_in_cluster, QEMUIOVector *qiov) ++{ ++ int ret; ++ ++ if (qiov->size == 0) { ++ return 0; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_COW_READ); ++ ++ if (!bs->drv) { ++ return -ENOMEDIUM; ++ } ++ ++ /* ++ * We never deal with requests that don't satisfy ++ * bdrv_check_qiov_request(), and aligning requests to clusters never ++ * breaks this condition. So, do some assertions before calling ++ * bs->drv->bdrv_co_preadv_part() which has int64_t arguments. ++ */ ++ assert(src_cluster_offset <= INT64_MAX); ++ assert(src_cluster_offset + offset_in_cluster <= INT64_MAX); ++ /* Cast qiov->size to uint64_t to silence a compiler warning on -m32 */ ++ assert((uint64_t)qiov->size <= INT64_MAX); ++ bdrv_check_qiov_request(src_cluster_offset + offset_in_cluster, qiov->size, ++ qiov, 0, &error_abort); ++ /* ++ * Call .bdrv_co_readv() directly instead of using the public block-layer ++ * interface. This avoids double I/O throttling and request tracking, ++ * which can lead to deadlock when block layer copy-on-read is enabled. ++ */ ++ ret = bs->drv->bdrv_co_preadv_part(bs, ++ src_cluster_offset + offset_in_cluster, ++ qiov->size, qiov, 0, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++do_perform_cow_write(BlockDriverState *bs, uint64_t cluster_offset, ++ unsigned offset_in_cluster, QEMUIOVector *qiov) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ if (qiov->size == 0) { ++ return 0; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, ++ cluster_offset + offset_in_cluster, qiov->size, true); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_COW_WRITE); ++ ret = bdrv_co_pwritev(s->data_file, cluster_offset + offset_in_cluster, ++ qiov->size, qiov, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++ ++/* ++ * get_host_offset ++ * ++ * For a given offset of the virtual disk find the equivalent host ++ * offset in the qcow2 file and store it in *host_offset. Neither ++ * offset needs to be aligned to a cluster boundary. ++ * ++ * If the cluster is unallocated then *host_offset will be 0. ++ * If the cluster is compressed then *host_offset will contain the l2 entry. ++ * ++ * On entry, *bytes is the maximum number of contiguous bytes starting at ++ * offset that we are interested in. ++ * ++ * On exit, *bytes is the number of bytes starting at offset that have the same ++ * subcluster type and (if applicable) are stored contiguously in the image ++ * file. The subcluster type is stored in *subcluster_type. ++ * Compressed clusters are always processed one by one. ++ * ++ * Returns 0 on success, -errno in error cases. ++ */ ++int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, ++ unsigned int *bytes, uint64_t *host_offset, ++ QCow2SubclusterType *subcluster_type) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ unsigned int l2_index, sc_index; ++ uint64_t l1_index, l2_offset, *l2_slice, l2_entry, l2_bitmap; ++ int sc; ++ unsigned int offset_in_cluster; ++ uint64_t bytes_available, bytes_needed, nb_clusters; ++ QCow2SubclusterType type; ++ int ret; ++ ++ offset_in_cluster = offset_into_cluster(s, offset); ++ bytes_needed = (uint64_t) *bytes + offset_in_cluster; ++ ++ /* compute how many bytes there are between the start of the cluster ++ * containing offset and the end of the l2 slice that contains ++ * the entry pointing to it */ ++ bytes_available = ++ ((uint64_t) (s->l2_slice_size - offset_to_l2_slice_index(s, offset))) ++ << s->cluster_bits; ++ ++ if (bytes_needed > bytes_available) { ++ bytes_needed = bytes_available; ++ } ++ ++ *host_offset = 0; ++ ++ /* seek to the l2 offset in the l1 table */ ++ ++ l1_index = offset_to_l1_index(s, offset); ++ if (l1_index >= s->l1_size) { ++ type = QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN; ++ goto out; ++ } ++ ++ l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; ++ if (!l2_offset) { ++ type = QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN; ++ goto out; ++ } ++ ++ if (offset_into_cluster(s, l2_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64 ++ " unaligned (L1 index: %#" PRIx64 ")", ++ l2_offset, l1_index); ++ return -EIO; ++ } ++ ++ /* load the l2 slice in memory */ ++ ++ ret = l2_load(bs, offset, l2_offset, &l2_slice); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* find the cluster offset for the given disk offset */ ++ ++ l2_index = offset_to_l2_slice_index(s, offset); ++ sc_index = offset_to_sc_index(s, offset); ++ l2_entry = get_l2_entry(s, l2_slice, l2_index); ++ l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index); ++ ++ nb_clusters = size_to_clusters(s, bytes_needed); ++ /* bytes_needed <= *bytes + offset_in_cluster, both of which are unsigned ++ * integers; the minimum cluster size is 512, so this assertion is always ++ * true */ ++ assert(nb_clusters <= INT_MAX); ++ ++ type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_index); ++ if (s->qcow_version < 3 && (type == QCOW2_SUBCLUSTER_ZERO_PLAIN || ++ type == QCOW2_SUBCLUSTER_ZERO_ALLOC)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found" ++ " in pre-v3 image (L2 offset: %#" PRIx64 ++ ", L2 index: %#x)", l2_offset, l2_index); ++ ret = -EIO; ++ goto fail; ++ } ++ switch (type) { ++ case QCOW2_SUBCLUSTER_INVALID: ++ break; /* This is handled by count_contiguous_subclusters() below */ ++ case QCOW2_SUBCLUSTER_COMPRESSED: ++ if (has_data_file(bs)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Compressed cluster " ++ "entry found in image with external data " ++ "file (L2 offset: %#" PRIx64 ", L2 index: " ++ "%#x)", l2_offset, l2_index); ++ ret = -EIO; ++ goto fail; ++ } ++ *host_offset = l2_entry; ++ break; ++ case QCOW2_SUBCLUSTER_ZERO_PLAIN: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: ++ break; ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ case QCOW2_SUBCLUSTER_NORMAL: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: { ++ uint64_t host_cluster_offset = l2_entry & L2E_OFFSET_MASK; ++ *host_offset = host_cluster_offset + offset_in_cluster; ++ if (offset_into_cluster(s, host_cluster_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, ++ "Cluster allocation offset %#" ++ PRIx64 " unaligned (L2 offset: %#" PRIx64 ++ ", L2 index: %#x)", host_cluster_offset, ++ l2_offset, l2_index); ++ ret = -EIO; ++ goto fail; ++ } ++ if (has_data_file(bs) && *host_offset != offset) { ++ qcow2_signal_corruption(bs, true, -1, -1, ++ "External data file host cluster offset %#" ++ PRIx64 " does not match guest cluster " ++ "offset: %#" PRIx64 ++ ", L2 index: %#x)", host_cluster_offset, ++ offset - offset_in_cluster, l2_index); ++ ret = -EIO; ++ goto fail; ++ } ++ break; ++ } ++ default: ++ abort(); ++ } ++ ++ sc = count_contiguous_subclusters(bs, nb_clusters, sc_index, ++ l2_slice, &l2_index); ++ if (sc < 0) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Invalid cluster entry found " ++ " (L2 offset: %#" PRIx64 ", L2 index: %#x)", ++ l2_offset, l2_index); ++ ret = -EIO; ++ goto fail; ++ } ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ bytes_available = ((int64_t)sc + sc_index) << s->subcluster_bits; ++ ++out: ++ if (bytes_available > bytes_needed) { ++ bytes_available = bytes_needed; ++ } ++ ++ /* bytes_available <= bytes_needed <= *bytes + offset_in_cluster; ++ * subtracting offset_in_cluster will therefore definitely yield something ++ * not exceeding UINT_MAX */ ++ assert(bytes_available - offset_in_cluster <= UINT_MAX); ++ *bytes = bytes_available - offset_in_cluster; ++ ++ *subcluster_type = type; ++ ++ return 0; ++ ++fail: ++ qcow2_cache_put(s->l2_table_cache, (void **)&l2_slice); ++ return ret; ++} ++ ++/* ++ * get_cluster_table ++ * ++ * for a given disk offset, load (and allocate if needed) ++ * the appropriate slice of its l2 table. ++ * ++ * the cluster index in the l2 slice is given to the caller. ++ * ++ * Returns 0 on success, -errno in failure case ++ */ ++static int GRAPH_RDLOCK ++get_cluster_table(BlockDriverState *bs, uint64_t offset, ++ uint64_t **new_l2_slice, int *new_l2_index) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ unsigned int l2_index; ++ uint64_t l1_index, l2_offset; ++ uint64_t *l2_slice = NULL; ++ int ret; ++ ++ /* seek to the l2 offset in the l1 table */ ++ ++ l1_index = offset_to_l1_index(s, offset); ++ if (l1_index >= s->l1_size) { ++ ret = qcow2_grow_l1_table(bs, l1_index + 1, false); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ assert(l1_index < s->l1_size); ++ l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; ++ if (offset_into_cluster(s, l2_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64 ++ " unaligned (L1 index: %#" PRIx64 ")", ++ l2_offset, l1_index); ++ return -EIO; ++ } ++ ++ if (!(s->l1_table[l1_index] & QCOW_OFLAG_COPIED)) { ++ /* First allocate a new L2 table (and do COW if needed) */ ++ ret = l2_allocate(bs, l1_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Then decrease the refcount of the old table */ ++ if (l2_offset) { ++ qcow2_free_clusters(bs, l2_offset, s->l2_size * l2_entry_size(s), ++ QCOW2_DISCARD_OTHER); ++ } ++ ++ /* Get the offset of the newly-allocated l2 table */ ++ l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; ++ assert(offset_into_cluster(s, l2_offset) == 0); ++ } ++ ++ /* load the l2 slice in memory */ ++ ret = l2_load(bs, offset, l2_offset, &l2_slice); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* find the cluster offset for the given disk offset */ ++ ++ l2_index = offset_to_l2_slice_index(s, offset); ++ ++ *new_l2_slice = l2_slice; ++ *new_l2_index = l2_index; ++ ++ return 0; ++} ++ ++/* ++ * alloc_compressed_cluster_offset ++ * ++ * For a given offset on the virtual disk, allocate a new compressed cluster ++ * and put the host offset of the cluster into *host_offset. If a cluster is ++ * already allocated at the offset, return an error. ++ * ++ * Return 0 on success and -errno in error cases ++ */ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, ++ int compressed_size, uint64_t *host_offset) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int l2_index, ret; ++ uint64_t *l2_slice; ++ int64_t cluster_offset; ++ int nb_csectors; ++ ++ if (has_data_file(bs)) { ++ return 0; ++ } ++ ++ ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Compression can't overwrite anything. Fail if the cluster was already ++ * allocated. */ ++ cluster_offset = get_l2_entry(s, l2_slice, l2_index); ++ if (cluster_offset & L2E_OFFSET_MASK) { ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ return -EIO; ++ } ++ ++ cluster_offset = qcow2_alloc_bytes(bs, compressed_size); ++ if (cluster_offset < 0) { ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ return cluster_offset; ++ } ++ ++ nb_csectors = ++ (cluster_offset + compressed_size - 1) / QCOW2_COMPRESSED_SECTOR_SIZE - ++ (cluster_offset / QCOW2_COMPRESSED_SECTOR_SIZE); ++ ++ /* The offset and size must fit in their fields of the L2 table entry */ ++ assert((cluster_offset & s->cluster_offset_mask) == cluster_offset); ++ assert((nb_csectors & s->csize_mask) == nb_csectors); ++ ++ cluster_offset |= QCOW_OFLAG_COMPRESSED | ++ ((uint64_t)nb_csectors << s->csize_shift); ++ ++ /* update L2 table */ ++ ++ /* compressed clusters never have the copied flag */ ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ set_l2_entry(s, l2_slice, l2_index, cluster_offset); ++ if (has_subclusters(s)) { ++ set_l2_bitmap(s, l2_slice, l2_index, 0); ++ } ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ *host_offset = cluster_offset & s->cluster_offset_mask; ++ return 0; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++perform_cow(BlockDriverState *bs, QCowL2Meta *m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2COWRegion *start = &m->cow_start; ++ Qcow2COWRegion *end = &m->cow_end; ++ unsigned buffer_size; ++ unsigned data_bytes = end->offset - (start->offset + start->nb_bytes); ++ bool merge_reads; ++ uint8_t *start_buffer, *end_buffer; ++ QEMUIOVector qiov; ++ int ret; ++ ++ assert(start->nb_bytes <= UINT_MAX - end->nb_bytes); ++ assert(start->nb_bytes + end->nb_bytes <= UINT_MAX - data_bytes); ++ assert(start->offset + start->nb_bytes <= end->offset); ++ ++ if ((start->nb_bytes == 0 && end->nb_bytes == 0) || m->skip_cow) { ++ return 0; ++ } ++ ++ /* If we have to read both the start and end COW regions and the ++ * middle region is not too large then perform just one read ++ * operation */ ++ merge_reads = start->nb_bytes && end->nb_bytes && data_bytes <= 16384; ++ if (merge_reads) { ++ buffer_size = start->nb_bytes + data_bytes + end->nb_bytes; ++ } else { ++ /* If we have to do two reads, add some padding in the middle ++ * if necessary to make sure that the end region is optimally ++ * aligned. */ ++ size_t align = bdrv_opt_mem_align(bs); ++ assert(align > 0 && align <= UINT_MAX); ++ assert(QEMU_ALIGN_UP(start->nb_bytes, align) <= ++ UINT_MAX - end->nb_bytes); ++ buffer_size = QEMU_ALIGN_UP(start->nb_bytes, align) + end->nb_bytes; ++ } ++ ++ /* Reserve a buffer large enough to store all the data that we're ++ * going to read */ ++ start_buffer = qemu_try_blockalign(bs, buffer_size); ++ if (start_buffer == NULL) { ++ return -ENOMEM; ++ } ++ /* The part of the buffer where the end region is located */ ++ end_buffer = start_buffer + buffer_size - end->nb_bytes; ++ ++ qemu_iovec_init(&qiov, 2 + (m->data_qiov ? ++ qemu_iovec_subvec_niov(m->data_qiov, ++ m->data_qiov_offset, ++ data_bytes) ++ : 0)); ++ ++ qemu_co_mutex_unlock(&s->lock); ++ /* First we read the existing data from both COW regions. We ++ * either read the whole region in one go, or the start and end ++ * regions separately. */ ++ if (merge_reads) { ++ qemu_iovec_add(&qiov, start_buffer, buffer_size); ++ ret = do_perform_cow_read(bs, m->offset, start->offset, &qiov); ++ } else { ++ qemu_iovec_add(&qiov, start_buffer, start->nb_bytes); ++ ret = do_perform_cow_read(bs, m->offset, start->offset, &qiov); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ qemu_iovec_reset(&qiov); ++ qemu_iovec_add(&qiov, end_buffer, end->nb_bytes); ++ ret = do_perform_cow_read(bs, m->offset, end->offset, &qiov); ++ } ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Encrypt the data if necessary before writing it */ ++ if (bs->encrypted) { ++ ret = qcow2_co_encrypt(bs, ++ m->alloc_offset + start->offset, ++ m->offset + start->offset, ++ start_buffer, start->nb_bytes); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_co_encrypt(bs, ++ m->alloc_offset + end->offset, ++ m->offset + end->offset, ++ end_buffer, end->nb_bytes); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ /* And now we can write everything. If we have the guest data we ++ * can write everything in one single operation */ ++ if (m->data_qiov) { ++ qemu_iovec_reset(&qiov); ++ if (start->nb_bytes) { ++ qemu_iovec_add(&qiov, start_buffer, start->nb_bytes); ++ } ++ qemu_iovec_concat(&qiov, m->data_qiov, m->data_qiov_offset, data_bytes); ++ if (end->nb_bytes) { ++ qemu_iovec_add(&qiov, end_buffer, end->nb_bytes); ++ } ++ /* NOTE: we have a write_aio blkdebug event here followed by ++ * a cow_write one in do_perform_cow_write(), but there's only ++ * one single I/O operation */ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); ++ ret = do_perform_cow_write(bs, m->alloc_offset, start->offset, &qiov); ++ } else { ++ /* If there's no guest data then write both COW regions separately */ ++ qemu_iovec_reset(&qiov); ++ qemu_iovec_add(&qiov, start_buffer, start->nb_bytes); ++ ret = do_perform_cow_write(bs, m->alloc_offset, start->offset, &qiov); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ qemu_iovec_reset(&qiov); ++ qemu_iovec_add(&qiov, end_buffer, end->nb_bytes); ++ ret = do_perform_cow_write(bs, m->alloc_offset, end->offset, &qiov); ++ } ++ ++fail: ++ qemu_co_mutex_lock(&s->lock); ++ ++ /* ++ * Before we update the L2 table to actually point to the new cluster, we ++ * need to be sure that the refcounts have been increased and COW was ++ * handled. ++ */ ++ if (ret == 0) { ++ qcow2_cache_depends_on_flush(s->l2_table_cache); ++ } ++ ++ qemu_vfree(start_buffer); ++ qemu_iovec_destroy(&qiov); ++ return ret; ++} ++ ++int coroutine_fn qcow2_alloc_cluster_link_l2(BlockDriverState *bs, ++ QCowL2Meta *m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i, j = 0, l2_index, ret; ++ uint64_t *old_cluster, *l2_slice; ++ uint64_t cluster_offset = m->alloc_offset; ++ ++ trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters); ++ assert(m->nb_clusters > 0); ++ ++ old_cluster = g_try_new(uint64_t, m->nb_clusters); ++ if (old_cluster == NULL) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ /* copy content of unmodified sectors */ ++ ret = perform_cow(bs, m); ++ if (ret < 0) { ++ goto err; ++ } ++ ++ /* Update L2 table. */ ++ if (s->use_lazy_refcounts) { ++ qcow2_mark_dirty(bs); ++ } ++ if (qcow2_need_accurate_refcounts(s)) { ++ qcow2_cache_set_dependency(bs, s->l2_table_cache, ++ s->refcount_block_cache); ++ } ++ ++ ret = get_cluster_table(bs, m->offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ goto err; ++ } ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ ++ assert(l2_index + m->nb_clusters <= s->l2_slice_size); ++ assert(m->cow_end.offset + m->cow_end.nb_bytes <= ++ m->nb_clusters << s->cluster_bits); ++ for (i = 0; i < m->nb_clusters; i++) { ++ uint64_t offset = cluster_offset + ((uint64_t)i << s->cluster_bits); ++ /* if two concurrent writes happen to the same unallocated cluster ++ * each write allocates separate cluster and writes data concurrently. ++ * The first one to complete updates l2 table with pointer to its ++ * cluster the second one has to do RMW (which is done above by ++ * perform_cow()), update l2 table with its cluster pointer and free ++ * old cluster. This is what this loop does */ ++ if (get_l2_entry(s, l2_slice, l2_index + i) != 0) { ++ old_cluster[j++] = get_l2_entry(s, l2_slice, l2_index + i); ++ } ++ ++ /* The offset must fit in the offset field of the L2 table entry */ ++ assert((offset & L2E_OFFSET_MASK) == offset); ++ ++ set_l2_entry(s, l2_slice, l2_index + i, offset | QCOW_OFLAG_COPIED); ++ ++ /* Update bitmap with the subclusters that were just written */ ++ if (has_subclusters(s) && !m->prealloc) { ++ uint64_t l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i); ++ unsigned written_from = m->cow_start.offset; ++ unsigned written_to = m->cow_end.offset + m->cow_end.nb_bytes; ++ int first_sc, last_sc; ++ /* Narrow written_from and written_to down to the current cluster */ ++ written_from = MAX(written_from, i << s->cluster_bits); ++ written_to = MIN(written_to, (i + 1) << s->cluster_bits); ++ assert(written_from < written_to); ++ first_sc = offset_to_sc_index(s, written_from); ++ last_sc = offset_to_sc_index(s, written_to - 1); ++ l2_bitmap |= QCOW_OFLAG_SUB_ALLOC_RANGE(first_sc, last_sc + 1); ++ l2_bitmap &= ~QCOW_OFLAG_SUB_ZERO_RANGE(first_sc, last_sc + 1); ++ set_l2_bitmap(s, l2_slice, l2_index + i, l2_bitmap); ++ } ++ } ++ ++ ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ /* ++ * If this was a COW, we need to decrease the refcount of the old cluster. ++ * ++ * Don't discard clusters that reach a refcount of 0 (e.g. compressed ++ * clusters), the next write will reuse them anyway. ++ */ ++ if (!m->keep_old_clusters && j != 0) { ++ for (i = 0; i < j; i++) { ++ qcow2_free_any_cluster(bs, old_cluster[i], QCOW2_DISCARD_NEVER); ++ } ++ } ++ ++ ret = 0; ++err: ++ g_free(old_cluster); ++ return ret; ++ } ++ ++/** ++ * Frees the allocated clusters because the request failed and they won't ++ * actually be linked. ++ */ ++void coroutine_fn qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ if (!has_data_file(bs) && !m->keep_old_clusters) { ++ qcow2_free_clusters(bs, m->alloc_offset, ++ m->nb_clusters << s->cluster_bits, ++ QCOW2_DISCARD_NEVER); ++ } ++} ++ ++/* ++ * For a given write request, create a new QCowL2Meta structure, add ++ * it to @m and the BDRVQcow2State.cluster_allocs list. If the write ++ * request does not need copy-on-write or changes to the L2 metadata ++ * then this function does nothing. ++ * ++ * @host_cluster_offset points to the beginning of the first cluster. ++ * ++ * @guest_offset and @bytes indicate the offset and length of the ++ * request. ++ * ++ * @l2_slice contains the L2 entries of all clusters involved in this ++ * write request. ++ * ++ * If @keep_old is true it means that the clusters were already ++ * allocated and will be overwritten. If false then the clusters are ++ * new and we have to decrease the reference count of the old ones. ++ * ++ * Returns 0 on success, -errno on failure. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++calculate_l2_meta(BlockDriverState *bs, uint64_t host_cluster_offset, ++ uint64_t guest_offset, unsigned bytes, uint64_t *l2_slice, ++ QCowL2Meta **m, bool keep_old) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int sc_index, l2_index = offset_to_l2_slice_index(s, guest_offset); ++ uint64_t l2_entry, l2_bitmap; ++ unsigned cow_start_from, cow_end_to; ++ unsigned cow_start_to = offset_into_cluster(s, guest_offset); ++ unsigned cow_end_from = cow_start_to + bytes; ++ unsigned nb_clusters = size_to_clusters(s, cow_end_from); ++ QCowL2Meta *old_m = *m; ++ QCow2SubclusterType type; ++ int i; ++ bool skip_cow = keep_old; ++ ++ assert(nb_clusters <= s->l2_slice_size - l2_index); ++ ++ /* Check the type of all affected subclusters */ ++ for (i = 0; i < nb_clusters; i++) { ++ l2_entry = get_l2_entry(s, l2_slice, l2_index + i); ++ l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i); ++ if (skip_cow) { ++ unsigned write_from = MAX(cow_start_to, i << s->cluster_bits); ++ unsigned write_to = MIN(cow_end_from, (i + 1) << s->cluster_bits); ++ int first_sc = offset_to_sc_index(s, write_from); ++ int last_sc = offset_to_sc_index(s, write_to - 1); ++ int cnt = qcow2_get_subcluster_range_type(bs, l2_entry, l2_bitmap, ++ first_sc, &type); ++ /* Is any of the subclusters of type != QCOW2_SUBCLUSTER_NORMAL ? */ ++ if (type != QCOW2_SUBCLUSTER_NORMAL || first_sc + cnt <= last_sc) { ++ skip_cow = false; ++ } ++ } else { ++ /* If we can't skip the cow we can still look for invalid entries */ ++ type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, 0); ++ } ++ if (type == QCOW2_SUBCLUSTER_INVALID) { ++ int l1_index = offset_to_l1_index(s, guest_offset); ++ uint64_t l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; ++ qcow2_signal_corruption(bs, true, -1, -1, "Invalid cluster " ++ "entry found (L2 offset: %#" PRIx64 ++ ", L2 index: %#x)", ++ l2_offset, l2_index + i); ++ return -EIO; ++ } ++ } ++ ++ if (skip_cow) { ++ return 0; ++ } ++ ++ /* Get the L2 entry of the first cluster */ ++ l2_entry = get_l2_entry(s, l2_slice, l2_index); ++ l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index); ++ sc_index = offset_to_sc_index(s, guest_offset); ++ type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_index); ++ ++ if (!keep_old) { ++ switch (type) { ++ case QCOW2_SUBCLUSTER_COMPRESSED: ++ cow_start_from = 0; ++ break; ++ case QCOW2_SUBCLUSTER_NORMAL: ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ if (has_subclusters(s)) { ++ /* Skip all leading zero and unallocated subclusters */ ++ uint32_t alloc_bitmap = l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC; ++ cow_start_from = ++ MIN(sc_index, ctz32(alloc_bitmap)) << s->subcluster_bits; ++ } else { ++ cow_start_from = 0; ++ } ++ break; ++ case QCOW2_SUBCLUSTER_ZERO_PLAIN: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: ++ cow_start_from = sc_index << s->subcluster_bits; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } else { ++ switch (type) { ++ case QCOW2_SUBCLUSTER_NORMAL: ++ cow_start_from = cow_start_to; ++ break; ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ cow_start_from = sc_index << s->subcluster_bits; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ ++ /* Get the L2 entry of the last cluster */ ++ l2_index += nb_clusters - 1; ++ l2_entry = get_l2_entry(s, l2_slice, l2_index); ++ l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index); ++ sc_index = offset_to_sc_index(s, guest_offset + bytes - 1); ++ type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_index); ++ ++ if (!keep_old) { ++ switch (type) { ++ case QCOW2_SUBCLUSTER_COMPRESSED: ++ cow_end_to = ROUND_UP(cow_end_from, s->cluster_size); ++ break; ++ case QCOW2_SUBCLUSTER_NORMAL: ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ cow_end_to = ROUND_UP(cow_end_from, s->cluster_size); ++ if (has_subclusters(s)) { ++ /* Skip all trailing zero and unallocated subclusters */ ++ uint32_t alloc_bitmap = l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC; ++ cow_end_to -= ++ MIN(s->subclusters_per_cluster - sc_index - 1, ++ clz32(alloc_bitmap)) << s->subcluster_bits; ++ } ++ break; ++ case QCOW2_SUBCLUSTER_ZERO_PLAIN: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: ++ cow_end_to = ROUND_UP(cow_end_from, s->subcluster_size); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } else { ++ switch (type) { ++ case QCOW2_SUBCLUSTER_NORMAL: ++ cow_end_to = cow_end_from; ++ break; ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ cow_end_to = ROUND_UP(cow_end_from, s->subcluster_size); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ ++ *m = g_malloc0(sizeof(**m)); ++ **m = (QCowL2Meta) { ++ .next = old_m, ++ ++ .alloc_offset = host_cluster_offset, ++ .offset = start_of_cluster(s, guest_offset), ++ .nb_clusters = nb_clusters, ++ ++ .keep_old_clusters = keep_old, ++ ++ .cow_start = { ++ .offset = cow_start_from, ++ .nb_bytes = cow_start_to - cow_start_from, ++ }, ++ .cow_end = { ++ .offset = cow_end_from, ++ .nb_bytes = cow_end_to - cow_end_from, ++ }, ++ }; ++ ++ qemu_co_queue_init(&(*m)->dependent_requests); ++ QLIST_INSERT_HEAD(&s->cluster_allocs, *m, next_in_flight); ++ ++ return 0; ++} ++ ++/* ++ * Returns true if writing to the cluster pointed to by @l2_entry ++ * requires a new allocation (that is, if the cluster is unallocated ++ * or has refcount > 1 and therefore cannot be written in-place). ++ */ ++static bool GRAPH_RDLOCK ++cluster_needs_new_alloc(BlockDriverState *bs, uint64_t l2_entry) ++{ ++ switch (qcow2_get_cluster_type(bs, l2_entry)) { ++ case QCOW2_CLUSTER_NORMAL: ++ case QCOW2_CLUSTER_ZERO_ALLOC: ++ if (l2_entry & QCOW_OFLAG_COPIED) { ++ return false; ++ } ++ /* fallthrough */ ++ case QCOW2_CLUSTER_UNALLOCATED: ++ case QCOW2_CLUSTER_COMPRESSED: ++ case QCOW2_CLUSTER_ZERO_PLAIN: ++ return true; ++ default: ++ abort(); ++ } ++} ++ ++/* ++ * Returns the number of contiguous clusters that can be written to ++ * using one single write request, starting from @l2_index. ++ * At most @nb_clusters are checked. ++ * ++ * If @new_alloc is true this counts clusters that are either ++ * unallocated, or allocated but with refcount > 1 (so they need to be ++ * newly allocated and COWed). ++ * ++ * If @new_alloc is false this counts clusters that are already ++ * allocated and can be overwritten in-place (this includes clusters ++ * of type QCOW2_CLUSTER_ZERO_ALLOC). ++ */ ++static int GRAPH_RDLOCK ++count_single_write_clusters(BlockDriverState *bs, int nb_clusters, ++ uint64_t *l2_slice, int l2_index, bool new_alloc) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t l2_entry = get_l2_entry(s, l2_slice, l2_index); ++ uint64_t expected_offset = l2_entry & L2E_OFFSET_MASK; ++ int i; ++ ++ for (i = 0; i < nb_clusters; i++) { ++ l2_entry = get_l2_entry(s, l2_slice, l2_index + i); ++ if (cluster_needs_new_alloc(bs, l2_entry) != new_alloc) { ++ break; ++ } ++ if (!new_alloc) { ++ if (expected_offset != (l2_entry & L2E_OFFSET_MASK)) { ++ break; ++ } ++ expected_offset += s->cluster_size; ++ } ++ } ++ ++ assert(i <= nb_clusters); ++ return i; ++} ++ ++/* ++ * Check if there already is an AIO write request in flight which allocates ++ * the same cluster. In this case we need to wait until the previous ++ * request has completed and updated the L2 table accordingly. ++ * ++ * Returns: ++ * 0 if there was no dependency. *cur_bytes indicates the number of ++ * bytes from guest_offset that can be read before the next ++ * dependency must be processed (or the request is complete) ++ * ++ * -EAGAIN if we had to wait for another request, previously gathered ++ * information on cluster allocation may be invalid now. The caller ++ * must start over anyway, so consider *cur_bytes undefined. ++ */ ++static int coroutine_fn handle_dependencies(BlockDriverState *bs, ++ uint64_t guest_offset, ++ uint64_t *cur_bytes, QCowL2Meta **m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowL2Meta *old_alloc; ++ uint64_t bytes = *cur_bytes; ++ ++ QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) { ++ ++ uint64_t start = guest_offset; ++ uint64_t end = start + bytes; ++ uint64_t old_start = start_of_cluster(s, l2meta_cow_start(old_alloc)); ++ uint64_t old_end = ROUND_UP(l2meta_cow_end(old_alloc), s->cluster_size); ++ ++ if (end <= old_start || start >= old_end) { ++ /* No intersection */ ++ continue; ++ } ++ ++ if (old_alloc->keep_old_clusters && ++ (end <= l2meta_cow_start(old_alloc) || ++ start >= l2meta_cow_end(old_alloc))) ++ { ++ /* ++ * Clusters intersect but COW areas don't. And cluster itself is ++ * already allocated. So, there is no actual conflict. ++ */ ++ continue; ++ } ++ ++ /* Conflict */ ++ ++ if (start < old_start) { ++ /* Stop at the start of a running allocation */ ++ bytes = old_start - start; ++ } else { ++ bytes = 0; ++ } ++ ++ /* ++ * Stop if an l2meta already exists. After yielding, it wouldn't ++ * be valid any more, so we'd have to clean up the old L2Metas ++ * and deal with requests depending on them before starting to ++ * gather new ones. Not worth the trouble. ++ */ ++ if (bytes == 0 && *m) { ++ *cur_bytes = 0; ++ return 0; ++ } ++ ++ if (bytes == 0) { ++ /* ++ * Wait for the dependency to complete. We need to recheck ++ * the free/allocated clusters when we continue. ++ */ ++ qemu_co_queue_wait(&old_alloc->dependent_requests, &s->lock); ++ return -EAGAIN; ++ } ++ } ++ ++ /* Make sure that existing clusters and new allocations are only used up to ++ * the next dependency if we shortened the request above */ ++ *cur_bytes = bytes; ++ ++ return 0; ++} ++ ++/* ++ * Checks how many already allocated clusters that don't require a new ++ * allocation there are at the given guest_offset (up to *bytes). ++ * If *host_offset is not INV_OFFSET, only physically contiguous clusters ++ * beginning at this host offset are counted. ++ * ++ * Note that guest_offset may not be cluster aligned. In this case, the ++ * returned *host_offset points to exact byte referenced by guest_offset and ++ * therefore isn't cluster aligned as well. ++ * ++ * Returns: ++ * 0: if no allocated clusters are available at the given offset. ++ * *bytes is normally unchanged. It is set to 0 if the cluster ++ * is allocated and can be overwritten in-place but doesn't have ++ * the right physical offset. ++ * ++ * 1: if allocated clusters that can be overwritten in place are ++ * available at the requested offset. *bytes may have decreased ++ * and describes the length of the area that can be written to. ++ * ++ * -errno: in error cases ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++handle_copied(BlockDriverState *bs, uint64_t guest_offset, ++ uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int l2_index; ++ uint64_t l2_entry, cluster_offset; ++ uint64_t *l2_slice; ++ uint64_t nb_clusters; ++ unsigned int keep_clusters; ++ int ret; ++ ++ trace_qcow2_handle_copied(qemu_coroutine_self(), guest_offset, *host_offset, ++ *bytes); ++ ++ assert(*host_offset == INV_OFFSET || offset_into_cluster(s, guest_offset) ++ == offset_into_cluster(s, *host_offset)); ++ ++ /* ++ * Calculate the number of clusters to look for. We stop at L2 slice ++ * boundaries to keep things simple. ++ */ ++ nb_clusters = ++ size_to_clusters(s, offset_into_cluster(s, guest_offset) + *bytes); ++ ++ l2_index = offset_to_l2_slice_index(s, guest_offset); ++ nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); ++ /* Limit total byte count to BDRV_REQUEST_MAX_BYTES */ ++ nb_clusters = MIN(nb_clusters, BDRV_REQUEST_MAX_BYTES >> s->cluster_bits); ++ ++ /* Find L2 entry for the first involved cluster */ ++ ret = get_cluster_table(bs, guest_offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ l2_entry = get_l2_entry(s, l2_slice, l2_index); ++ cluster_offset = l2_entry & L2E_OFFSET_MASK; ++ ++ if (!cluster_needs_new_alloc(bs, l2_entry)) { ++ if (offset_into_cluster(s, cluster_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "%s cluster offset " ++ "%#" PRIx64 " unaligned (guest offset: %#" ++ PRIx64 ")", l2_entry & QCOW_OFLAG_ZERO ? ++ "Preallocated zero" : "Data", ++ cluster_offset, guest_offset); ++ ret = -EIO; ++ goto out; ++ } ++ ++ /* If a specific host_offset is required, check it */ ++ if (*host_offset != INV_OFFSET && cluster_offset != *host_offset) { ++ *bytes = 0; ++ ret = 0; ++ goto out; ++ } ++ ++ /* We keep all QCOW_OFLAG_COPIED clusters */ ++ keep_clusters = count_single_write_clusters(bs, nb_clusters, l2_slice, ++ l2_index, false); ++ assert(keep_clusters <= nb_clusters); ++ ++ *bytes = MIN(*bytes, ++ keep_clusters * s->cluster_size ++ - offset_into_cluster(s, guest_offset)); ++ assert(*bytes != 0); ++ ++ ret = calculate_l2_meta(bs, cluster_offset, guest_offset, ++ *bytes, l2_slice, m, true); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = 1; ++ } else { ++ ret = 0; ++ } ++ ++ /* Cleanup */ ++out: ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ /* Only return a host offset if we actually made progress. Otherwise we ++ * would make requirements for handle_alloc() that it can't fulfill */ ++ if (ret > 0) { ++ *host_offset = cluster_offset + offset_into_cluster(s, guest_offset); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Allocates new clusters for the given guest_offset. ++ * ++ * At most *nb_clusters are allocated, and on return *nb_clusters is updated to ++ * contain the number of clusters that have been allocated and are contiguous ++ * in the image file. ++ * ++ * If *host_offset is not INV_OFFSET, it specifies the offset in the image file ++ * at which the new clusters must start. *nb_clusters can be 0 on return in ++ * this case if the cluster at host_offset is already in use. If *host_offset ++ * is INV_OFFSET, the clusters can be allocated anywhere in the image file. ++ * ++ * *host_offset is updated to contain the offset into the image file at which ++ * the first allocated cluster starts. ++ * ++ * Return 0 on success and -errno in error cases. -EAGAIN means that the ++ * function has been waiting for another request and the allocation must be ++ * restarted, but the whole request should not be failed. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, ++ uint64_t *host_offset, uint64_t *nb_clusters) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset, ++ *host_offset, *nb_clusters); ++ ++ if (has_data_file(bs)) { ++ assert(*host_offset == INV_OFFSET || ++ *host_offset == start_of_cluster(s, guest_offset)); ++ *host_offset = start_of_cluster(s, guest_offset); ++ return 0; ++ } ++ ++ /* Allocate new clusters */ ++ trace_qcow2_cluster_alloc_phys(qemu_coroutine_self()); ++ if (*host_offset == INV_OFFSET) { ++ int64_t cluster_offset = ++ qcow2_alloc_clusters(bs, *nb_clusters * s->cluster_size); ++ if (cluster_offset < 0) { ++ return cluster_offset; ++ } ++ *host_offset = cluster_offset; ++ return 0; ++ } else { ++ int64_t ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters); ++ if (ret < 0) { ++ return ret; ++ } ++ *nb_clusters = ret; ++ return 0; ++ } ++} ++ ++/* ++ * Allocates new clusters for an area that is either still unallocated or ++ * cannot be overwritten in-place. If *host_offset is not INV_OFFSET, ++ * clusters are only allocated if the new allocation can match the specified ++ * host offset. ++ * ++ * Note that guest_offset may not be cluster aligned. In this case, the ++ * returned *host_offset points to exact byte referenced by guest_offset and ++ * therefore isn't cluster aligned as well. ++ * ++ * Returns: ++ * 0: if no clusters could be allocated. *bytes is set to 0, ++ * *host_offset is left unchanged. ++ * ++ * 1: if new clusters were allocated. *bytes may be decreased if the ++ * new allocation doesn't cover all of the requested area. ++ * *host_offset is updated to contain the host offset of the first ++ * newly allocated cluster. ++ * ++ * -errno: in error cases ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++handle_alloc(BlockDriverState *bs, uint64_t guest_offset, ++ uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int l2_index; ++ uint64_t *l2_slice; ++ uint64_t nb_clusters; ++ int ret; ++ ++ uint64_t alloc_cluster_offset; ++ ++ trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset, ++ *bytes); ++ assert(*bytes > 0); ++ ++ /* ++ * Calculate the number of clusters to look for. We stop at L2 slice ++ * boundaries to keep things simple. ++ */ ++ nb_clusters = ++ size_to_clusters(s, offset_into_cluster(s, guest_offset) + *bytes); ++ ++ l2_index = offset_to_l2_slice_index(s, guest_offset); ++ nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); ++ /* Limit total allocation byte count to BDRV_REQUEST_MAX_BYTES */ ++ nb_clusters = MIN(nb_clusters, BDRV_REQUEST_MAX_BYTES >> s->cluster_bits); ++ ++ /* Find L2 entry for the first involved cluster */ ++ ret = get_cluster_table(bs, guest_offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ nb_clusters = count_single_write_clusters(bs, nb_clusters, ++ l2_slice, l2_index, true); ++ ++ /* This function is only called when there were no non-COW clusters, so if ++ * we can't find any unallocated or COW clusters either, something is ++ * wrong with our code. */ ++ assert(nb_clusters > 0); ++ ++ /* Allocate at a given offset in the image file */ ++ alloc_cluster_offset = *host_offset == INV_OFFSET ? INV_OFFSET : ++ start_of_cluster(s, *host_offset); ++ ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset, ++ &nb_clusters); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ /* Can't extend contiguous allocation */ ++ if (nb_clusters == 0) { ++ *bytes = 0; ++ ret = 0; ++ goto out; ++ } ++ ++ assert(alloc_cluster_offset != INV_OFFSET); ++ ++ /* ++ * Save info needed for meta data update. ++ * ++ * requested_bytes: Number of bytes from the start of the first ++ * newly allocated cluster to the end of the (possibly shortened ++ * before) write request. ++ * ++ * avail_bytes: Number of bytes from the start of the first ++ * newly allocated to the end of the last newly allocated cluster. ++ * ++ * nb_bytes: The number of bytes from the start of the first ++ * newly allocated cluster to the end of the area that the write ++ * request actually writes to (excluding COW at the end) ++ */ ++ uint64_t requested_bytes = *bytes + offset_into_cluster(s, guest_offset); ++ int avail_bytes = nb_clusters << s->cluster_bits; ++ int nb_bytes = MIN(requested_bytes, avail_bytes); ++ ++ *host_offset = alloc_cluster_offset + offset_into_cluster(s, guest_offset); ++ *bytes = MIN(*bytes, nb_bytes - offset_into_cluster(s, guest_offset)); ++ assert(*bytes != 0); ++ ++ ret = calculate_l2_meta(bs, alloc_cluster_offset, guest_offset, *bytes, ++ l2_slice, m, false); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = 1; ++ ++out: ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ return ret; ++} ++ ++/* ++ * For a given area on the virtual disk defined by @offset and @bytes, ++ * find the corresponding area on the qcow2 image, allocating new ++ * clusters (or subclusters) if necessary. The result can span a ++ * combination of allocated and previously unallocated clusters. ++ * ++ * Note that offset may not be cluster aligned. In this case, the returned ++ * *host_offset points to exact byte referenced by offset and therefore ++ * isn't cluster aligned as well. ++ * ++ * On return, @host_offset is set to the beginning of the requested ++ * area. This area is guaranteed to be contiguous on the qcow2 file ++ * but it can be smaller than initially requested. In this case @bytes ++ * is updated with the actual size. ++ * ++ * If any clusters or subclusters were allocated then @m contains a ++ * list with the information of all the affected regions. Note that ++ * this can happen regardless of whether this function succeeds or ++ * not. The caller is responsible for updating the L2 metadata of the ++ * allocated clusters (on success) or freeing them (on failure), and ++ * for clearing the contents of @m afterwards in both cases. ++ * ++ * If the request conflicts with another write request in flight, the coroutine ++ * is queued and will be reentered when the dependency has completed. ++ * ++ * Return 0 on success and -errno in error cases ++ */ ++int coroutine_fn qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, ++ unsigned int *bytes, ++ uint64_t *host_offset, ++ QCowL2Meta **m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t start, remaining; ++ uint64_t cluster_offset; ++ uint64_t cur_bytes; ++ int ret; ++ ++ trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, *bytes); ++ ++again: ++ start = offset; ++ remaining = *bytes; ++ cluster_offset = INV_OFFSET; ++ *host_offset = INV_OFFSET; ++ cur_bytes = 0; ++ *m = NULL; ++ ++ while (true) { ++ ++ if (*host_offset == INV_OFFSET && cluster_offset != INV_OFFSET) { ++ *host_offset = cluster_offset; ++ } ++ ++ assert(remaining >= cur_bytes); ++ ++ start += cur_bytes; ++ remaining -= cur_bytes; ++ ++ if (cluster_offset != INV_OFFSET) { ++ cluster_offset += cur_bytes; ++ } ++ ++ if (remaining == 0) { ++ break; ++ } ++ ++ cur_bytes = remaining; ++ ++ /* ++ * Now start gathering as many contiguous clusters as possible: ++ * ++ * 1. Check for overlaps with in-flight allocations ++ * ++ * a) Overlap not in the first cluster -> shorten this request and ++ * let the caller handle the rest in its next loop iteration. ++ * ++ * b) Real overlaps of two requests. Yield and restart the search ++ * for contiguous clusters (the situation could have changed ++ * while we were sleeping) ++ * ++ * c) TODO: Request starts in the same cluster as the in-flight ++ * allocation ends. Shorten the COW of the in-fight allocation, ++ * set cluster_offset to write to the same cluster and set up ++ * the right synchronisation between the in-flight request and ++ * the new one. ++ */ ++ ret = handle_dependencies(bs, start, &cur_bytes, m); ++ if (ret == -EAGAIN) { ++ /* Currently handle_dependencies() doesn't yield if we already had ++ * an allocation. If it did, we would have to clean up the L2Meta ++ * structs before starting over. */ ++ assert(*m == NULL); ++ goto again; ++ } else if (ret < 0) { ++ return ret; ++ } else if (cur_bytes == 0) { ++ break; ++ } else { ++ /* handle_dependencies() may have decreased cur_bytes (shortened ++ * the allocations below) so that the next dependency is processed ++ * correctly during the next loop iteration. */ ++ } ++ ++ /* ++ * 2. Count contiguous COPIED clusters. ++ */ ++ ret = handle_copied(bs, start, &cluster_offset, &cur_bytes, m); ++ if (ret < 0) { ++ return ret; ++ } else if (ret) { ++ continue; ++ } else if (cur_bytes == 0) { ++ break; ++ } ++ ++ /* ++ * 3. If the request still hasn't completed, allocate new clusters, ++ * considering any cluster_offset of steps 1c or 2. ++ */ ++ ret = handle_alloc(bs, start, &cluster_offset, &cur_bytes, m); ++ if (ret < 0) { ++ return ret; ++ } else if (ret) { ++ continue; ++ } else { ++ assert(cur_bytes == 0); ++ break; ++ } ++ } ++ ++ *bytes -= remaining; ++ assert(*bytes > 0); ++ assert(*host_offset != INV_OFFSET); ++ assert(offset_into_cluster(s, *host_offset) == ++ offset_into_cluster(s, offset)); ++ ++ return 0; ++} ++ ++/* ++ * This discards as many clusters of nb_clusters as possible at once (i.e. ++ * all clusters in the same L2 slice) and returns the number of discarded ++ * clusters. ++ */ ++static int GRAPH_RDLOCK ++discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, uint64_t nb_clusters, ++ enum qcow2_discard_type type, bool full_discard) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *l2_slice; ++ int l2_index; ++ int ret; ++ int i; ++ ++ ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Limit nb_clusters to one L2 slice */ ++ nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); ++ assert(nb_clusters <= INT_MAX); ++ ++ for (i = 0; i < nb_clusters; i++) { ++ uint64_t old_l2_entry = get_l2_entry(s, l2_slice, l2_index + i); ++ uint64_t old_l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i); ++ uint64_t new_l2_entry = old_l2_entry; ++ uint64_t new_l2_bitmap = old_l2_bitmap; ++ QCow2ClusterType cluster_type = ++ qcow2_get_cluster_type(bs, old_l2_entry); ++ bool keep_reference = (cluster_type != QCOW2_CLUSTER_COMPRESSED) && ++ !full_discard && ++ (s->discard_no_unref && ++ type == QCOW2_DISCARD_REQUEST); ++ ++ /* ++ * If full_discard is true, the cluster should not read back as zeroes, ++ * but rather fall through to the backing file. ++ * ++ * If full_discard is false, make sure that a discarded area reads back ++ * as zeroes for v3 images (we cannot do it for v2 without actually ++ * writing a zero-filled buffer). We can skip the operation if the ++ * cluster is already marked as zero, or if it's unallocated and we ++ * don't have a backing file. ++ * ++ * TODO We might want to use bdrv_block_status(bs) here, but we're ++ * holding s->lock, so that doesn't work today. ++ */ ++ if (full_discard) { ++ new_l2_entry = new_l2_bitmap = 0; ++ } else if (bs->backing || qcow2_cluster_is_allocated(cluster_type)) { ++ if (has_subclusters(s)) { ++ if (keep_reference) { ++ new_l2_entry = old_l2_entry; ++ } else { ++ new_l2_entry = 0; ++ } ++ new_l2_bitmap = QCOW_L2_BITMAP_ALL_ZEROES; ++ } else { ++ if (s->qcow_version >= 3) { ++ if (keep_reference) { ++ new_l2_entry |= QCOW_OFLAG_ZERO; ++ } else { ++ new_l2_entry = QCOW_OFLAG_ZERO; ++ } ++ } else { ++ new_l2_entry = 0; ++ } ++ } ++ } ++ ++ if (old_l2_entry == new_l2_entry && old_l2_bitmap == new_l2_bitmap) { ++ continue; ++ } ++ ++ /* First remove L2 entries */ ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ set_l2_entry(s, l2_slice, l2_index + i, new_l2_entry); ++ if (has_subclusters(s)) { ++ set_l2_bitmap(s, l2_slice, l2_index + i, new_l2_bitmap); ++ } ++ if (!keep_reference) { ++ /* Then decrease the refcount */ ++ qcow2_free_any_cluster(bs, old_l2_entry, type); ++ } else if (s->discard_passthrough[type] && ++ (cluster_type == QCOW2_CLUSTER_NORMAL || ++ cluster_type == QCOW2_CLUSTER_ZERO_ALLOC)) { ++ /* If we keep the reference, pass on the discard still */ ++ bdrv_pdiscard(s->data_file, old_l2_entry & L2E_OFFSET_MASK, ++ s->cluster_size); ++ } ++ } ++ ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ return nb_clusters; ++} ++ ++int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, ++ uint64_t bytes, enum qcow2_discard_type type, ++ bool full_discard) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t end_offset = offset + bytes; ++ uint64_t nb_clusters; ++ int64_t cleared; ++ int ret; ++ ++ /* Caller must pass aligned values, except at image end */ ++ assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); ++ assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) || ++ end_offset == bs->total_sectors << BDRV_SECTOR_BITS); ++ ++ nb_clusters = size_to_clusters(s, bytes); ++ ++ s->cache_discards = true; ++ ++ /* Each L2 slice is handled by its own loop iteration */ ++ while (nb_clusters > 0) { ++ cleared = discard_in_l2_slice(bs, offset, nb_clusters, type, ++ full_discard); ++ if (cleared < 0) { ++ ret = cleared; ++ goto fail; ++ } ++ ++ nb_clusters -= cleared; ++ offset += (cleared * s->cluster_size); ++ } ++ ++ ret = 0; ++fail: ++ s->cache_discards = false; ++ qcow2_process_discards(bs, ret); ++ ++ return ret; ++} ++ ++/* ++ * This zeroes as many clusters of nb_clusters as possible at once (i.e. ++ * all clusters in the same L2 slice) and returns the number of zeroed ++ * clusters. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, ++ uint64_t nb_clusters, int flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *l2_slice; ++ int l2_index; ++ int ret; ++ int i; ++ ++ ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Limit nb_clusters to one L2 slice */ ++ nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); ++ assert(nb_clusters <= INT_MAX); ++ ++ for (i = 0; i < nb_clusters; i++) { ++ uint64_t old_l2_entry = get_l2_entry(s, l2_slice, l2_index + i); ++ uint64_t old_l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i); ++ QCow2ClusterType type = qcow2_get_cluster_type(bs, old_l2_entry); ++ bool unmap = (type == QCOW2_CLUSTER_COMPRESSED) || ++ ((flags & BDRV_REQ_MAY_UNMAP) && qcow2_cluster_is_allocated(type)); ++ bool keep_reference = ++ (s->discard_no_unref && type != QCOW2_CLUSTER_COMPRESSED); ++ uint64_t new_l2_entry = old_l2_entry; ++ uint64_t new_l2_bitmap = old_l2_bitmap; ++ ++ if (unmap && !keep_reference) { ++ new_l2_entry = 0; ++ } ++ ++ if (has_subclusters(s)) { ++ new_l2_bitmap = QCOW_L2_BITMAP_ALL_ZEROES; ++ } else { ++ new_l2_entry |= QCOW_OFLAG_ZERO; ++ } ++ ++ if (old_l2_entry == new_l2_entry && old_l2_bitmap == new_l2_bitmap) { ++ continue; ++ } ++ ++ /* First update L2 entries */ ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ set_l2_entry(s, l2_slice, l2_index + i, new_l2_entry); ++ if (has_subclusters(s)) { ++ set_l2_bitmap(s, l2_slice, l2_index + i, new_l2_bitmap); ++ } ++ ++ if (unmap) { ++ if (!keep_reference) { ++ /* Then decrease the refcount */ ++ qcow2_free_any_cluster(bs, old_l2_entry, QCOW2_DISCARD_REQUEST); ++ } else if (s->discard_passthrough[QCOW2_DISCARD_REQUEST] && ++ (type == QCOW2_CLUSTER_NORMAL || ++ type == QCOW2_CLUSTER_ZERO_ALLOC)) { ++ /* If we keep the reference, pass on the discard still */ ++ bdrv_pdiscard(s->data_file, old_l2_entry & L2E_OFFSET_MASK, ++ s->cluster_size); ++ } ++ } ++ } ++ ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ return nb_clusters; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++zero_l2_subclusters(BlockDriverState *bs, uint64_t offset, ++ unsigned nb_subclusters) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *l2_slice; ++ uint64_t old_l2_bitmap, l2_bitmap; ++ int l2_index, ret, sc = offset_to_sc_index(s, offset); ++ ++ /* For full clusters use zero_in_l2_slice() instead */ ++ assert(nb_subclusters > 0 && nb_subclusters < s->subclusters_per_cluster); ++ assert(sc + nb_subclusters <= s->subclusters_per_cluster); ++ assert(offset_into_subcluster(s, offset) == 0); ++ ++ ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ switch (qcow2_get_cluster_type(bs, get_l2_entry(s, l2_slice, l2_index))) { ++ case QCOW2_CLUSTER_COMPRESSED: ++ ret = -ENOTSUP; /* We cannot partially zeroize compressed clusters */ ++ goto out; ++ case QCOW2_CLUSTER_NORMAL: ++ case QCOW2_CLUSTER_UNALLOCATED: ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ ++ old_l2_bitmap = l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index); ++ ++ l2_bitmap |= QCOW_OFLAG_SUB_ZERO_RANGE(sc, sc + nb_subclusters); ++ l2_bitmap &= ~QCOW_OFLAG_SUB_ALLOC_RANGE(sc, sc + nb_subclusters); ++ ++ if (old_l2_bitmap != l2_bitmap) { ++ set_l2_bitmap(s, l2_slice, l2_index, l2_bitmap); ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ } ++ ++ ret = 0; ++out: ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ return ret; ++} ++ ++int coroutine_fn qcow2_subcluster_zeroize(BlockDriverState *bs, uint64_t offset, ++ uint64_t bytes, int flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t end_offset = offset + bytes; ++ uint64_t nb_clusters; ++ unsigned head, tail; ++ int64_t cleared; ++ int ret; ++ ++ /* If we have to stay in sync with an external data file, zero out ++ * s->data_file first. */ ++ if (data_file_is_raw(bs)) { ++ assert(has_data_file(bs)); ++ ret = bdrv_co_pwrite_zeroes(s->data_file, offset, bytes, flags); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ /* Caller must pass aligned values, except at image end */ ++ assert(offset_into_subcluster(s, offset) == 0); ++ assert(offset_into_subcluster(s, end_offset) == 0 || ++ end_offset >= bs->total_sectors << BDRV_SECTOR_BITS); ++ ++ /* ++ * The zero flag is only supported by version 3 and newer. However, if we ++ * have no backing file, we can resort to discard in version 2. ++ */ ++ if (s->qcow_version < 3) { ++ if (!bs->backing) { ++ return qcow2_cluster_discard(bs, offset, bytes, ++ QCOW2_DISCARD_REQUEST, false); ++ } ++ return -ENOTSUP; ++ } ++ ++ head = MIN(end_offset, ROUND_UP(offset, s->cluster_size)) - offset; ++ offset += head; ++ ++ tail = (end_offset >= bs->total_sectors << BDRV_SECTOR_BITS) ? 0 : ++ end_offset - MAX(offset, start_of_cluster(s, end_offset)); ++ end_offset -= tail; ++ ++ s->cache_discards = true; ++ ++ if (head) { ++ ret = zero_l2_subclusters(bs, offset - head, ++ size_to_subclusters(s, head)); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ /* Each L2 slice is handled by its own loop iteration */ ++ nb_clusters = size_to_clusters(s, end_offset - offset); ++ ++ while (nb_clusters > 0) { ++ cleared = zero_in_l2_slice(bs, offset, nb_clusters, flags); ++ if (cleared < 0) { ++ ret = cleared; ++ goto fail; ++ } ++ ++ nb_clusters -= cleared; ++ offset += (cleared * s->cluster_size); ++ } ++ ++ if (tail) { ++ ret = zero_l2_subclusters(bs, end_offset, size_to_subclusters(s, tail)); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ ret = 0; ++fail: ++ s->cache_discards = false; ++ qcow2_process_discards(bs, ret); ++ ++ return ret; ++} ++ ++/* ++ * Expands all zero clusters in a specific L1 table (or deallocates them, for ++ * non-backed non-pre-allocated zero clusters). ++ * ++ * l1_entries and *visited_l1_entries are used to keep track of progress for ++ * status_cb(). l1_entries contains the total number of L1 entries and ++ * *visited_l1_entries counts all visited L1 entries. ++ */ ++static int GRAPH_RDLOCK ++expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, ++ int l1_size, int64_t *visited_l1_entries, ++ int64_t l1_entries, ++ BlockDriverAmendStatusCB *status_cb, ++ void *cb_opaque) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ bool is_active_l1 = (l1_table == s->l1_table); ++ uint64_t *l2_slice = NULL; ++ unsigned slice, slice_size2, n_slices; ++ int ret; ++ int i, j; ++ ++ /* qcow2_downgrade() is not allowed in images with subclusters */ ++ assert(!has_subclusters(s)); ++ ++ slice_size2 = s->l2_slice_size * l2_entry_size(s); ++ n_slices = s->cluster_size / slice_size2; ++ ++ if (!is_active_l1) { ++ /* inactive L2 tables require a buffer to be stored in when loading ++ * them from disk */ ++ l2_slice = qemu_try_blockalign(bs->file->bs, slice_size2); ++ if (l2_slice == NULL) { ++ return -ENOMEM; ++ } ++ } ++ ++ for (i = 0; i < l1_size; i++) { ++ uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK; ++ uint64_t l2_refcount; ++ ++ if (!l2_offset) { ++ /* unallocated */ ++ (*visited_l1_entries)++; ++ if (status_cb) { ++ status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque); ++ } ++ continue; ++ } ++ ++ if (offset_into_cluster(s, l2_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" ++ PRIx64 " unaligned (L1 index: %#x)", ++ l2_offset, i); ++ ret = -EIO; ++ goto fail; ++ } ++ ++ ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, ++ &l2_refcount); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (slice = 0; slice < n_slices; slice++) { ++ uint64_t slice_offset = l2_offset + slice * slice_size2; ++ bool l2_dirty = false; ++ if (is_active_l1) { ++ /* get active L2 tables from cache */ ++ ret = qcow2_cache_get(bs, s->l2_table_cache, slice_offset, ++ (void **)&l2_slice); ++ } else { ++ /* load inactive L2 tables from disk */ ++ ret = bdrv_pread(bs->file, slice_offset, slice_size2, ++ l2_slice, 0); ++ } ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (j = 0; j < s->l2_slice_size; j++) { ++ uint64_t l2_entry = get_l2_entry(s, l2_slice, j); ++ int64_t offset = l2_entry & L2E_OFFSET_MASK; ++ QCow2ClusterType cluster_type = ++ qcow2_get_cluster_type(bs, l2_entry); ++ ++ if (cluster_type != QCOW2_CLUSTER_ZERO_PLAIN && ++ cluster_type != QCOW2_CLUSTER_ZERO_ALLOC) { ++ continue; ++ } ++ ++ if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { ++ if (!bs->backing) { ++ /* ++ * not backed; therefore we can simply deallocate the ++ * cluster. No need to call set_l2_bitmap(), this ++ * function doesn't support images with subclusters. ++ */ ++ set_l2_entry(s, l2_slice, j, 0); ++ l2_dirty = true; ++ continue; ++ } ++ ++ offset = qcow2_alloc_clusters(bs, s->cluster_size); ++ if (offset < 0) { ++ ret = offset; ++ goto fail; ++ } ++ ++ /* The offset must fit in the offset field */ ++ assert((offset & L2E_OFFSET_MASK) == offset); ++ ++ if (l2_refcount > 1) { ++ /* For shared L2 tables, set the refcount accordingly ++ * (it is already 1 and needs to be l2_refcount) */ ++ ret = qcow2_update_cluster_refcount( ++ bs, offset >> s->cluster_bits, ++ refcount_diff(1, l2_refcount), false, ++ QCOW2_DISCARD_OTHER); ++ if (ret < 0) { ++ qcow2_free_clusters(bs, offset, s->cluster_size, ++ QCOW2_DISCARD_OTHER); ++ goto fail; ++ } ++ } ++ } ++ ++ if (offset_into_cluster(s, offset)) { ++ int l2_index = slice * s->l2_slice_size + j; ++ qcow2_signal_corruption( ++ bs, true, -1, -1, ++ "Cluster allocation offset " ++ "%#" PRIx64 " unaligned (L2 offset: %#" ++ PRIx64 ", L2 index: %#x)", offset, ++ l2_offset, l2_index); ++ if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { ++ qcow2_free_clusters(bs, offset, s->cluster_size, ++ QCOW2_DISCARD_ALWAYS); ++ } ++ ret = -EIO; ++ goto fail; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, offset, ++ s->cluster_size, true); ++ if (ret < 0) { ++ if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { ++ qcow2_free_clusters(bs, offset, s->cluster_size, ++ QCOW2_DISCARD_ALWAYS); ++ } ++ goto fail; ++ } ++ ++ ret = bdrv_pwrite_zeroes(s->data_file, offset, ++ s->cluster_size, 0); ++ if (ret < 0) { ++ if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { ++ qcow2_free_clusters(bs, offset, s->cluster_size, ++ QCOW2_DISCARD_ALWAYS); ++ } ++ goto fail; ++ } ++ ++ if (l2_refcount == 1) { ++ set_l2_entry(s, l2_slice, j, offset | QCOW_OFLAG_COPIED); ++ } else { ++ set_l2_entry(s, l2_slice, j, offset); ++ } ++ /* ++ * No need to call set_l2_bitmap() after set_l2_entry() because ++ * this function doesn't support images with subclusters. ++ */ ++ l2_dirty = true; ++ } ++ ++ if (is_active_l1) { ++ if (l2_dirty) { ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ qcow2_cache_depends_on_flush(s->l2_table_cache); ++ } ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ } else { ++ if (l2_dirty) { ++ ret = qcow2_pre_write_overlap_check( ++ bs, QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, ++ slice_offset, slice_size2, false); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = bdrv_pwrite(bs->file, slice_offset, slice_size2, ++ l2_slice, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ } ++ } ++ ++ (*visited_l1_entries)++; ++ if (status_cb) { ++ status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque); ++ } ++ } ++ ++ ret = 0; ++ ++fail: ++ if (l2_slice) { ++ if (!is_active_l1) { ++ qemu_vfree(l2_slice); ++ } else { ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ } ++ } ++ return ret; ++} ++ ++/* ++ * For backed images, expands all zero clusters on the image. For non-backed ++ * images, deallocates all non-pre-allocated zero clusters (and claims the ++ * allocation for pre-allocated ones). This is important for downgrading to a ++ * qcow2 version which doesn't yet support metadata zero clusters. ++ */ ++int qcow2_expand_zero_clusters(BlockDriverState *bs, ++ BlockDriverAmendStatusCB *status_cb, ++ void *cb_opaque) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *l1_table = NULL; ++ int64_t l1_entries = 0, visited_l1_entries = 0; ++ int ret; ++ int i, j; ++ ++ if (status_cb) { ++ l1_entries = s->l1_size; ++ for (i = 0; i < s->nb_snapshots; i++) { ++ l1_entries += s->snapshots[i].l1_size; ++ } ++ } ++ ++ ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size, ++ &visited_l1_entries, l1_entries, ++ status_cb, cb_opaque); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Inactive L1 tables may point to active L2 tables - therefore it is ++ * necessary to flush the L2 table cache before trying to access the L2 ++ * tables pointed to by inactive L1 entries (else we might try to expand ++ * zero clusters that have already been expanded); furthermore, it is also ++ * necessary to empty the L2 table cache, since it may contain tables which ++ * are now going to be modified directly on disk, bypassing the cache. ++ * qcow2_cache_empty() does both for us. */ ++ ret = qcow2_cache_empty(bs, s->l2_table_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (i = 0; i < s->nb_snapshots; i++) { ++ int l1_size2; ++ uint64_t *new_l1_table; ++ Error *local_err = NULL; ++ ++ ret = qcow2_validate_table(bs, s->snapshots[i].l1_table_offset, ++ s->snapshots[i].l1_size, L1E_SIZE, ++ QCOW_MAX_L1_SIZE, "Snapshot L1 table", ++ &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto fail; ++ } ++ ++ l1_size2 = s->snapshots[i].l1_size * L1E_SIZE; ++ new_l1_table = g_try_realloc(l1_table, l1_size2); ++ ++ if (!new_l1_table) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ l1_table = new_l1_table; ++ ++ ret = bdrv_pread(bs->file, s->snapshots[i].l1_table_offset, l1_size2, ++ l1_table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (j = 0; j < s->snapshots[i].l1_size; j++) { ++ be64_to_cpus(&l1_table[j]); ++ } ++ ++ ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size, ++ &visited_l1_entries, l1_entries, ++ status_cb, cb_opaque); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ ret = 0; ++ ++fail: ++ g_free(l1_table); ++ return ret; ++} ++ ++void qcow2_parse_compressed_l2_entry(BlockDriverState *bs, uint64_t l2_entry, ++ uint64_t *coffset, int *csize) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int nb_csectors; ++ ++ assert(qcow2_get_cluster_type(bs, l2_entry) == QCOW2_CLUSTER_COMPRESSED); ++ ++ *coffset = l2_entry & s->cluster_offset_mask; ++ ++ nb_csectors = ((l2_entry >> s->csize_shift) & s->csize_mask) + 1; ++ *csize = nb_csectors * QCOW2_COMPRESSED_SECTOR_SIZE - ++ (*coffset & (QCOW2_COMPRESSED_SECTOR_SIZE - 1)); ++} +diff --git a/qcow2/lib/qcow2-refcount.c b/qcow2/lib/qcow2-refcount.c +new file mode 100644 +index 00000000..0266542c +--- /dev/null ++++ b/qcow2/lib/qcow2-refcount.c +@@ -0,0 +1,3750 @@ ++/* ++ * Block driver for the QCOW version 2 format ++ * ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/block-io.h" ++#include "qapi/error.h" ++#include "qcow2.h" ++#include "qemu/range.h" ++#include "qemu/bswap.h" ++#include "qemu/cutils.h" ++#include "qemu/memalign.h" ++#include "trace.h" ++ ++static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, ++ uint64_t max); ++ ++G_GNUC_WARN_UNUSED_RESULT ++static int update_refcount(BlockDriverState *bs, ++ int64_t offset, int64_t length, uint64_t addend, ++ bool decrease, enum qcow2_discard_type type); ++ ++static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index); ++static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index); ++static uint64_t get_refcount_ro2(const void *refcount_array, uint64_t index); ++static uint64_t get_refcount_ro3(const void *refcount_array, uint64_t index); ++static uint64_t get_refcount_ro4(const void *refcount_array, uint64_t index); ++static uint64_t get_refcount_ro5(const void *refcount_array, uint64_t index); ++static uint64_t get_refcount_ro6(const void *refcount_array, uint64_t index); ++ ++static void set_refcount_ro0(void *refcount_array, uint64_t index, ++ uint64_t value); ++static void set_refcount_ro1(void *refcount_array, uint64_t index, ++ uint64_t value); ++static void set_refcount_ro2(void *refcount_array, uint64_t index, ++ uint64_t value); ++static void set_refcount_ro3(void *refcount_array, uint64_t index, ++ uint64_t value); ++static void set_refcount_ro4(void *refcount_array, uint64_t index, ++ uint64_t value); ++static void set_refcount_ro5(void *refcount_array, uint64_t index, ++ uint64_t value); ++static void set_refcount_ro6(void *refcount_array, uint64_t index, ++ uint64_t value); ++ ++ ++static Qcow2GetRefcountFunc *const get_refcount_funcs[] = { ++ &get_refcount_ro0, ++ &get_refcount_ro1, ++ &get_refcount_ro2, ++ &get_refcount_ro3, ++ &get_refcount_ro4, ++ &get_refcount_ro5, ++ &get_refcount_ro6 ++}; ++ ++static Qcow2SetRefcountFunc *const set_refcount_funcs[] = { ++ &set_refcount_ro0, ++ &set_refcount_ro1, ++ &set_refcount_ro2, ++ &set_refcount_ro3, ++ &set_refcount_ro4, ++ &set_refcount_ro5, ++ &set_refcount_ro6 ++}; ++ ++ ++/*********************************************************/ ++/* refcount handling */ ++ ++static void update_max_refcount_table_index(BDRVQcow2State *s) ++{ ++ unsigned i = s->refcount_table_size - 1; ++ while (i > 0 && (s->refcount_table[i] & REFT_OFFSET_MASK) == 0) { ++ i--; ++ } ++ /* Set s->max_refcount_table_index to the index of the last used entry */ ++ s->max_refcount_table_index = i; ++} ++ ++int coroutine_fn qcow2_refcount_init(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ unsigned int refcount_table_size2, i; ++ int ret; ++ ++ assert(s->refcount_order >= 0 && s->refcount_order <= 6); ++ ++ s->get_refcount = get_refcount_funcs[s->refcount_order]; ++ s->set_refcount = set_refcount_funcs[s->refcount_order]; ++ ++ assert(s->refcount_table_size <= INT_MAX / REFTABLE_ENTRY_SIZE); ++ refcount_table_size2 = s->refcount_table_size * REFTABLE_ENTRY_SIZE; ++ s->refcount_table = g_try_malloc(refcount_table_size2); ++ ++ if (s->refcount_table_size > 0) { ++ if (s->refcount_table == NULL) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_REFTABLE_LOAD); ++ ret = bdrv_co_pread(bs->file, s->refcount_table_offset, ++ refcount_table_size2, s->refcount_table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ for(i = 0; i < s->refcount_table_size; i++) ++ be64_to_cpus(&s->refcount_table[i]); ++ update_max_refcount_table_index(s); ++ } ++ return 0; ++ fail: ++ return ret; ++} ++ ++void qcow2_refcount_close(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ g_free(s->refcount_table); ++} ++ ++ ++static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index) ++{ ++ return (((const uint8_t *)refcount_array)[index / 8] >> (index % 8)) & 0x1; ++} ++ ++static void set_refcount_ro0(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ assert(!(value >> 1)); ++ ((uint8_t *)refcount_array)[index / 8] &= ~(0x1 << (index % 8)); ++ ((uint8_t *)refcount_array)[index / 8] |= value << (index % 8); ++} ++ ++static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index) ++{ ++ return (((const uint8_t *)refcount_array)[index / 4] >> (2 * (index % 4))) ++ & 0x3; ++} ++ ++static void set_refcount_ro1(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ assert(!(value >> 2)); ++ ((uint8_t *)refcount_array)[index / 4] &= ~(0x3 << (2 * (index % 4))); ++ ((uint8_t *)refcount_array)[index / 4] |= value << (2 * (index % 4)); ++} ++ ++static uint64_t get_refcount_ro2(const void *refcount_array, uint64_t index) ++{ ++ return (((const uint8_t *)refcount_array)[index / 2] >> (4 * (index % 2))) ++ & 0xf; ++} ++ ++static void set_refcount_ro2(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ assert(!(value >> 4)); ++ ((uint8_t *)refcount_array)[index / 2] &= ~(0xf << (4 * (index % 2))); ++ ((uint8_t *)refcount_array)[index / 2] |= value << (4 * (index % 2)); ++} ++ ++static uint64_t get_refcount_ro3(const void *refcount_array, uint64_t index) ++{ ++ return ((const uint8_t *)refcount_array)[index]; ++} ++ ++static void set_refcount_ro3(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ assert(!(value >> 8)); ++ ((uint8_t *)refcount_array)[index] = value; ++} ++ ++static uint64_t get_refcount_ro4(const void *refcount_array, uint64_t index) ++{ ++ return be16_to_cpu(((const uint16_t *)refcount_array)[index]); ++} ++ ++static void set_refcount_ro4(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ assert(!(value >> 16)); ++ ((uint16_t *)refcount_array)[index] = cpu_to_be16(value); ++} ++ ++static uint64_t get_refcount_ro5(const void *refcount_array, uint64_t index) ++{ ++ return be32_to_cpu(((const uint32_t *)refcount_array)[index]); ++} ++ ++static void set_refcount_ro5(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ assert(!(value >> 32)); ++ ((uint32_t *)refcount_array)[index] = cpu_to_be32(value); ++} ++ ++static uint64_t get_refcount_ro6(const void *refcount_array, uint64_t index) ++{ ++ return be64_to_cpu(((const uint64_t *)refcount_array)[index]); ++} ++ ++static void set_refcount_ro6(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ ((uint64_t *)refcount_array)[index] = cpu_to_be64(value); ++} ++ ++ ++static int GRAPH_RDLOCK ++load_refcount_block(BlockDriverState *bs, int64_t refcount_block_offset, ++ void **refcount_block) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD); ++ return qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, ++ refcount_block); ++} ++ ++/* ++ * Retrieves the refcount of the cluster given by its index and stores it in ++ * *refcount. Returns 0 on success and -errno on failure. ++ */ ++int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, ++ uint64_t *refcount) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t refcount_table_index, block_index; ++ int64_t refcount_block_offset; ++ int ret; ++ void *refcount_block; ++ ++ refcount_table_index = cluster_index >> s->refcount_block_bits; ++ if (refcount_table_index >= s->refcount_table_size) { ++ *refcount = 0; ++ return 0; ++ } ++ refcount_block_offset = ++ s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK; ++ if (!refcount_block_offset) { ++ *refcount = 0; ++ return 0; ++ } ++ ++ if (offset_into_cluster(s, refcount_block_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" PRIx64 ++ " unaligned (reftable index: %#" PRIx64 ")", ++ refcount_block_offset, refcount_table_index); ++ return -EIO; ++ } ++ ++ ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, ++ &refcount_block); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ block_index = cluster_index & (s->refcount_block_size - 1); ++ *refcount = s->get_refcount(refcount_block, block_index); ++ ++ qcow2_cache_put(s->refcount_block_cache, &refcount_block); ++ ++ return 0; ++} ++ ++/* Checks if two offsets are described by the same refcount block */ ++static int in_same_refcount_block(BDRVQcow2State *s, uint64_t offset_a, ++ uint64_t offset_b) ++{ ++ uint64_t block_a = offset_a >> (s->cluster_bits + s->refcount_block_bits); ++ uint64_t block_b = offset_b >> (s->cluster_bits + s->refcount_block_bits); ++ ++ return (block_a == block_b); ++} ++ ++/* ++ * Loads a refcount block. If it doesn't exist yet, it is allocated first ++ * (including growing the refcount table if needed). ++ * ++ * Returns 0 on success or -errno in error case ++ */ ++static int GRAPH_RDLOCK ++alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index, ++ void **refcount_block) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ unsigned int refcount_table_index; ++ int64_t ret; ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC); ++ ++ /* Find the refcount block for the given cluster */ ++ refcount_table_index = cluster_index >> s->refcount_block_bits; ++ ++ if (refcount_table_index < s->refcount_table_size) { ++ ++ uint64_t refcount_block_offset = ++ s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK; ++ ++ /* If it's already there, we're done */ ++ if (refcount_block_offset) { ++ if (offset_into_cluster(s, refcount_block_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" ++ PRIx64 " unaligned (reftable index: " ++ "%#x)", refcount_block_offset, ++ refcount_table_index); ++ return -EIO; ++ } ++ ++ return load_refcount_block(bs, refcount_block_offset, ++ refcount_block); ++ } ++ } ++ ++ /* ++ * If we came here, we need to allocate something. Something is at least ++ * a cluster for the new refcount block. It may also include a new refcount ++ * table if the old refcount table is too small. ++ * ++ * Note that allocating clusters here needs some special care: ++ * ++ * - We can't use the normal qcow2_alloc_clusters(), it would try to ++ * increase the refcount and very likely we would end up with an endless ++ * recursion. Instead we must place the refcount blocks in a way that ++ * they can describe them themselves. ++ * ++ * - We need to consider that at this point we are inside update_refcounts ++ * and potentially doing an initial refcount increase. This means that ++ * some clusters have already been allocated by the caller, but their ++ * refcount isn't accurate yet. If we allocate clusters for metadata, we ++ * need to return -EAGAIN to signal the caller that it needs to restart ++ * the search for free clusters. ++ * ++ * - alloc_clusters_noref and qcow2_free_clusters may load a different ++ * refcount block into the cache ++ */ ++ ++ *refcount_block = NULL; ++ ++ /* We write to the refcount table, so we might depend on L2 tables */ ++ ret = qcow2_cache_flush(bs, s->l2_table_cache); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Allocate the refcount block itself and mark it as used */ ++ int64_t new_block = alloc_clusters_noref(bs, s->cluster_size, INT64_MAX); ++ if (new_block < 0) { ++ return new_block; ++ } ++ ++ /* The offset must fit in the offset field of the refcount table entry */ ++ assert((new_block & REFT_OFFSET_MASK) == new_block); ++ ++ /* If we're allocating the block at offset 0 then something is wrong */ ++ if (new_block == 0) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " ++ "allocation of refcount block at offset 0"); ++ return -EIO; ++ } ++ ++#ifdef DEBUG_ALLOC2 ++ fprintf(stderr, "qcow2: Allocate refcount block %d for %" PRIx64 ++ " at %" PRIx64 "\n", ++ refcount_table_index, cluster_index << s->cluster_bits, new_block); ++#endif ++ ++ if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) { ++ /* Zero the new refcount block before updating it */ ++ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, ++ refcount_block); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ memset(*refcount_block, 0, s->cluster_size); ++ ++ /* The block describes itself, need to update the cache */ ++ int block_index = (new_block >> s->cluster_bits) & ++ (s->refcount_block_size - 1); ++ s->set_refcount(*refcount_block, block_index, 1); ++ } else { ++ /* Described somewhere else. This can recurse at most twice before we ++ * arrive at a block that describes itself. */ ++ ret = update_refcount(bs, new_block, s->cluster_size, 1, false, ++ QCOW2_DISCARD_NEVER); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Initialize the new refcount block only after updating its refcount, ++ * update_refcount uses the refcount cache itself */ ++ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, ++ refcount_block); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ memset(*refcount_block, 0, s->cluster_size); ++ } ++ ++ /* Now the new refcount block needs to be written to disk */ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE); ++ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block); ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* If the refcount table is big enough, just hook the block up there */ ++ if (refcount_table_index < s->refcount_table_size) { ++ uint64_t data64 = cpu_to_be64(new_block); ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_HOOKUP); ++ ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset + ++ refcount_table_index * REFTABLE_ENTRY_SIZE, ++ sizeof(data64), &data64, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ s->refcount_table[refcount_table_index] = new_block; ++ /* If there's a hole in s->refcount_table then it can happen ++ * that refcount_table_index < s->max_refcount_table_index */ ++ s->max_refcount_table_index = ++ MAX(s->max_refcount_table_index, refcount_table_index); ++ ++ /* The new refcount block may be where the caller intended to put its ++ * data, so let it restart the search. */ ++ return -EAGAIN; ++ } ++ ++ qcow2_cache_put(s->refcount_block_cache, refcount_block); ++ ++ /* ++ * If we come here, we need to grow the refcount table. Again, a new ++ * refcount table needs some space and we can't simply allocate to avoid ++ * endless recursion. ++ * ++ * Therefore let's grab new refcount blocks at the end of the image, which ++ * will describe themselves and the new refcount table. This way we can ++ * reference them only in the new table and do the switch to the new ++ * refcount table at once without producing an inconsistent state in ++ * between. ++ */ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_GROW); ++ ++ /* Calculate the number of refcount blocks needed so far; this will be the ++ * basis for calculating the index of the first cluster used for the ++ * self-describing refcount structures which we are about to create. ++ * ++ * Because we reached this point, there cannot be any refcount entries for ++ * cluster_index or higher indices yet. However, because new_block has been ++ * allocated to describe that cluster (and it will assume this role later ++ * on), we cannot use that index; also, new_block may actually have a higher ++ * cluster index than cluster_index, so it needs to be taken into account ++ * here (and 1 needs to be added to its value because that cluster is used). ++ */ ++ uint64_t blocks_used = DIV_ROUND_UP(MAX(cluster_index + 1, ++ (new_block >> s->cluster_bits) + 1), ++ s->refcount_block_size); ++ ++ /* Create the new refcount table and blocks */ ++ uint64_t meta_offset = (blocks_used * s->refcount_block_size) * ++ s->cluster_size; ++ ++ ret = qcow2_refcount_area(bs, meta_offset, 0, false, ++ refcount_table_index, new_block); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ ret = load_refcount_block(bs, new_block, refcount_block); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* If we were trying to do the initial refcount update for some cluster ++ * allocation, we might have used the same clusters to store newly ++ * allocated metadata. Make the caller search some new space. */ ++ return -EAGAIN; ++ ++fail: ++ if (*refcount_block != NULL) { ++ qcow2_cache_put(s->refcount_block_cache, refcount_block); ++ } ++ return ret; ++} ++ ++/* ++ * Starting at @start_offset, this function creates new self-covering refcount ++ * structures: A new refcount table and refcount blocks which cover all of ++ * themselves, and a number of @additional_clusters beyond their end. ++ * @start_offset must be at the end of the image file, that is, there must be ++ * only empty space beyond it. ++ * If @exact_size is false, the refcount table will have 50 % more entries than ++ * necessary so it will not need to grow again soon. ++ * If @new_refblock_offset is not zero, it contains the offset of a refcount ++ * block that should be entered into the new refcount table at index ++ * @new_refblock_index. ++ * ++ * Returns: The offset after the new refcount structures (i.e. where the ++ * @additional_clusters may be placed) on success, -errno on error. ++ */ ++int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, ++ uint64_t additional_clusters, bool exact_size, ++ int new_refblock_index, ++ uint64_t new_refblock_offset) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t total_refblock_count_u64, additional_refblock_count; ++ int total_refblock_count, table_size, area_reftable_index, table_clusters; ++ int i; ++ uint64_t table_offset, block_offset, end_offset; ++ int ret; ++ uint64_t *new_table; ++ ++ assert(!(start_offset % s->cluster_size)); ++ ++ qcow2_refcount_metadata_size(start_offset / s->cluster_size + ++ additional_clusters, ++ s->cluster_size, s->refcount_order, ++ !exact_size, &total_refblock_count_u64); ++ if (total_refblock_count_u64 > QCOW_MAX_REFTABLE_SIZE) { ++ return -EFBIG; ++ } ++ total_refblock_count = total_refblock_count_u64; ++ ++ /* Index in the refcount table of the first refcount block to cover the area ++ * of refcount structures we are about to create; we know that ++ * @total_refblock_count can cover @start_offset, so this will definitely ++ * fit into an int. */ ++ area_reftable_index = (start_offset / s->cluster_size) / ++ s->refcount_block_size; ++ ++ if (exact_size) { ++ table_size = total_refblock_count; ++ } else { ++ table_size = total_refblock_count + ++ DIV_ROUND_UP(total_refblock_count, 2); ++ } ++ /* The qcow2 file can only store the reftable size in number of clusters */ ++ table_size = ROUND_UP(table_size, s->cluster_size / REFTABLE_ENTRY_SIZE); ++ table_clusters = (table_size * REFTABLE_ENTRY_SIZE) / s->cluster_size; ++ ++ if (table_size > QCOW_MAX_REFTABLE_SIZE) { ++ return -EFBIG; ++ } ++ ++ new_table = g_try_new0(uint64_t, table_size); ++ ++ assert(table_size > 0); ++ if (new_table == NULL) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ /* Fill the new refcount table */ ++ if (table_size > s->max_refcount_table_index) { ++ /* We're actually growing the reftable */ ++ memcpy(new_table, s->refcount_table, ++ (s->max_refcount_table_index + 1) * REFTABLE_ENTRY_SIZE); ++ } else { ++ /* Improbable case: We're shrinking the reftable. However, the caller ++ * has assured us that there is only empty space beyond @start_offset, ++ * so we can simply drop all of the refblocks that won't fit into the ++ * new reftable. */ ++ memcpy(new_table, s->refcount_table, table_size * REFTABLE_ENTRY_SIZE); ++ } ++ ++ if (new_refblock_offset) { ++ assert(new_refblock_index < total_refblock_count); ++ new_table[new_refblock_index] = new_refblock_offset; ++ } ++ ++ /* Count how many new refblocks we have to create */ ++ additional_refblock_count = 0; ++ for (i = area_reftable_index; i < total_refblock_count; i++) { ++ if (!new_table[i]) { ++ additional_refblock_count++; ++ } ++ } ++ ++ table_offset = start_offset + additional_refblock_count * s->cluster_size; ++ end_offset = table_offset + table_clusters * s->cluster_size; ++ ++ /* Fill the refcount blocks, and create new ones, if necessary */ ++ block_offset = start_offset; ++ for (i = area_reftable_index; i < total_refblock_count; i++) { ++ void *refblock_data; ++ uint64_t first_offset_covered; ++ ++ /* Reuse an existing refblock if possible, create a new one otherwise */ ++ if (new_table[i]) { ++ ret = qcow2_cache_get(bs, s->refcount_block_cache, new_table[i], ++ &refblock_data); ++ if (ret < 0) { ++ goto fail; ++ } ++ } else { ++ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, ++ block_offset, &refblock_data); ++ if (ret < 0) { ++ goto fail; ++ } ++ memset(refblock_data, 0, s->cluster_size); ++ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, ++ refblock_data); ++ ++ new_table[i] = block_offset; ++ block_offset += s->cluster_size; ++ } ++ ++ /* First host offset covered by this refblock */ ++ first_offset_covered = (uint64_t)i * s->refcount_block_size * ++ s->cluster_size; ++ if (first_offset_covered < end_offset) { ++ int j, end_index; ++ ++ /* Set the refcount of all of the new refcount structures to 1 */ ++ ++ if (first_offset_covered < start_offset) { ++ assert(i == area_reftable_index); ++ j = (start_offset - first_offset_covered) / s->cluster_size; ++ assert(j < s->refcount_block_size); ++ } else { ++ j = 0; ++ } ++ ++ end_index = MIN((end_offset - first_offset_covered) / ++ s->cluster_size, ++ s->refcount_block_size); ++ ++ for (; j < end_index; j++) { ++ /* The caller guaranteed us this space would be empty */ ++ assert(s->get_refcount(refblock_data, j) == 0); ++ s->set_refcount(refblock_data, j, 1); ++ } ++ ++ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, ++ refblock_data); ++ } ++ ++ qcow2_cache_put(s->refcount_block_cache, &refblock_data); ++ } ++ ++ assert(block_offset == table_offset); ++ ++ /* Write refcount blocks to disk */ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS); ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Write refcount table to disk */ ++ for (i = 0; i < total_refblock_count; i++) { ++ cpu_to_be64s(&new_table[i]); ++ } ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE); ++ ret = bdrv_pwrite_sync(bs->file, table_offset, ++ table_size * REFTABLE_ENTRY_SIZE, new_table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (i = 0; i < total_refblock_count; i++) { ++ be64_to_cpus(&new_table[i]); ++ } ++ ++ /* Hook up the new refcount table in the qcow2 header */ ++ struct QEMU_PACKED { ++ uint64_t d64; ++ uint32_t d32; ++ } data; ++ data.d64 = cpu_to_be64(table_offset); ++ data.d32 = cpu_to_be32(table_clusters); ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE); ++ ret = bdrv_pwrite_sync(bs->file, ++ offsetof(QCowHeader, refcount_table_offset), ++ sizeof(data), &data, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* And switch it in memory */ ++ uint64_t old_table_offset = s->refcount_table_offset; ++ uint64_t old_table_size = s->refcount_table_size; ++ ++ g_free(s->refcount_table); ++ s->refcount_table = new_table; ++ s->refcount_table_size = table_size; ++ s->refcount_table_offset = table_offset; ++ update_max_refcount_table_index(s); ++ ++ /* Free old table. */ ++ qcow2_free_clusters(bs, old_table_offset, ++ old_table_size * REFTABLE_ENTRY_SIZE, ++ QCOW2_DISCARD_OTHER); ++ ++ return end_offset; ++ ++fail: ++ g_free(new_table); ++ return ret; ++} ++ ++void qcow2_process_discards(BlockDriverState *bs, int ret) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2DiscardRegion *d, *next; ++ ++ QTAILQ_FOREACH_SAFE(d, &s->discards, next, next) { ++ QTAILQ_REMOVE(&s->discards, d, next); ++ ++ /* Discard is optional, ignore the return value */ ++ if (ret >= 0) { ++ int r2 = bdrv_pdiscard(bs->file, d->offset, d->bytes); ++ if (r2 < 0) { ++ trace_qcow2_process_discards_failed_region(d->offset, d->bytes, ++ r2); ++ } ++ } ++ ++ g_free(d); ++ } ++} ++ ++static void update_refcount_discard(BlockDriverState *bs, ++ uint64_t offset, uint64_t length) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2DiscardRegion *d, *p, *next; ++ ++ QTAILQ_FOREACH(d, &s->discards, next) { ++ uint64_t new_start = MIN(offset, d->offset); ++ uint64_t new_end = MAX(offset + length, d->offset + d->bytes); ++ ++ if (new_end - new_start <= length + d->bytes) { ++ /* There can't be any overlap, areas ending up here have no ++ * references any more and therefore shouldn't get freed another ++ * time. */ ++ assert(d->bytes + length == new_end - new_start); ++ d->offset = new_start; ++ d->bytes = new_end - new_start; ++ goto found; ++ } ++ } ++ ++ d = g_malloc(sizeof(*d)); ++ *d = (Qcow2DiscardRegion) { ++ .bs = bs, ++ .offset = offset, ++ .bytes = length, ++ }; ++ QTAILQ_INSERT_TAIL(&s->discards, d, next); ++ ++found: ++ /* Merge discard requests if they are adjacent now */ ++ QTAILQ_FOREACH_SAFE(p, &s->discards, next, next) { ++ if (p == d ++ || p->offset > d->offset + d->bytes ++ || d->offset > p->offset + p->bytes) ++ { ++ continue; ++ } ++ ++ /* Still no overlap possible */ ++ assert(p->offset == d->offset + d->bytes ++ || d->offset == p->offset + p->bytes); ++ ++ QTAILQ_REMOVE(&s->discards, p, next); ++ d->offset = MIN(d->offset, p->offset); ++ d->bytes += p->bytes; ++ g_free(p); ++ } ++} ++ ++/* XXX: cache several refcount block clusters ? */ ++/* @addend is the absolute value of the addend; if @decrease is set, @addend ++ * will be subtracted from the current refcount, otherwise it will be added */ ++static int GRAPH_RDLOCK ++update_refcount(BlockDriverState *bs, int64_t offset, int64_t length, ++ uint64_t addend, bool decrease, enum qcow2_discard_type type) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t start, last, cluster_offset; ++ void *refcount_block = NULL; ++ int64_t old_table_index = -1; ++ int ret; ++ ++#ifdef DEBUG_ALLOC2 ++ fprintf(stderr, "update_refcount: offset=%" PRId64 " size=%" PRId64 ++ " addend=%s%" PRIu64 "\n", offset, length, decrease ? "-" : "", ++ addend); ++#endif ++ if (length < 0) { ++ return -EINVAL; ++ } else if (length == 0) { ++ return 0; ++ } ++ ++ if (decrease) { ++ qcow2_cache_set_dependency(bs, s->refcount_block_cache, ++ s->l2_table_cache); ++ } ++ ++ start = start_of_cluster(s, offset); ++ last = start_of_cluster(s, offset + length - 1); ++ for(cluster_offset = start; cluster_offset <= last; ++ cluster_offset += s->cluster_size) ++ { ++ int block_index; ++ uint64_t refcount; ++ int64_t cluster_index = cluster_offset >> s->cluster_bits; ++ int64_t table_index = cluster_index >> s->refcount_block_bits; ++ ++ /* Load the refcount block and allocate it if needed */ ++ if (table_index != old_table_index) { ++ if (refcount_block) { ++ qcow2_cache_put(s->refcount_block_cache, &refcount_block); ++ } ++ ret = alloc_refcount_block(bs, cluster_index, &refcount_block); ++ /* If the caller needs to restart the search for free clusters, ++ * try the same ones first to see if they're still free. */ ++ if (ret == -EAGAIN) { ++ if (s->free_cluster_index > (start >> s->cluster_bits)) { ++ s->free_cluster_index = (start >> s->cluster_bits); ++ } ++ } ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ old_table_index = table_index; ++ ++ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block); ++ ++ /* we can update the count and save it */ ++ block_index = cluster_index & (s->refcount_block_size - 1); ++ ++ refcount = s->get_refcount(refcount_block, block_index); ++ if (decrease ? (refcount - addend > refcount) ++ : (refcount + addend < refcount || ++ refcount + addend > s->refcount_max)) ++ { ++ ret = -EINVAL; ++ goto fail; ++ } ++ if (decrease) { ++ refcount -= addend; ++ } else { ++ refcount += addend; ++ } ++ if (refcount == 0 && cluster_index < s->free_cluster_index) { ++ s->free_cluster_index = cluster_index; ++ } ++ s->set_refcount(refcount_block, block_index, refcount); ++ ++ if (refcount == 0) { ++ void *table; ++ ++ table = qcow2_cache_is_table_offset(s->refcount_block_cache, ++ offset); ++ if (table != NULL) { ++ qcow2_cache_put(s->refcount_block_cache, &refcount_block); ++ old_table_index = -1; ++ qcow2_cache_discard(s->refcount_block_cache, table); ++ } ++ ++ table = qcow2_cache_is_table_offset(s->l2_table_cache, offset); ++ if (table != NULL) { ++ qcow2_cache_discard(s->l2_table_cache, table); ++ } ++ ++ if (s->discard_passthrough[type]) { ++ update_refcount_discard(bs, cluster_offset, s->cluster_size); ++ } ++ } ++ } ++ ++ ret = 0; ++fail: ++ if (!s->cache_discards) { ++ qcow2_process_discards(bs, ret); ++ } ++ ++ /* Write last changed block to disk */ ++ if (refcount_block) { ++ qcow2_cache_put(s->refcount_block_cache, &refcount_block); ++ } ++ ++ /* ++ * Try do undo any updates if an error is returned (This may succeed in ++ * some cases like ENOSPC for allocating a new refcount block) ++ */ ++ if (ret < 0) { ++ int dummy; ++ dummy = update_refcount(bs, offset, cluster_offset - offset, addend, ++ !decrease, QCOW2_DISCARD_NEVER); ++ (void)dummy; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Increases or decreases the refcount of a given cluster. ++ * ++ * @addend is the absolute value of the addend; if @decrease is set, @addend ++ * will be subtracted from the current refcount, otherwise it will be added. ++ * ++ * On success 0 is returned; on failure -errno is returned. ++ */ ++int qcow2_update_cluster_refcount(BlockDriverState *bs, ++ int64_t cluster_index, ++ uint64_t addend, bool decrease, ++ enum qcow2_discard_type type) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend, ++ decrease, type); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++ ++ ++/*********************************************************/ ++/* cluster allocation functions */ ++ ++ ++ ++/* return < 0 if error */ ++static int64_t GRAPH_RDLOCK ++alloc_clusters_noref(BlockDriverState *bs, uint64_t size, uint64_t max) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t i, nb_clusters, refcount; ++ int ret; ++ ++ /* We can't allocate clusters if they may still be queued for discard. */ ++ if (s->cache_discards) { ++ qcow2_process_discards(bs, 0); ++ } ++ ++ nb_clusters = size_to_clusters(s, size); ++retry: ++ for(i = 0; i < nb_clusters; i++) { ++ uint64_t next_cluster_index = s->free_cluster_index++; ++ ret = qcow2_get_refcount(bs, next_cluster_index, &refcount); ++ ++ if (ret < 0) { ++ return ret; ++ } else if (refcount != 0) { ++ goto retry; ++ } ++ } ++ ++ /* Make sure that all offsets in the "allocated" range are representable ++ * in the requested max */ ++ if (s->free_cluster_index > 0 && ++ s->free_cluster_index - 1 > (max >> s->cluster_bits)) ++ { ++ return -EFBIG; ++ } ++ ++#ifdef DEBUG_ALLOC2 ++ fprintf(stderr, "alloc_clusters: size=%" PRId64 " -> %" PRId64 "\n", ++ size, ++ (s->free_cluster_index - nb_clusters) << s->cluster_bits); ++#endif ++ return (s->free_cluster_index - nb_clusters) << s->cluster_bits; ++} ++ ++int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) ++{ ++ int64_t offset; ++ int ret; ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); ++ do { ++ offset = alloc_clusters_noref(bs, size, QCOW_MAX_CLUSTER_OFFSET); ++ if (offset < 0) { ++ return offset; ++ } ++ ++ ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); ++ } while (ret == -EAGAIN); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return offset; ++} ++ ++int64_t coroutine_fn qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, ++ int64_t nb_clusters) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t cluster_index, refcount; ++ uint64_t i; ++ int ret; ++ ++ assert(nb_clusters >= 0); ++ if (nb_clusters == 0) { ++ return 0; ++ } ++ ++ do { ++ /* Check how many clusters there are free */ ++ cluster_index = offset >> s->cluster_bits; ++ for(i = 0; i < nb_clusters; i++) { ++ ret = qcow2_get_refcount(bs, cluster_index++, &refcount); ++ if (ret < 0) { ++ return ret; ++ } else if (refcount != 0) { ++ break; ++ } ++ } ++ ++ /* And then allocate them */ ++ ret = update_refcount(bs, offset, i << s->cluster_bits, 1, false, ++ QCOW2_DISCARD_NEVER); ++ } while (ret == -EAGAIN); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return i; ++} ++ ++/* only used to allocate compressed sectors. We try to allocate ++ contiguous sectors. size must be <= cluster_size */ ++int64_t coroutine_fn GRAPH_RDLOCK qcow2_alloc_bytes(BlockDriverState *bs, int size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t offset; ++ size_t free_in_cluster; ++ int ret; ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_BYTES); ++ assert(size > 0 && size <= s->cluster_size); ++ assert(!s->free_byte_offset || offset_into_cluster(s, s->free_byte_offset)); ++ ++ offset = s->free_byte_offset; ++ ++ if (offset) { ++ uint64_t refcount; ++ ret = qcow2_get_refcount(bs, offset >> s->cluster_bits, &refcount); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (refcount == s->refcount_max) { ++ offset = 0; ++ } ++ } ++ ++ free_in_cluster = s->cluster_size - offset_into_cluster(s, offset); ++ do { ++ if (!offset || free_in_cluster < size) { ++ int64_t new_cluster; ++ ++ new_cluster = alloc_clusters_noref(bs, s->cluster_size, ++ MIN(s->cluster_offset_mask, ++ QCOW_MAX_CLUSTER_OFFSET)); ++ if (new_cluster < 0) { ++ return new_cluster; ++ } ++ ++ if (new_cluster == 0) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " ++ "allocation of compressed cluster " ++ "at offset 0"); ++ return -EIO; ++ } ++ ++ if (!offset || ROUND_UP(offset, s->cluster_size) != new_cluster) { ++ offset = new_cluster; ++ free_in_cluster = s->cluster_size; ++ } else { ++ free_in_cluster += s->cluster_size; ++ } ++ } ++ ++ assert(offset); ++ ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); ++ if (ret < 0) { ++ offset = 0; ++ } ++ } while (ret == -EAGAIN); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* The cluster refcount was incremented; refcount blocks must be flushed ++ * before the caller's L2 table updates. */ ++ qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); ++ ++ s->free_byte_offset = offset + size; ++ if (!offset_into_cluster(s, s->free_byte_offset)) { ++ s->free_byte_offset = 0; ++ } ++ ++ return offset; ++} ++ ++void qcow2_free_clusters(BlockDriverState *bs, ++ int64_t offset, int64_t size, ++ enum qcow2_discard_type type) ++{ ++ int ret; ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_FREE); ++ ret = update_refcount(bs, offset, size, 1, true, type); ++ if (ret < 0) { ++ fprintf(stderr, "qcow2_free_clusters failed: %s\n", strerror(-ret)); ++ /* TODO Remember the clusters to free them later and avoid leaking */ ++ } ++} ++ ++/* ++ * Free a cluster using its L2 entry (handles clusters of all types, e.g. ++ * normal cluster, compressed cluster, etc.) ++ */ ++void qcow2_free_any_cluster(BlockDriverState *bs, uint64_t l2_entry, ++ enum qcow2_discard_type type) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCow2ClusterType ctype = qcow2_get_cluster_type(bs, l2_entry); ++ ++ if (has_data_file(bs)) { ++ if (s->discard_passthrough[type] && ++ (ctype == QCOW2_CLUSTER_NORMAL || ++ ctype == QCOW2_CLUSTER_ZERO_ALLOC)) ++ { ++ bdrv_pdiscard(s->data_file, l2_entry & L2E_OFFSET_MASK, ++ s->cluster_size); ++ } ++ return; ++ } ++ ++ switch (ctype) { ++ case QCOW2_CLUSTER_COMPRESSED: ++ { ++ uint64_t coffset; ++ int csize; ++ ++ qcow2_parse_compressed_l2_entry(bs, l2_entry, &coffset, &csize); ++ qcow2_free_clusters(bs, coffset, csize, type); ++ } ++ break; ++ case QCOW2_CLUSTER_NORMAL: ++ case QCOW2_CLUSTER_ZERO_ALLOC: ++ if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) { ++ qcow2_signal_corruption(bs, false, -1, -1, ++ "Cannot free unaligned cluster %#llx", ++ l2_entry & L2E_OFFSET_MASK); ++ } else { ++ qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK, ++ s->cluster_size, type); ++ } ++ break; ++ case QCOW2_CLUSTER_ZERO_PLAIN: ++ case QCOW2_CLUSTER_UNALLOCATED: ++ break; ++ default: ++ abort(); ++ } ++} ++ ++int qcow2_write_caches(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ ret = qcow2_cache_write(bs, s->l2_table_cache); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (qcow2_need_accurate_refcounts(s)) { ++ ret = qcow2_cache_write(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++int qcow2_flush_caches(BlockDriverState *bs) ++{ ++ int ret = qcow2_write_caches(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return bdrv_flush(bs->file->bs); ++} ++ ++/*********************************************************/ ++/* snapshots and image creation */ ++ ++ ++ ++/* update the refcounts of snapshots and the copied flag */ ++int qcow2_update_snapshot_refcount(BlockDriverState *bs, ++ int64_t l1_table_offset, int l1_size, int addend) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *l1_table, *l2_slice, l2_offset, entry, l1_size2, refcount; ++ bool l1_allocated = false; ++ int64_t old_entry, old_l2_offset; ++ unsigned slice, slice_size2, n_slices; ++ int i, j, l1_modified = 0; ++ int ret; ++ ++ assert(addend >= -1 && addend <= 1); ++ ++ l2_slice = NULL; ++ l1_table = NULL; ++ l1_size2 = l1_size * L1E_SIZE; ++ slice_size2 = s->l2_slice_size * l2_entry_size(s); ++ n_slices = s->cluster_size / slice_size2; ++ ++ s->cache_discards = true; ++ ++ /* WARNING: qcow2_snapshot_goto relies on this function not using the ++ * l1_table_offset when it is the current s->l1_table_offset! Be careful ++ * when changing this! */ ++ if (l1_table_offset != s->l1_table_offset) { ++ l1_table = g_try_malloc0(l1_size2); ++ if (l1_size2 && l1_table == NULL) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ l1_allocated = true; ++ ++ ret = bdrv_pread(bs->file, l1_table_offset, l1_size2, l1_table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (i = 0; i < l1_size; i++) { ++ be64_to_cpus(&l1_table[i]); ++ } ++ } else { ++ assert(l1_size == s->l1_size); ++ l1_table = s->l1_table; ++ l1_allocated = false; ++ } ++ ++ for (i = 0; i < l1_size; i++) { ++ l2_offset = l1_table[i]; ++ if (l2_offset) { ++ old_l2_offset = l2_offset; ++ l2_offset &= L1E_OFFSET_MASK; ++ ++ if (offset_into_cluster(s, l2_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" ++ PRIx64 " unaligned (L1 index: %#x)", ++ l2_offset, i); ++ ret = -EIO; ++ goto fail; ++ } ++ ++ for (slice = 0; slice < n_slices; slice++) { ++ ret = qcow2_cache_get(bs, s->l2_table_cache, ++ l2_offset + slice * slice_size2, ++ (void **) &l2_slice); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (j = 0; j < s->l2_slice_size; j++) { ++ uint64_t cluster_index; ++ uint64_t offset; ++ ++ entry = get_l2_entry(s, l2_slice, j); ++ old_entry = entry; ++ entry &= ~QCOW_OFLAG_COPIED; ++ offset = entry & L2E_OFFSET_MASK; ++ ++ switch (qcow2_get_cluster_type(bs, entry)) { ++ case QCOW2_CLUSTER_COMPRESSED: ++ if (addend != 0) { ++ uint64_t coffset; ++ int csize; ++ ++ qcow2_parse_compressed_l2_entry(bs, entry, ++ &coffset, &csize); ++ ret = update_refcount( ++ bs, coffset, csize, ++ abs(addend), addend < 0, ++ QCOW2_DISCARD_SNAPSHOT); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ /* compressed clusters are never modified */ ++ refcount = 2; ++ break; ++ ++ case QCOW2_CLUSTER_NORMAL: ++ case QCOW2_CLUSTER_ZERO_ALLOC: ++ if (offset_into_cluster(s, offset)) { ++ /* Here l2_index means table (not slice) index */ ++ int l2_index = slice * s->l2_slice_size + j; ++ qcow2_signal_corruption( ++ bs, true, -1, -1, "Cluster " ++ "allocation offset %#" PRIx64 ++ " unaligned (L2 offset: %#" ++ PRIx64 ", L2 index: %#x)", ++ offset, l2_offset, l2_index); ++ ret = -EIO; ++ goto fail; ++ } ++ ++ cluster_index = offset >> s->cluster_bits; ++ assert(cluster_index); ++ if (addend != 0) { ++ ret = qcow2_update_cluster_refcount( ++ bs, cluster_index, abs(addend), addend < 0, ++ QCOW2_DISCARD_SNAPSHOT); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ ret = qcow2_get_refcount(bs, cluster_index, &refcount); ++ if (ret < 0) { ++ goto fail; ++ } ++ break; ++ ++ case QCOW2_CLUSTER_ZERO_PLAIN: ++ case QCOW2_CLUSTER_UNALLOCATED: ++ refcount = 0; ++ break; ++ ++ default: ++ abort(); ++ } ++ ++ if (refcount == 1) { ++ entry |= QCOW_OFLAG_COPIED; ++ } ++ if (entry != old_entry) { ++ if (addend > 0) { ++ qcow2_cache_set_dependency(bs, s->l2_table_cache, ++ s->refcount_block_cache); ++ } ++ set_l2_entry(s, l2_slice, j, entry); ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, ++ l2_slice); ++ } ++ } ++ ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ } ++ ++ if (addend != 0) { ++ ret = qcow2_update_cluster_refcount(bs, l2_offset >> ++ s->cluster_bits, ++ abs(addend), addend < 0, ++ QCOW2_DISCARD_SNAPSHOT); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, ++ &refcount); ++ if (ret < 0) { ++ goto fail; ++ } else if (refcount == 1) { ++ l2_offset |= QCOW_OFLAG_COPIED; ++ } ++ if (l2_offset != old_l2_offset) { ++ l1_table[i] = l2_offset; ++ l1_modified = 1; ++ } ++ } ++ } ++ ++ ret = bdrv_flush(bs); ++fail: ++ if (l2_slice) { ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ } ++ ++ s->cache_discards = false; ++ qcow2_process_discards(bs, ret); ++ ++ /* Update L1 only if it isn't deleted anyway (addend = -1) */ ++ if (ret == 0 && addend >= 0 && l1_modified) { ++ for (i = 0; i < l1_size; i++) { ++ cpu_to_be64s(&l1_table[i]); ++ } ++ ++ ret = bdrv_pwrite_sync(bs->file, l1_table_offset, l1_size2, l1_table, ++ 0); ++ ++ for (i = 0; i < l1_size; i++) { ++ be64_to_cpus(&l1_table[i]); ++ } ++ } ++ if (l1_allocated) ++ g_free(l1_table); ++ return ret; ++} ++ ++ ++ ++ ++/*********************************************************/ ++/* refcount checking functions */ ++ ++ ++static uint64_t refcount_array_byte_size(BDRVQcow2State *s, uint64_t entries) ++{ ++ /* This assertion holds because there is no way we can address more than ++ * 2^(64 - 9) clusters at once (with cluster size 512 = 2^9, and because ++ * offsets have to be representable in bytes); due to every cluster ++ * corresponding to one refcount entry, we are well below that limit */ ++ assert(entries < (UINT64_C(1) << (64 - 9))); ++ ++ /* Thanks to the assertion this will not overflow, because ++ * s->refcount_order < 7. ++ * (note: x << s->refcount_order == x * s->refcount_bits) */ ++ return DIV_ROUND_UP(entries << s->refcount_order, 8); ++} ++ ++/** ++ * Reallocates *array so that it can hold new_size entries. *size must contain ++ * the current number of entries in *array. If the reallocation fails, *array ++ * and *size will not be modified and -errno will be returned. If the ++ * reallocation is successful, *array will be set to the new buffer, *size ++ * will be set to new_size and 0 will be returned. The size of the reallocated ++ * refcount array buffer will be aligned to a cluster boundary, and the newly ++ * allocated area will be zeroed. ++ */ ++static int realloc_refcount_array(BDRVQcow2State *s, void **array, ++ int64_t *size, int64_t new_size) ++{ ++ int64_t old_byte_size, new_byte_size; ++ void *new_ptr; ++ ++ /* Round to clusters so the array can be directly written to disk */ ++ old_byte_size = size_to_clusters(s, refcount_array_byte_size(s, *size)) ++ * s->cluster_size; ++ new_byte_size = size_to_clusters(s, refcount_array_byte_size(s, new_size)) ++ * s->cluster_size; ++ ++ if (new_byte_size == old_byte_size) { ++ *size = new_size; ++ return 0; ++ } ++ ++ assert(new_byte_size > 0); ++ ++ if (new_byte_size > SIZE_MAX) { ++ return -ENOMEM; ++ } ++ ++ new_ptr = g_try_realloc(*array, new_byte_size); ++ if (!new_ptr) { ++ return -ENOMEM; ++ } ++ ++ if (new_byte_size > old_byte_size) { ++ memset((char *)new_ptr + old_byte_size, 0, ++ new_byte_size - old_byte_size); ++ } ++ ++ *array = new_ptr; ++ *size = new_size; ++ ++ return 0; ++} ++ ++/* ++ * Increases the refcount for a range of clusters in a given refcount table. ++ * This is used to construct a temporary refcount table out of L1 and L2 tables ++ * which can be compared to the refcount table saved in the image. ++ * ++ * Modifies the number of errors in res. ++ */ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, ++ int64_t *refcount_table_size, ++ int64_t offset, int64_t size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t start, last, cluster_offset, k, refcount; ++ int64_t file_len; ++ int ret; ++ ++ if (size <= 0) { ++ return 0; ++ } ++ ++ file_len = bdrv_co_getlength(bs->file->bs); ++ if (file_len < 0) { ++ return file_len; ++ } ++ ++ /* ++ * Last cluster of qcow2 image may be semi-allocated, so it may be OK to ++ * reference some space after file end but it should be less than one ++ * cluster. ++ */ ++ if (offset + size - file_len >= s->cluster_size) { ++ fprintf(stderr, "ERROR: counting reference for region exceeding the " ++ "end of the file by one cluster or more: offset 0x%" PRIx64 ++ " size 0x%" PRIx64 "\n", offset, size); ++ res->corruptions++; ++ return 0; ++ } ++ ++ start = start_of_cluster(s, offset); ++ last = start_of_cluster(s, offset + size - 1); ++ for(cluster_offset = start; cluster_offset <= last; ++ cluster_offset += s->cluster_size) { ++ k = cluster_offset >> s->cluster_bits; ++ if (k >= *refcount_table_size) { ++ ret = realloc_refcount_array(s, refcount_table, ++ refcount_table_size, k + 1); ++ if (ret < 0) { ++ res->check_errors++; ++ return ret; ++ } ++ } ++ ++ refcount = s->get_refcount(*refcount_table, k); ++ if (refcount == s->refcount_max) { ++ fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64 ++ "\n", cluster_offset); ++ fprintf(stderr, "Use qemu-img amend to increase the refcount entry " ++ "width or qemu-img convert to create a clean copy if the " ++ "image cannot be opened for writing\n"); ++ res->corruptions++; ++ continue; ++ } ++ s->set_refcount(*refcount_table, k, refcount + 1); ++ } ++ ++ return 0; ++} ++ ++/* Flags for check_refcounts_l1() and check_refcounts_l2() */ ++enum { ++ CHECK_FRAG_INFO = 0x2, /* update BlockFragInfo counters */ ++}; ++ ++/* ++ * Fix L2 entry by making it QCOW2_CLUSTER_ZERO_PLAIN (or making all its present ++ * subclusters QCOW2_SUBCLUSTER_ZERO_PLAIN). ++ * ++ * This function decrements res->corruptions on success, so the caller is ++ * responsible to increment res->corruptions prior to the call. ++ * ++ * On failure in-memory @l2_table may be modified. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++fix_l2_entry_by_zero(BlockDriverState *bs, BdrvCheckResult *res, ++ uint64_t l2_offset, uint64_t *l2_table, ++ int l2_index, bool active, ++ bool *metadata_overlap) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ int idx = l2_index * (l2_entry_size(s) / sizeof(uint64_t)); ++ uint64_t l2e_offset = l2_offset + (uint64_t)l2_index * l2_entry_size(s); ++ int ign = active ? QCOW2_OL_ACTIVE_L2 : QCOW2_OL_INACTIVE_L2; ++ ++ if (has_subclusters(s)) { ++ uint64_t l2_bitmap = get_l2_bitmap(s, l2_table, l2_index); ++ ++ /* Allocated subclusters become zero */ ++ l2_bitmap |= l2_bitmap << 32; ++ l2_bitmap &= QCOW_L2_BITMAP_ALL_ZEROES; ++ ++ set_l2_bitmap(s, l2_table, l2_index, l2_bitmap); ++ set_l2_entry(s, l2_table, l2_index, 0); ++ } else { ++ set_l2_entry(s, l2_table, l2_index, QCOW_OFLAG_ZERO); ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, ign, l2e_offset, l2_entry_size(s), ++ false); ++ if (metadata_overlap) { ++ *metadata_overlap = ret < 0; ++ } ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: Overlap check failed\n"); ++ goto fail; ++ } ++ ++ ret = bdrv_co_pwrite_sync(bs->file, l2e_offset, l2_entry_size(s), ++ &l2_table[idx], 0); ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: Failed to overwrite L2 " ++ "table entry: %s\n", strerror(-ret)); ++ goto fail; ++ } ++ ++ res->corruptions--; ++ res->corruptions_fixed++; ++ return 0; ++ ++fail: ++ res->check_errors++; ++ return ret; ++} ++ ++/* ++ * Increases the refcount in the given refcount table for the all clusters ++ * referenced in the L2 table. While doing so, performs some checks on L2 ++ * entries. ++ * ++ * Returns the number of errors found by the checks or -errno if an internal ++ * error occurred. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, ++ int64_t *refcount_table_size, int64_t l2_offset, ++ int flags, BdrvCheckMode fix, bool active) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t l2_entry, l2_bitmap; ++ uint64_t next_contiguous_offset = 0; ++ int i, ret; ++ size_t l2_size_bytes = s->l2_size * l2_entry_size(s); ++ g_autofree uint64_t *l2_table = g_malloc(l2_size_bytes); ++ bool metadata_overlap; ++ ++ /* Read L2 table from disk */ ++ ret = bdrv_co_pread(bs->file, l2_offset, l2_size_bytes, l2_table, 0); ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n"); ++ res->check_errors++; ++ return ret; ++ } ++ ++ /* Do the actual checks */ ++ for (i = 0; i < s->l2_size; i++) { ++ uint64_t coffset; ++ int csize; ++ QCow2ClusterType type; ++ ++ l2_entry = get_l2_entry(s, l2_table, i); ++ l2_bitmap = get_l2_bitmap(s, l2_table, i); ++ type = qcow2_get_cluster_type(bs, l2_entry); ++ ++ if (type != QCOW2_CLUSTER_COMPRESSED) { ++ /* Check reserved bits of Standard Cluster Descriptor */ ++ if (l2_entry & L2E_STD_RESERVED_MASK) { ++ fprintf(stderr, "ERROR found l2 entry with reserved bits set: " ++ "%" PRIx64 "\n", l2_entry); ++ res->corruptions++; ++ } ++ } ++ ++ switch (type) { ++ case QCOW2_CLUSTER_COMPRESSED: ++ /* Compressed clusters don't have QCOW_OFLAG_COPIED */ ++ if (l2_entry & QCOW_OFLAG_COPIED) { ++ fprintf(stderr, "ERROR: coffset=0x%" PRIx64 ": " ++ "copied flag must never be set for compressed " ++ "clusters\n", l2_entry & s->cluster_offset_mask); ++ l2_entry &= ~QCOW_OFLAG_COPIED; ++ res->corruptions++; ++ } ++ ++ if (has_data_file(bs)) { ++ fprintf(stderr, "ERROR compressed cluster %d with data file, " ++ "entry=0x%" PRIx64 "\n", i, l2_entry); ++ res->corruptions++; ++ break; ++ } ++ ++ if (l2_bitmap) { ++ fprintf(stderr, "ERROR compressed cluster %d with non-zero " ++ "subcluster allocation bitmap, entry=0x%" PRIx64 "\n", ++ i, l2_entry); ++ res->corruptions++; ++ break; ++ } ++ ++ /* Mark cluster as used */ ++ qcow2_parse_compressed_l2_entry(bs, l2_entry, &coffset, &csize); ++ ret = qcow2_inc_refcounts_imrt( ++ bs, res, refcount_table, refcount_table_size, coffset, csize); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (flags & CHECK_FRAG_INFO) { ++ res->bfi.allocated_clusters++; ++ res->bfi.compressed_clusters++; ++ ++ /* ++ * Compressed clusters are fragmented by nature. Since they ++ * take up sub-sector space but we only have sector granularity ++ * I/O we need to re-read the same sectors even for adjacent ++ * compressed clusters. ++ */ ++ res->bfi.fragmented_clusters++; ++ } ++ break; ++ ++ case QCOW2_CLUSTER_ZERO_ALLOC: ++ case QCOW2_CLUSTER_NORMAL: ++ { ++ uint64_t offset = l2_entry & L2E_OFFSET_MASK; ++ ++ if ((l2_bitmap >> 32) & l2_bitmap) { ++ res->corruptions++; ++ fprintf(stderr, "ERROR offset=%" PRIx64 ": Allocated " ++ "cluster has corrupted subcluster allocation bitmap\n", ++ offset); ++ } ++ ++ /* Correct offsets are cluster aligned */ ++ if (offset_into_cluster(s, offset)) { ++ bool contains_data; ++ res->corruptions++; ++ ++ if (has_subclusters(s)) { ++ contains_data = (l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC); ++ } else { ++ contains_data = !(l2_entry & QCOW_OFLAG_ZERO); ++ } ++ ++ if (!contains_data) { ++ fprintf(stderr, "%s offset=%" PRIx64 ": Preallocated " ++ "cluster is not properly aligned; L2 entry " ++ "corrupted.\n", ++ fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", ++ offset); ++ if (fix & BDRV_FIX_ERRORS) { ++ ret = fix_l2_entry_by_zero(bs, res, l2_offset, ++ l2_table, i, active, ++ &metadata_overlap); ++ if (metadata_overlap) { ++ /* ++ * Something is seriously wrong, so abort checking ++ * this L2 table. ++ */ ++ return ret; ++ } ++ ++ if (ret == 0) { ++ /* ++ * Skip marking the cluster as used ++ * (it is unused now). ++ */ ++ continue; ++ } ++ ++ /* ++ * Failed to fix. ++ * Do not abort, continue checking the rest of this ++ * L2 table's entries. ++ */ ++ } ++ } else { ++ fprintf(stderr, "ERROR offset=%" PRIx64 ": Data cluster is " ++ "not properly aligned; L2 entry corrupted.\n", offset); ++ } ++ } ++ ++ if (flags & CHECK_FRAG_INFO) { ++ res->bfi.allocated_clusters++; ++ if (next_contiguous_offset && ++ offset != next_contiguous_offset) { ++ res->bfi.fragmented_clusters++; ++ } ++ next_contiguous_offset = offset + s->cluster_size; ++ } ++ ++ /* Mark cluster as used */ ++ if (!has_data_file(bs)) { ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, ++ refcount_table_size, ++ offset, s->cluster_size); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ break; ++ } ++ ++ case QCOW2_CLUSTER_ZERO_PLAIN: ++ /* Impossible when image has subclusters */ ++ assert(!l2_bitmap); ++ break; ++ ++ case QCOW2_CLUSTER_UNALLOCATED: ++ if (l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC) { ++ res->corruptions++; ++ fprintf(stderr, "ERROR: Unallocated " ++ "cluster has non-zero subcluster allocation map\n"); ++ } ++ break; ++ ++ default: ++ abort(); ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Increases the refcount for the L1 table, its L2 tables and all referenced ++ * clusters in the given refcount table. While doing so, performs some checks ++ * on L1 and L2 entries. ++ * ++ * Returns the number of errors found by the checks or -errno if an internal ++ * error occurred. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++check_refcounts_l1(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, int64_t *refcount_table_size, ++ int64_t l1_table_offset, int l1_size, ++ int flags, BdrvCheckMode fix, bool active) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ size_t l1_size_bytes = l1_size * L1E_SIZE; ++ g_autofree uint64_t *l1_table = NULL; ++ uint64_t l2_offset; ++ int i, ret; ++ ++ if (!l1_size) { ++ return 0; ++ } ++ ++ /* Mark L1 table as used */ ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, refcount_table_size, ++ l1_table_offset, l1_size_bytes); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ l1_table = g_try_malloc(l1_size_bytes); ++ if (l1_table == NULL) { ++ res->check_errors++; ++ return -ENOMEM; ++ } ++ ++ /* Read L1 table entries from disk */ ++ ret = bdrv_co_pread(bs->file, l1_table_offset, l1_size_bytes, l1_table, 0); ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); ++ res->check_errors++; ++ return ret; ++ } ++ ++ for (i = 0; i < l1_size; i++) { ++ be64_to_cpus(&l1_table[i]); ++ } ++ ++ /* Do the actual checks */ ++ for (i = 0; i < l1_size; i++) { ++ if (!l1_table[i]) { ++ continue; ++ } ++ ++ if (l1_table[i] & L1E_RESERVED_MASK) { ++ fprintf(stderr, "ERROR found L1 entry with reserved bits set: " ++ "%" PRIx64 "\n", l1_table[i]); ++ res->corruptions++; ++ } ++ ++ l2_offset = l1_table[i] & L1E_OFFSET_MASK; ++ ++ /* Mark L2 table as used */ ++ ret = qcow2_inc_refcounts_imrt(bs, res, ++ refcount_table, refcount_table_size, ++ l2_offset, s->cluster_size); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* L2 tables are cluster aligned */ ++ if (offset_into_cluster(s, l2_offset)) { ++ fprintf(stderr, "ERROR l2_offset=%" PRIx64 ": Table is not " ++ "cluster aligned; L1 entry corrupted\n", l2_offset); ++ res->corruptions++; ++ } ++ ++ /* Process and check L2 entries */ ++ ret = check_refcounts_l2(bs, res, refcount_table, ++ refcount_table_size, l2_offset, flags, ++ fix, active); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Checks the OFLAG_COPIED flag for all L1 and L2 entries. ++ * ++ * This function does not print an error message nor does it increment ++ * check_errors if qcow2_get_refcount fails (this is because such an error will ++ * have been already detected and sufficiently signaled by the calling function ++ * (qcow2_check_refcounts) by the time this function is called). ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size); ++ int ret; ++ uint64_t refcount; ++ int i, j; ++ bool repair; ++ ++ if (fix & BDRV_FIX_ERRORS) { ++ /* Always repair */ ++ repair = true; ++ } else if (fix & BDRV_FIX_LEAKS) { ++ /* Repair only if that seems safe: This function is always ++ * called after the refcounts have been fixed, so the refcount ++ * is accurate if that repair was successful */ ++ repair = !res->check_errors && !res->corruptions && !res->leaks; ++ } else { ++ repair = false; ++ } ++ ++ for (i = 0; i < s->l1_size; i++) { ++ uint64_t l1_entry = s->l1_table[i]; ++ uint64_t l2_offset = l1_entry & L1E_OFFSET_MASK; ++ int l2_dirty = 0; ++ ++ if (!l2_offset) { ++ continue; ++ } ++ ++ ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, ++ &refcount); ++ if (ret < 0) { ++ /* don't print message nor increment check_errors */ ++ continue; ++ } ++ if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { ++ res->corruptions++; ++ fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " ++ "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", ++ repair ? "Repairing" : "ERROR", i, l1_entry, refcount); ++ if (repair) { ++ s->l1_table[i] = refcount == 1 ++ ? l1_entry | QCOW_OFLAG_COPIED ++ : l1_entry & ~QCOW_OFLAG_COPIED; ++ ret = qcow2_write_l1_entry(bs, i); ++ if (ret < 0) { ++ res->check_errors++; ++ goto fail; ++ } ++ res->corruptions--; ++ res->corruptions_fixed++; ++ } ++ } ++ ++ ret = bdrv_co_pread(bs->file, l2_offset, s->l2_size * l2_entry_size(s), ++ l2_table, 0); ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: Could not read L2 table: %s\n", ++ strerror(-ret)); ++ res->check_errors++; ++ goto fail; ++ } ++ ++ for (j = 0; j < s->l2_size; j++) { ++ uint64_t l2_entry = get_l2_entry(s, l2_table, j); ++ uint64_t data_offset = l2_entry & L2E_OFFSET_MASK; ++ QCow2ClusterType cluster_type = qcow2_get_cluster_type(bs, l2_entry); ++ ++ if (cluster_type == QCOW2_CLUSTER_NORMAL || ++ cluster_type == QCOW2_CLUSTER_ZERO_ALLOC) { ++ if (has_data_file(bs)) { ++ refcount = 1; ++ } else { ++ ret = qcow2_get_refcount(bs, ++ data_offset >> s->cluster_bits, ++ &refcount); ++ if (ret < 0) { ++ /* don't print message nor increment check_errors */ ++ continue; ++ } ++ } ++ if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { ++ res->corruptions++; ++ fprintf(stderr, "%s OFLAG_COPIED data cluster: " ++ "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", ++ repair ? "Repairing" : "ERROR", l2_entry, refcount); ++ if (repair) { ++ set_l2_entry(s, l2_table, j, ++ refcount == 1 ? ++ l2_entry | QCOW_OFLAG_COPIED : ++ l2_entry & ~QCOW_OFLAG_COPIED); ++ l2_dirty++; ++ } ++ } ++ } ++ } ++ ++ if (l2_dirty > 0) { ++ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, ++ l2_offset, s->cluster_size, ++ false); ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: Could not write L2 table; metadata " ++ "overlap check failed: %s\n", strerror(-ret)); ++ res->check_errors++; ++ goto fail; ++ } ++ ++ ret = bdrv_co_pwrite(bs->file, l2_offset, s->cluster_size, l2_table, 0); ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: Could not write L2 table: %s\n", ++ strerror(-ret)); ++ res->check_errors++; ++ goto fail; ++ } ++ res->corruptions -= l2_dirty; ++ res->corruptions_fixed += l2_dirty; ++ } ++ } ++ ++ ret = 0; ++ ++fail: ++ qemu_vfree(l2_table); ++ return ret; ++} ++ ++/* ++ * Checks consistency of refblocks and accounts for each refblock in ++ * *refcount_table. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, ++ BdrvCheckMode fix, bool *rebuild, ++ void **refcount_table, int64_t *nb_clusters) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t i, size; ++ int ret; ++ ++ for(i = 0; i < s->refcount_table_size; i++) { ++ uint64_t offset, cluster; ++ offset = s->refcount_table[i] & REFT_OFFSET_MASK; ++ cluster = offset >> s->cluster_bits; ++ ++ if (s->refcount_table[i] & REFT_RESERVED_MASK) { ++ fprintf(stderr, "ERROR refcount table entry %" PRId64 " has " ++ "reserved bits set\n", i); ++ res->corruptions++; ++ *rebuild = true; ++ continue; ++ } ++ ++ /* Refcount blocks are cluster aligned */ ++ if (offset_into_cluster(s, offset)) { ++ fprintf(stderr, "ERROR refcount block %" PRId64 " is not " ++ "cluster aligned; refcount table entry corrupted\n", i); ++ res->corruptions++; ++ *rebuild = true; ++ continue; ++ } ++ ++ if (cluster >= *nb_clusters) { ++ res->corruptions++; ++ fprintf(stderr, "%s refcount block %" PRId64 " is outside image\n", ++ fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); ++ ++ if (fix & BDRV_FIX_ERRORS) { ++ int64_t new_nb_clusters; ++ Error *local_err = NULL; ++ ++ if (offset > INT64_MAX - s->cluster_size) { ++ ret = -EINVAL; ++ goto resize_fail; ++ } ++ ++ ret = bdrv_co_truncate(bs->file, offset + s->cluster_size, false, ++ PREALLOC_MODE_OFF, 0, &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto resize_fail; ++ } ++ size = bdrv_co_getlength(bs->file->bs); ++ if (size < 0) { ++ ret = size; ++ goto resize_fail; ++ } ++ ++ new_nb_clusters = size_to_clusters(s, size); ++ assert(new_nb_clusters >= *nb_clusters); ++ ++ ret = realloc_refcount_array(s, refcount_table, ++ nb_clusters, new_nb_clusters); ++ if (ret < 0) { ++ res->check_errors++; ++ return ret; ++ } ++ ++ if (cluster >= *nb_clusters) { ++ ret = -EINVAL; ++ goto resize_fail; ++ } ++ ++ res->corruptions--; ++ res->corruptions_fixed++; ++ ret = qcow2_inc_refcounts_imrt(bs, res, ++ refcount_table, nb_clusters, ++ offset, s->cluster_size); ++ if (ret < 0) { ++ return ret; ++ } ++ /* No need to check whether the refcount is now greater than 1: ++ * This area was just allocated and zeroed, so it can only be ++ * exactly 1 after qcow2_inc_refcounts_imrt() */ ++ continue; ++ ++resize_fail: ++ *rebuild = true; ++ fprintf(stderr, "ERROR could not resize image: %s\n", ++ strerror(-ret)); ++ } ++ continue; ++ } ++ ++ if (offset != 0) { ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, ++ offset, s->cluster_size); ++ if (ret < 0) { ++ return ret; ++ } ++ if (s->get_refcount(*refcount_table, cluster) != 1) { ++ fprintf(stderr, "ERROR refcount block %" PRId64 ++ " refcount=%" PRIu64 "\n", i, ++ s->get_refcount(*refcount_table, cluster)); ++ res->corruptions++; ++ *rebuild = true; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Calculates an in-memory refcount table. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, ++ BdrvCheckMode fix, bool *rebuild, ++ void **refcount_table, int64_t *nb_clusters) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t i; ++ QCowSnapshot *sn; ++ int ret; ++ ++ if (!*refcount_table) { ++ int64_t old_size = 0; ++ ret = realloc_refcount_array(s, refcount_table, ++ &old_size, *nb_clusters); ++ if (ret < 0) { ++ res->check_errors++; ++ return ret; ++ } ++ } ++ ++ /* header */ ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, ++ 0, s->cluster_size); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* current L1 table */ ++ ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, ++ s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO, ++ fix, true); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* snapshots */ ++ if (has_data_file(bs) && s->nb_snapshots) { ++ fprintf(stderr, "ERROR %d snapshots in image with data file\n", ++ s->nb_snapshots); ++ res->corruptions++; ++ } ++ ++ for (i = 0; i < s->nb_snapshots; i++) { ++ sn = s->snapshots + i; ++ if (offset_into_cluster(s, sn->l1_table_offset)) { ++ fprintf(stderr, "ERROR snapshot %s (%s) l1_offset=%#" PRIx64 ": " ++ "L1 table is not cluster aligned; snapshot table entry " ++ "corrupted\n", sn->id_str, sn->name, sn->l1_table_offset); ++ res->corruptions++; ++ continue; ++ } ++ if (sn->l1_size > QCOW_MAX_L1_SIZE / L1E_SIZE) { ++ fprintf(stderr, "ERROR snapshot %s (%s) l1_size=%#" PRIx32 ": " ++ "L1 table is too large; snapshot table entry corrupted\n", ++ sn->id_str, sn->name, sn->l1_size); ++ res->corruptions++; ++ continue; ++ } ++ ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, ++ sn->l1_table_offset, sn->l1_size, 0, fix, ++ false); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, ++ s->snapshots_offset, s->snapshots_size); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* refcount data */ ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, ++ s->refcount_table_offset, ++ s->refcount_table_size * ++ REFTABLE_ENTRY_SIZE); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* encryption */ ++ if (s->crypto_header.length) { ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, ++ s->crypto_header.offset, ++ s->crypto_header.length); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ /* bitmaps */ ++ ret = qcow2_check_bitmaps_refcounts(bs, res, refcount_table, nb_clusters); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return check_refblocks(bs, res, fix, rebuild, refcount_table, nb_clusters); ++} ++ ++/* ++ * Compares the actual reference count for each cluster in the image against the ++ * refcount as reported by the refcount structures on-disk. ++ */ ++static void coroutine_fn GRAPH_RDLOCK ++compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, ++ BdrvCheckMode fix, bool *rebuild, ++ int64_t *highest_cluster, ++ void *refcount_table, int64_t nb_clusters) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t i; ++ uint64_t refcount1, refcount2; ++ int ret; ++ ++ for (i = 0, *highest_cluster = 0; i < nb_clusters; i++) { ++ ret = qcow2_get_refcount(bs, i, &refcount1); ++ if (ret < 0) { ++ fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n", ++ i, strerror(-ret)); ++ res->check_errors++; ++ continue; ++ } ++ ++ refcount2 = s->get_refcount(refcount_table, i); ++ ++ if (refcount1 > 0 || refcount2 > 0) { ++ *highest_cluster = i; ++ } ++ ++ if (refcount1 != refcount2) { ++ /* Check if we're allowed to fix the mismatch */ ++ int *num_fixed = NULL; ++ if (refcount1 == 0) { ++ *rebuild = true; ++ } else if (refcount1 > refcount2 && (fix & BDRV_FIX_LEAKS)) { ++ num_fixed = &res->leaks_fixed; ++ } else if (refcount1 < refcount2 && (fix & BDRV_FIX_ERRORS)) { ++ num_fixed = &res->corruptions_fixed; ++ } ++ ++ fprintf(stderr, "%s cluster %" PRId64 " refcount=%" PRIu64 ++ " reference=%" PRIu64 "\n", ++ num_fixed != NULL ? "Repairing" : ++ refcount1 < refcount2 ? "ERROR" : ++ "Leaked", ++ i, refcount1, refcount2); ++ ++ if (num_fixed) { ++ ret = update_refcount(bs, i << s->cluster_bits, 1, ++ refcount_diff(refcount1, refcount2), ++ refcount1 > refcount2, ++ QCOW2_DISCARD_ALWAYS); ++ if (ret >= 0) { ++ (*num_fixed)++; ++ continue; ++ } ++ } ++ ++ /* And if we couldn't, print an error */ ++ if (refcount1 < refcount2) { ++ res->corruptions++; ++ } else { ++ res->leaks++; ++ } ++ } ++ } ++} ++ ++/* ++ * Allocates clusters using an in-memory refcount table (IMRT) in contrast to ++ * the on-disk refcount structures. ++ * ++ * On input, *first_free_cluster tells where to start looking, and need not ++ * actually be a free cluster; the returned offset will not be before that ++ * cluster. On output, *first_free_cluster points to the first gap found, even ++ * if that gap was too small to be used as the returned offset. ++ * ++ * Note that *first_free_cluster is a cluster index whereas the return value is ++ * an offset. ++ */ ++static int64_t alloc_clusters_imrt(BlockDriverState *bs, ++ int cluster_count, ++ void **refcount_table, ++ int64_t *imrt_nb_clusters, ++ int64_t *first_free_cluster) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t cluster = *first_free_cluster, i; ++ bool first_gap = true; ++ int contiguous_free_clusters; ++ int ret; ++ ++ /* Starting at *first_free_cluster, find a range of at least cluster_count ++ * continuously free clusters */ ++ for (contiguous_free_clusters = 0; ++ cluster < *imrt_nb_clusters && ++ contiguous_free_clusters < cluster_count; ++ cluster++) ++ { ++ if (!s->get_refcount(*refcount_table, cluster)) { ++ contiguous_free_clusters++; ++ if (first_gap) { ++ /* If this is the first free cluster found, update ++ * *first_free_cluster accordingly */ ++ *first_free_cluster = cluster; ++ first_gap = false; ++ } ++ } else if (contiguous_free_clusters) { ++ contiguous_free_clusters = 0; ++ } ++ } ++ ++ /* If contiguous_free_clusters is greater than zero, it contains the number ++ * of continuously free clusters until the current cluster; the first free ++ * cluster in the current "gap" is therefore ++ * cluster - contiguous_free_clusters */ ++ ++ /* If no such range could be found, grow the in-memory refcount table ++ * accordingly to append free clusters at the end of the image */ ++ if (contiguous_free_clusters < cluster_count) { ++ /* contiguous_free_clusters clusters are already empty at the image end; ++ * we need cluster_count clusters; therefore, we have to allocate ++ * cluster_count - contiguous_free_clusters new clusters at the end of ++ * the image (which is the current value of cluster; note that cluster ++ * may exceed old_imrt_nb_clusters if *first_free_cluster pointed beyond ++ * the image end) */ ++ ret = realloc_refcount_array(s, refcount_table, imrt_nb_clusters, ++ cluster + cluster_count ++ - contiguous_free_clusters); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ /* Go back to the first free cluster */ ++ cluster -= contiguous_free_clusters; ++ for (i = 0; i < cluster_count; i++) { ++ s->set_refcount(*refcount_table, cluster + i, 1); ++ } ++ ++ return cluster << s->cluster_bits; ++} ++ ++/* ++ * Helper function for rebuild_refcount_structure(). ++ * ++ * Scan the range of clusters [first_cluster, end_cluster) for allocated ++ * clusters and write all corresponding refblocks to disk. The refblock ++ * and allocation data is taken from the in-memory refcount table ++ * *refcount_table[] (of size *nb_clusters), which is basically one big ++ * (unlimited size) refblock for the whole image. ++ * ++ * For these refblocks, clusters are allocated using said in-memory ++ * refcount table. Care is taken that these allocations are reflected ++ * in the refblocks written to disk. ++ * ++ * The refblocks' offsets are written into a reftable, which is ++ * *on_disk_reftable_ptr[] (of size *on_disk_reftable_entries_ptr). If ++ * that reftable is of insufficient size, it will be resized to fit. ++ * This reftable is not written to disk. ++ * ++ * (If *on_disk_reftable_ptr is not NULL, the entries within are assumed ++ * to point to existing valid refblocks that do not need to be allocated ++ * again.) ++ * ++ * Return whether the on-disk reftable array was resized (true/false), ++ * or -errno on error. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++rebuild_refcounts_write_refblocks( ++ BlockDriverState *bs, void **refcount_table, int64_t *nb_clusters, ++ int64_t first_cluster, int64_t end_cluster, ++ uint64_t **on_disk_reftable_ptr, uint32_t *on_disk_reftable_entries_ptr, ++ Error **errp ++ ) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t cluster; ++ int64_t refblock_offset, refblock_start, refblock_index; ++ int64_t first_free_cluster = 0; ++ uint64_t *on_disk_reftable = *on_disk_reftable_ptr; ++ uint32_t on_disk_reftable_entries = *on_disk_reftable_entries_ptr; ++ void *on_disk_refblock; ++ bool reftable_grown = false; ++ int ret; ++ ++ for (cluster = first_cluster; cluster < end_cluster; cluster++) { ++ /* Check all clusters to find refblocks that contain non-zero entries */ ++ if (!s->get_refcount(*refcount_table, cluster)) { ++ continue; ++ } ++ ++ /* ++ * This cluster is allocated, so we need to create a refblock ++ * for it. The data we will write to disk is just the ++ * respective slice from *refcount_table, so it will contain ++ * accurate refcounts for all clusters belonging to this ++ * refblock. After we have written it, we will therefore skip ++ * all remaining clusters in this refblock. ++ */ ++ ++ refblock_index = cluster >> s->refcount_block_bits; ++ refblock_start = refblock_index << s->refcount_block_bits; ++ ++ if (on_disk_reftable_entries > refblock_index && ++ on_disk_reftable[refblock_index]) ++ { ++ /* ++ * We can get here after a `goto write_refblocks`: We have a ++ * reftable from a previous run, and the refblock is already ++ * allocated. No need to allocate it again. ++ */ ++ refblock_offset = on_disk_reftable[refblock_index]; ++ } else { ++ int64_t refblock_cluster_index; ++ ++ /* Don't allocate a cluster in a refblock already written to disk */ ++ if (first_free_cluster < refblock_start) { ++ first_free_cluster = refblock_start; ++ } ++ refblock_offset = alloc_clusters_imrt(bs, 1, refcount_table, ++ nb_clusters, ++ &first_free_cluster); ++ if (refblock_offset < 0) { ++ error_setg_errno(errp, -refblock_offset, ++ "ERROR allocating refblock"); ++ return refblock_offset; ++ } ++ ++ refblock_cluster_index = refblock_offset / s->cluster_size; ++ if (refblock_cluster_index >= end_cluster) { ++ /* ++ * We must write the refblock that holds this refblock's ++ * refcount ++ */ ++ end_cluster = refblock_cluster_index + 1; ++ } ++ ++ if (on_disk_reftable_entries <= refblock_index) { ++ on_disk_reftable_entries = ++ ROUND_UP((refblock_index + 1) * REFTABLE_ENTRY_SIZE, ++ s->cluster_size) / REFTABLE_ENTRY_SIZE; ++ on_disk_reftable = ++ g_try_realloc(on_disk_reftable, ++ on_disk_reftable_entries * ++ REFTABLE_ENTRY_SIZE); ++ if (!on_disk_reftable) { ++ error_setg(errp, "ERROR allocating reftable memory"); ++ return -ENOMEM; ++ } ++ ++ memset(on_disk_reftable + *on_disk_reftable_entries_ptr, 0, ++ (on_disk_reftable_entries - ++ *on_disk_reftable_entries_ptr) * ++ REFTABLE_ENTRY_SIZE); ++ ++ *on_disk_reftable_ptr = on_disk_reftable; ++ *on_disk_reftable_entries_ptr = on_disk_reftable_entries; ++ ++ reftable_grown = true; ++ } else { ++ assert(on_disk_reftable); ++ } ++ on_disk_reftable[refblock_index] = refblock_offset; ++ } ++ ++ /* Refblock is allocated, write it to disk */ ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, refblock_offset, ++ s->cluster_size, false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR writing refblock"); ++ return ret; ++ } ++ ++ /* ++ * The refblock is simply a slice of *refcount_table. ++ * Note that the size of *refcount_table is always aligned to ++ * whole clusters, so the write operation will not result in ++ * out-of-bounds accesses. ++ */ ++ on_disk_refblock = (void *)((char *) *refcount_table + ++ refblock_index * s->cluster_size); ++ ++ ret = bdrv_co_pwrite(bs->file, refblock_offset, s->cluster_size, ++ on_disk_refblock, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR writing refblock"); ++ return ret; ++ } ++ ++ /* This refblock is done, skip to its end */ ++ cluster = refblock_start + s->refcount_block_size - 1; ++ } ++ ++ return reftable_grown; ++} ++ ++/* ++ * Creates a new refcount structure based solely on the in-memory information ++ * given through *refcount_table (this in-memory information is basically just ++ * the concatenation of all refblocks). All necessary allocations will be ++ * reflected in that array. ++ * ++ * On success, the old refcount structure is leaked (it will be covered by the ++ * new refcount structure). ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++rebuild_refcount_structure(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, int64_t *nb_clusters, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t reftable_offset = -1; ++ int64_t reftable_length = 0; ++ int64_t reftable_clusters; ++ int64_t refblock_index; ++ uint32_t on_disk_reftable_entries = 0; ++ uint64_t *on_disk_reftable = NULL; ++ int ret = 0; ++ int reftable_size_changed = 0; ++ struct { ++ uint64_t reftable_offset; ++ uint32_t reftable_clusters; ++ } QEMU_PACKED reftable_offset_and_clusters; ++ ++ qcow2_cache_empty(bs, s->refcount_block_cache); ++ ++ /* ++ * For each refblock containing entries, we try to allocate a ++ * cluster (in the in-memory refcount table) and write its offset ++ * into on_disk_reftable[]. We then write the whole refblock to ++ * disk (as a slice of the in-memory refcount table). ++ * This is done by rebuild_refcounts_write_refblocks(). ++ * ++ * Once we have scanned all clusters, we try to find space for the ++ * reftable. This will dirty the in-memory refcount table (i.e. ++ * make it differ from the refblocks we have already written), so we ++ * need to run rebuild_refcounts_write_refblocks() again for the ++ * range of clusters where the reftable has been allocated. ++ * ++ * This second run might make the reftable grow again, in which case ++ * we will need to allocate another space for it, which is why we ++ * repeat all this until the reftable stops growing. ++ * ++ * (This loop will terminate, because with every cluster the ++ * reftable grows, it can accommodate a multitude of more refcounts, ++ * so that at some point this must be able to cover the reftable ++ * and all refblocks describing it.) ++ * ++ * We then convert the reftable to big-endian and write it to disk. ++ * ++ * Note that we never free any reftable allocations. Doing so would ++ * needlessly complicate the algorithm: The eventual second check ++ * run we do will clean up all leaks we have caused. ++ */ ++ ++ reftable_size_changed = ++ rebuild_refcounts_write_refblocks(bs, refcount_table, nb_clusters, ++ 0, *nb_clusters, ++ &on_disk_reftable, ++ &on_disk_reftable_entries, errp); ++ if (reftable_size_changed < 0) { ++ res->check_errors++; ++ ret = reftable_size_changed; ++ goto fail; ++ } ++ ++ /* ++ * There was no reftable before, so rebuild_refcounts_write_refblocks() ++ * must have increased its size (from 0 to something). ++ */ ++ assert(reftable_size_changed); ++ ++ do { ++ int64_t reftable_start_cluster, reftable_end_cluster; ++ int64_t first_free_cluster = 0; ++ ++ reftable_length = on_disk_reftable_entries * REFTABLE_ENTRY_SIZE; ++ reftable_clusters = size_to_clusters(s, reftable_length); ++ ++ reftable_offset = alloc_clusters_imrt(bs, reftable_clusters, ++ refcount_table, nb_clusters, ++ &first_free_cluster); ++ if (reftable_offset < 0) { ++ error_setg_errno(errp, -reftable_offset, ++ "ERROR allocating reftable"); ++ res->check_errors++; ++ ret = reftable_offset; ++ goto fail; ++ } ++ ++ /* ++ * We need to update the affected refblocks, so re-run the ++ * write_refblocks loop for the reftable's range of clusters. ++ */ ++ assert(offset_into_cluster(s, reftable_offset) == 0); ++ reftable_start_cluster = reftable_offset / s->cluster_size; ++ reftable_end_cluster = reftable_start_cluster + reftable_clusters; ++ reftable_size_changed = ++ rebuild_refcounts_write_refblocks(bs, refcount_table, nb_clusters, ++ reftable_start_cluster, ++ reftable_end_cluster, ++ &on_disk_reftable, ++ &on_disk_reftable_entries, errp); ++ if (reftable_size_changed < 0) { ++ res->check_errors++; ++ ret = reftable_size_changed; ++ goto fail; ++ } ++ ++ /* ++ * If the reftable size has changed, we will need to find a new ++ * allocation, repeating the loop. ++ */ ++ } while (reftable_size_changed); ++ ++ /* The above loop must have run at least once */ ++ assert(reftable_offset >= 0); ++ ++ /* ++ * All allocations are done, all refblocks are written, convert the ++ * reftable to big-endian and write it to disk. ++ */ ++ ++ for (refblock_index = 0; refblock_index < on_disk_reftable_entries; ++ refblock_index++) ++ { ++ cpu_to_be64s(&on_disk_reftable[refblock_index]); ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset, reftable_length, ++ false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR writing reftable"); ++ goto fail; ++ } ++ ++ assert(reftable_length < INT_MAX); ++ ret = bdrv_co_pwrite(bs->file, reftable_offset, reftable_length, ++ on_disk_reftable, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR writing reftable"); ++ goto fail; ++ } ++ ++ /* Enter new reftable into the image header */ ++ reftable_offset_and_clusters.reftable_offset = cpu_to_be64(reftable_offset); ++ reftable_offset_and_clusters.reftable_clusters = ++ cpu_to_be32(reftable_clusters); ++ ret = bdrv_co_pwrite_sync(bs->file, ++ offsetof(QCowHeader, refcount_table_offset), ++ sizeof(reftable_offset_and_clusters), ++ &reftable_offset_and_clusters, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR setting reftable"); ++ goto fail; ++ } ++ ++ for (refblock_index = 0; refblock_index < on_disk_reftable_entries; ++ refblock_index++) ++ { ++ be64_to_cpus(&on_disk_reftable[refblock_index]); ++ } ++ s->refcount_table = on_disk_reftable; ++ s->refcount_table_offset = reftable_offset; ++ s->refcount_table_size = on_disk_reftable_entries; ++ update_max_refcount_table_index(s); ++ ++ return 0; ++ ++fail: ++ g_free(on_disk_reftable); ++ return ret; ++} ++ ++/* ++ * Checks an image for refcount consistency. ++ * ++ * Returns 0 if no errors are found, the number of errors in case the image is ++ * detected as corrupted, and -errno when an internal error occurred. ++ */ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ BdrvCheckResult pre_compare_res; ++ int64_t size, highest_cluster, nb_clusters; ++ void *refcount_table = NULL; ++ bool rebuild = false; ++ int ret; ++ ++ size = bdrv_co_getlength(bs->file->bs); ++ if (size < 0) { ++ res->check_errors++; ++ return size; ++ } ++ ++ nb_clusters = size_to_clusters(s, size); ++ if (nb_clusters > INT_MAX) { ++ res->check_errors++; ++ return -EFBIG; ++ } ++ ++ res->bfi.total_clusters = ++ size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE); ++ ++ ret = calculate_refcounts(bs, res, fix, &rebuild, &refcount_table, ++ &nb_clusters); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* In case we don't need to rebuild the refcount structure (but want to fix ++ * something), this function is immediately called again, in which case the ++ * result should be ignored */ ++ pre_compare_res = *res; ++ compare_refcounts(bs, res, 0, &rebuild, &highest_cluster, refcount_table, ++ nb_clusters); ++ ++ if (rebuild && (fix & BDRV_FIX_ERRORS)) { ++ BdrvCheckResult old_res = *res; ++ int fresh_leaks = 0; ++ Error *local_err = NULL; ++ ++ fprintf(stderr, "Rebuilding refcount structure\n"); ++ ret = rebuild_refcount_structure(bs, res, &refcount_table, ++ &nb_clusters, &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto fail; ++ } ++ ++ res->corruptions = 0; ++ res->leaks = 0; ++ ++ /* Because the old reftable has been exchanged for a new one the ++ * references have to be recalculated */ ++ rebuild = false; ++ memset(refcount_table, 0, refcount_array_byte_size(s, nb_clusters)); ++ ret = calculate_refcounts(bs, res, 0, &rebuild, &refcount_table, ++ &nb_clusters); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if (fix & BDRV_FIX_LEAKS) { ++ /* The old refcount structures are now leaked, fix it; the result ++ * can be ignored, aside from leaks which were introduced by ++ * rebuild_refcount_structure() that could not be fixed */ ++ BdrvCheckResult saved_res = *res; ++ *res = (BdrvCheckResult){ 0 }; ++ ++ compare_refcounts(bs, res, BDRV_FIX_LEAKS, &rebuild, ++ &highest_cluster, refcount_table, nb_clusters); ++ if (rebuild) { ++ fprintf(stderr, "ERROR rebuilt refcount structure is still " ++ "broken\n"); ++ } ++ ++ /* Any leaks accounted for here were introduced by ++ * rebuild_refcount_structure() because that function has created a ++ * new refcount structure from scratch */ ++ fresh_leaks = res->leaks; ++ *res = saved_res; ++ } ++ ++ if (res->corruptions < old_res.corruptions) { ++ res->corruptions_fixed += old_res.corruptions - res->corruptions; ++ } ++ if (res->leaks < old_res.leaks) { ++ res->leaks_fixed += old_res.leaks - res->leaks; ++ } ++ res->leaks += fresh_leaks; ++ } else if (fix) { ++ if (rebuild) { ++ fprintf(stderr, "ERROR need to rebuild refcount structures\n"); ++ res->check_errors++; ++ ret = -EIO; ++ goto fail; ++ } ++ ++ if (res->leaks || res->corruptions) { ++ *res = pre_compare_res; ++ compare_refcounts(bs, res, fix, &rebuild, &highest_cluster, ++ refcount_table, nb_clusters); ++ } ++ } ++ ++ /* check OFLAG_COPIED */ ++ ret = check_oflag_copied(bs, res, fix); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ res->image_end_offset = (highest_cluster + 1) * s->cluster_size; ++ ret = 0; ++ ++fail: ++ g_free(refcount_table); ++ ++ return ret; ++} ++ ++#define overlaps_with(ofs, sz) \ ++ ranges_overlap(offset, size, ofs, sz) ++ ++/* ++ * Checks if the given offset into the image file is actually free to use by ++ * looking for overlaps with important metadata sections (L1/L2 tables etc.), ++ * i.e. a sanity check without relying on the refcount tables. ++ * ++ * The ign parameter specifies what checks not to perform (being a bitmask of ++ * QCow2MetadataOverlap values), i.e., what sections to ignore. ++ * ++ * Returns: ++ * - 0 if writing to this offset will not affect the mentioned metadata ++ * - a positive QCow2MetadataOverlap value indicating one overlapping section ++ * - a negative value (-errno) indicating an error while performing a check, ++ * e.g. when bdrv_pread failed on QCOW2_OL_INACTIVE_L2 ++ */ ++int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, ++ int64_t size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int chk = s->overlap_check & ~ign; ++ int i, j; ++ ++ if (!size) { ++ return 0; ++ } ++ ++ if (chk & QCOW2_OL_MAIN_HEADER) { ++ if (offset < s->cluster_size) { ++ return QCOW2_OL_MAIN_HEADER; ++ } ++ } ++ ++ /* align range to test to cluster boundaries */ ++ size = ROUND_UP(offset_into_cluster(s, offset) + size, s->cluster_size); ++ offset = start_of_cluster(s, offset); ++ ++ if ((chk & QCOW2_OL_ACTIVE_L1) && s->l1_size) { ++ if (overlaps_with(s->l1_table_offset, s->l1_size * L1E_SIZE)) { ++ return QCOW2_OL_ACTIVE_L1; ++ } ++ } ++ ++ if ((chk & QCOW2_OL_REFCOUNT_TABLE) && s->refcount_table_size) { ++ if (overlaps_with(s->refcount_table_offset, ++ s->refcount_table_size * REFTABLE_ENTRY_SIZE)) { ++ return QCOW2_OL_REFCOUNT_TABLE; ++ } ++ } ++ ++ if ((chk & QCOW2_OL_SNAPSHOT_TABLE) && s->snapshots_size) { ++ if (overlaps_with(s->snapshots_offset, s->snapshots_size)) { ++ return QCOW2_OL_SNAPSHOT_TABLE; ++ } ++ } ++ ++ if ((chk & QCOW2_OL_INACTIVE_L1) && s->snapshots) { ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (s->snapshots[i].l1_size && ++ overlaps_with(s->snapshots[i].l1_table_offset, ++ s->snapshots[i].l1_size * L1E_SIZE)) { ++ return QCOW2_OL_INACTIVE_L1; ++ } ++ } ++ } ++ ++ if ((chk & QCOW2_OL_ACTIVE_L2) && s->l1_table) { ++ for (i = 0; i < s->l1_size; i++) { ++ if ((s->l1_table[i] & L1E_OFFSET_MASK) && ++ overlaps_with(s->l1_table[i] & L1E_OFFSET_MASK, ++ s->cluster_size)) { ++ return QCOW2_OL_ACTIVE_L2; ++ } ++ } ++ } ++ ++ if ((chk & QCOW2_OL_REFCOUNT_BLOCK) && s->refcount_table) { ++ unsigned last_entry = s->max_refcount_table_index; ++ assert(last_entry < s->refcount_table_size); ++ assert(last_entry + 1 == s->refcount_table_size || ++ (s->refcount_table[last_entry + 1] & REFT_OFFSET_MASK) == 0); ++ for (i = 0; i <= last_entry; i++) { ++ if ((s->refcount_table[i] & REFT_OFFSET_MASK) && ++ overlaps_with(s->refcount_table[i] & REFT_OFFSET_MASK, ++ s->cluster_size)) { ++ return QCOW2_OL_REFCOUNT_BLOCK; ++ } ++ } ++ } ++ ++ if ((chk & QCOW2_OL_INACTIVE_L2) && s->snapshots) { ++ for (i = 0; i < s->nb_snapshots; i++) { ++ uint64_t l1_ofs = s->snapshots[i].l1_table_offset; ++ uint32_t l1_sz = s->snapshots[i].l1_size; ++ uint64_t l1_sz2 = l1_sz * L1E_SIZE; ++ uint64_t *l1; ++ int ret; ++ ++ ret = qcow2_validate_table(bs, l1_ofs, l1_sz, L1E_SIZE, ++ QCOW_MAX_L1_SIZE, "", NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ l1 = g_try_malloc(l1_sz2); ++ ++ if (l1_sz2 && l1 == NULL) { ++ return -ENOMEM; ++ } ++ ++ ret = bdrv_pread(bs->file, l1_ofs, l1_sz2, l1, 0); ++ if (ret < 0) { ++ g_free(l1); ++ return ret; ++ } ++ ++ for (j = 0; j < l1_sz; j++) { ++ uint64_t l2_ofs = be64_to_cpu(l1[j]) & L1E_OFFSET_MASK; ++ if (l2_ofs && overlaps_with(l2_ofs, s->cluster_size)) { ++ g_free(l1); ++ return QCOW2_OL_INACTIVE_L2; ++ } ++ } ++ ++ g_free(l1); ++ } ++ } ++ ++ if ((chk & QCOW2_OL_BITMAP_DIRECTORY) && ++ (s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS)) ++ { ++ if (overlaps_with(s->bitmap_directory_offset, ++ s->bitmap_directory_size)) ++ { ++ return QCOW2_OL_BITMAP_DIRECTORY; ++ } ++ } ++ ++ return 0; ++} ++ ++static const char *metadata_ol_names[] = { ++ [QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header", ++ [QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table", ++ [QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table", ++ [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table", ++ [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block", ++ [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table", ++ [QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table", ++ [QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table", ++ [QCOW2_OL_BITMAP_DIRECTORY_BITNR] = "bitmap directory", ++}; ++QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names)); ++ ++/* ++ * First performs a check for metadata overlaps (through ++ * qcow2_check_metadata_overlap); if that fails with a negative value (error ++ * while performing a check), that value is returned. If an impending overlap ++ * is detected, the BDS will be made unusable, the qcow2 file marked corrupt ++ * and -EIO returned. ++ * ++ * Returns 0 if there were neither overlaps nor errors while checking for ++ * overlaps; or a negative value (-errno) on error. ++ */ ++int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, ++ int64_t size, bool data_file) ++{ ++ int ret; ++ ++ if (data_file && has_data_file(bs)) { ++ return 0; ++ } ++ ++ ret = qcow2_check_metadata_overlap(bs, ign, offset, size); ++ if (ret < 0) { ++ return ret; ++ } else if (ret > 0) { ++ int metadata_ol_bitnr = ctz32(ret); ++ assert(metadata_ol_bitnr < QCOW2_OL_MAX_BITNR); ++ ++ qcow2_signal_corruption(bs, true, offset, size, "Preventing invalid " ++ "write on metadata (overlaps with %s)", ++ metadata_ol_names[metadata_ol_bitnr]); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++/* A pointer to a function of this type is given to walk_over_reftable(). That ++ * function will create refblocks and pass them to a RefblockFinishOp once they ++ * are completed (@refblock). @refblock_empty is set if the refblock is ++ * completely empty. ++ * ++ * Along with the refblock, a corresponding reftable entry is passed, in the ++ * reftable @reftable (which may be reallocated) at @reftable_index. ++ * ++ * @allocated should be set to true if a new cluster has been allocated. ++ */ ++typedef int /* GRAPH_RDLOCK_PTR */ ++ (RefblockFinishOp)(BlockDriverState *bs, uint64_t **reftable, ++ uint64_t reftable_index, uint64_t *reftable_size, ++ void *refblock, bool refblock_empty, ++ bool *allocated, Error **errp); ++ ++/** ++ * This "operation" for walk_over_reftable() allocates the refblock on disk (if ++ * it is not empty) and inserts its offset into the new reftable. The size of ++ * this new reftable is increased as required. ++ */ ++static int GRAPH_RDLOCK ++alloc_refblock(BlockDriverState *bs, uint64_t **reftable, ++ uint64_t reftable_index, uint64_t *reftable_size, ++ void *refblock, bool refblock_empty, bool *allocated, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t offset; ++ ++ if (!refblock_empty && reftable_index >= *reftable_size) { ++ uint64_t *new_reftable; ++ uint64_t new_reftable_size; ++ ++ new_reftable_size = ROUND_UP(reftable_index + 1, ++ s->cluster_size / REFTABLE_ENTRY_SIZE); ++ if (new_reftable_size > QCOW_MAX_REFTABLE_SIZE / REFTABLE_ENTRY_SIZE) { ++ error_setg(errp, ++ "This operation would make the refcount table grow " ++ "beyond the maximum size supported by QEMU, aborting"); ++ return -ENOTSUP; ++ } ++ ++ new_reftable = g_try_realloc(*reftable, new_reftable_size * ++ REFTABLE_ENTRY_SIZE); ++ if (!new_reftable) { ++ error_setg(errp, "Failed to increase reftable buffer size"); ++ return -ENOMEM; ++ } ++ ++ memset(new_reftable + *reftable_size, 0, ++ (new_reftable_size - *reftable_size) * REFTABLE_ENTRY_SIZE); ++ ++ *reftable = new_reftable; ++ *reftable_size = new_reftable_size; ++ } ++ ++ if (!refblock_empty && !(*reftable)[reftable_index]) { ++ offset = qcow2_alloc_clusters(bs, s->cluster_size); ++ if (offset < 0) { ++ error_setg_errno(errp, -offset, "Failed to allocate refblock"); ++ return offset; ++ } ++ (*reftable)[reftable_index] = offset; ++ *allocated = true; ++ } ++ ++ return 0; ++} ++ ++/** ++ * This "operation" for walk_over_reftable() writes the refblock to disk at the ++ * offset specified by the new reftable's entry. It does not modify the new ++ * reftable or change any refcounts. ++ */ ++static int GRAPH_RDLOCK ++flush_refblock(BlockDriverState *bs, uint64_t **reftable, ++ uint64_t reftable_index, uint64_t *reftable_size, ++ void *refblock, bool refblock_empty, bool *allocated, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t offset; ++ int ret; ++ ++ if (reftable_index < *reftable_size && (*reftable)[reftable_index]) { ++ offset = (*reftable)[reftable_index]; ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size, ++ false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Overlap check failed"); ++ return ret; ++ } ++ ++ ret = bdrv_pwrite(bs->file, offset, s->cluster_size, refblock, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to write refblock"); ++ return ret; ++ } ++ } else { ++ assert(refblock_empty); ++ } ++ ++ return 0; ++} ++ ++/** ++ * This function walks over the existing reftable and every referenced refblock; ++ * if @new_set_refcount is non-NULL, it is called for every refcount entry to ++ * create an equal new entry in the passed @new_refblock. Once that ++ * @new_refblock is completely filled, @operation will be called. ++ * ++ * @status_cb and @cb_opaque are used for the amend operation's status callback. ++ * @index is the index of the walk_over_reftable() calls and @total is the total ++ * number of walk_over_reftable() calls per amend operation. Both are used for ++ * calculating the parameters for the status callback. ++ * ++ * @allocated is set to true if a new cluster has been allocated. ++ */ ++static int GRAPH_RDLOCK ++walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable, ++ uint64_t *new_reftable_index, ++ uint64_t *new_reftable_size, ++ void *new_refblock, int new_refblock_size, ++ int new_refcount_bits, ++ RefblockFinishOp *operation, bool *allocated, ++ Qcow2SetRefcountFunc *new_set_refcount, ++ BlockDriverAmendStatusCB *status_cb, ++ void *cb_opaque, int index, int total, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t reftable_index; ++ bool new_refblock_empty = true; ++ int refblock_index; ++ int new_refblock_index = 0; ++ int ret; ++ ++ for (reftable_index = 0; reftable_index < s->refcount_table_size; ++ reftable_index++) ++ { ++ uint64_t refblock_offset = s->refcount_table[reftable_index] ++ & REFT_OFFSET_MASK; ++ ++ status_cb(bs, (uint64_t)index * s->refcount_table_size + reftable_index, ++ (uint64_t)total * s->refcount_table_size, cb_opaque); ++ ++ if (refblock_offset) { ++ void *refblock; ++ ++ if (offset_into_cluster(s, refblock_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" ++ PRIx64 " unaligned (reftable index: %#" ++ PRIx64 ")", refblock_offset, ++ reftable_index); ++ error_setg(errp, ++ "Image is corrupt (unaligned refblock offset)"); ++ return -EIO; ++ } ++ ++ ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offset, ++ &refblock); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to retrieve refblock"); ++ return ret; ++ } ++ ++ for (refblock_index = 0; refblock_index < s->refcount_block_size; ++ refblock_index++) ++ { ++ uint64_t refcount; ++ ++ if (new_refblock_index >= new_refblock_size) { ++ /* new_refblock is now complete */ ++ ret = operation(bs, new_reftable, *new_reftable_index, ++ new_reftable_size, new_refblock, ++ new_refblock_empty, allocated, errp); ++ if (ret < 0) { ++ qcow2_cache_put(s->refcount_block_cache, &refblock); ++ return ret; ++ } ++ ++ (*new_reftable_index)++; ++ new_refblock_index = 0; ++ new_refblock_empty = true; ++ } ++ ++ refcount = s->get_refcount(refblock, refblock_index); ++ if (new_refcount_bits < 64 && refcount >> new_refcount_bits) { ++ uint64_t offset; ++ ++ qcow2_cache_put(s->refcount_block_cache, &refblock); ++ ++ offset = ((reftable_index << s->refcount_block_bits) ++ + refblock_index) << s->cluster_bits; ++ ++ error_setg(errp, "Cannot decrease refcount entry width to " ++ "%i bits: Cluster at offset %#" PRIx64 " has a " ++ "refcount of %" PRIu64, new_refcount_bits, ++ offset, refcount); ++ return -EINVAL; ++ } ++ ++ if (new_set_refcount) { ++ new_set_refcount(new_refblock, new_refblock_index++, ++ refcount); ++ } else { ++ new_refblock_index++; ++ } ++ new_refblock_empty = new_refblock_empty && refcount == 0; ++ } ++ ++ qcow2_cache_put(s->refcount_block_cache, &refblock); ++ } else { ++ /* No refblock means every refcount is 0 */ ++ for (refblock_index = 0; refblock_index < s->refcount_block_size; ++ refblock_index++) ++ { ++ if (new_refblock_index >= new_refblock_size) { ++ /* new_refblock is now complete */ ++ ret = operation(bs, new_reftable, *new_reftable_index, ++ new_reftable_size, new_refblock, ++ new_refblock_empty, allocated, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ (*new_reftable_index)++; ++ new_refblock_index = 0; ++ new_refblock_empty = true; ++ } ++ ++ if (new_set_refcount) { ++ new_set_refcount(new_refblock, new_refblock_index++, 0); ++ } else { ++ new_refblock_index++; ++ } ++ } ++ } ++ } ++ ++ if (new_refblock_index > 0) { ++ /* Complete the potentially existing partially filled final refblock */ ++ if (new_set_refcount) { ++ for (; new_refblock_index < new_refblock_size; ++ new_refblock_index++) ++ { ++ new_set_refcount(new_refblock, new_refblock_index, 0); ++ } ++ } ++ ++ ret = operation(bs, new_reftable, *new_reftable_index, ++ new_reftable_size, new_refblock, new_refblock_empty, ++ allocated, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ (*new_reftable_index)++; ++ } ++ ++ status_cb(bs, (uint64_t)(index + 1) * s->refcount_table_size, ++ (uint64_t)total * s->refcount_table_size, cb_opaque); ++ ++ return 0; ++} ++ ++int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, ++ BlockDriverAmendStatusCB *status_cb, ++ void *cb_opaque, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2GetRefcountFunc *new_get_refcount; ++ Qcow2SetRefcountFunc *new_set_refcount; ++ void *new_refblock = qemu_blockalign(bs->file->bs, s->cluster_size); ++ uint64_t *new_reftable = NULL, new_reftable_size = 0; ++ uint64_t *old_reftable, old_reftable_size, old_reftable_offset; ++ uint64_t new_reftable_index = 0; ++ uint64_t i; ++ int64_t new_reftable_offset = 0, allocated_reftable_size = 0; ++ int new_refblock_size, new_refcount_bits = 1 << refcount_order; ++ int old_refcount_order; ++ int walk_index = 0; ++ int ret; ++ bool new_allocation; ++ ++ assert(s->qcow_version >= 3); ++ assert(refcount_order >= 0 && refcount_order <= 6); ++ ++ /* see qcow2_open() */ ++ new_refblock_size = 1 << (s->cluster_bits - (refcount_order - 3)); ++ ++ new_get_refcount = get_refcount_funcs[refcount_order]; ++ new_set_refcount = set_refcount_funcs[refcount_order]; ++ ++ ++ do { ++ int total_walks; ++ ++ new_allocation = false; ++ ++ /* At least we have to do this walk and the one which writes the ++ * refblocks; also, at least we have to do this loop here at least ++ * twice (normally), first to do the allocations, and second to ++ * determine that everything is correctly allocated, this then makes ++ * three walks in total */ ++ total_walks = MAX(walk_index + 2, 3); ++ ++ /* First, allocate the structures so they are present in the refcount ++ * structures */ ++ ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index, ++ &new_reftable_size, NULL, new_refblock_size, ++ new_refcount_bits, &alloc_refblock, ++ &new_allocation, NULL, status_cb, cb_opaque, ++ walk_index++, total_walks, errp); ++ if (ret < 0) { ++ goto done; ++ } ++ ++ new_reftable_index = 0; ++ ++ if (new_allocation) { ++ if (new_reftable_offset) { ++ qcow2_free_clusters( ++ bs, new_reftable_offset, ++ allocated_reftable_size * REFTABLE_ENTRY_SIZE, ++ QCOW2_DISCARD_NEVER); ++ } ++ ++ new_reftable_offset = qcow2_alloc_clusters(bs, new_reftable_size * ++ REFTABLE_ENTRY_SIZE); ++ if (new_reftable_offset < 0) { ++ error_setg_errno(errp, -new_reftable_offset, ++ "Failed to allocate the new reftable"); ++ ret = new_reftable_offset; ++ goto done; ++ } ++ allocated_reftable_size = new_reftable_size; ++ } ++ } while (new_allocation); ++ ++ /* Second, write the new refblocks */ ++ ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index, ++ &new_reftable_size, new_refblock, ++ new_refblock_size, new_refcount_bits, ++ &flush_refblock, &new_allocation, new_set_refcount, ++ status_cb, cb_opaque, walk_index, walk_index + 1, ++ errp); ++ if (ret < 0) { ++ goto done; ++ } ++ assert(!new_allocation); ++ ++ ++ /* Write the new reftable */ ++ ret = qcow2_pre_write_overlap_check(bs, 0, new_reftable_offset, ++ new_reftable_size * REFTABLE_ENTRY_SIZE, ++ false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Overlap check failed"); ++ goto done; ++ } ++ ++ for (i = 0; i < new_reftable_size; i++) { ++ cpu_to_be64s(&new_reftable[i]); ++ } ++ ++ ret = bdrv_pwrite(bs->file, new_reftable_offset, ++ new_reftable_size * REFTABLE_ENTRY_SIZE, new_reftable, ++ 0); ++ ++ for (i = 0; i < new_reftable_size; i++) { ++ be64_to_cpus(&new_reftable[i]); ++ } ++ ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to write the new reftable"); ++ goto done; ++ } ++ ++ ++ /* Empty the refcount cache */ ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to flush the refblock cache"); ++ goto done; ++ } ++ ++ /* Update the image header to point to the new reftable; this only updates ++ * the fields which are relevant to qcow2_update_header(); other fields ++ * such as s->refcount_table or s->refcount_bits stay stale for now ++ * (because we have to restore everything if qcow2_update_header() fails) */ ++ old_refcount_order = s->refcount_order; ++ old_reftable_size = s->refcount_table_size; ++ old_reftable_offset = s->refcount_table_offset; ++ ++ s->refcount_order = refcount_order; ++ s->refcount_table_size = new_reftable_size; ++ s->refcount_table_offset = new_reftable_offset; ++ ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ s->refcount_order = old_refcount_order; ++ s->refcount_table_size = old_reftable_size; ++ s->refcount_table_offset = old_reftable_offset; ++ error_setg_errno(errp, -ret, "Failed to update the qcow2 header"); ++ goto done; ++ } ++ ++ /* Now update the rest of the in-memory information */ ++ old_reftable = s->refcount_table; ++ s->refcount_table = new_reftable; ++ update_max_refcount_table_index(s); ++ ++ s->refcount_bits = 1 << refcount_order; ++ s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1); ++ s->refcount_max += s->refcount_max - 1; ++ ++ s->refcount_block_bits = s->cluster_bits - (refcount_order - 3); ++ s->refcount_block_size = 1 << s->refcount_block_bits; ++ ++ s->get_refcount = new_get_refcount; ++ s->set_refcount = new_set_refcount; ++ ++ /* For cleaning up all old refblocks and the old reftable below the "done" ++ * label */ ++ new_reftable = old_reftable; ++ new_reftable_size = old_reftable_size; ++ new_reftable_offset = old_reftable_offset; ++ ++done: ++ if (new_reftable) { ++ /* On success, new_reftable actually points to the old reftable (and ++ * new_reftable_size is the old reftable's size); but that is just ++ * fine */ ++ for (i = 0; i < new_reftable_size; i++) { ++ uint64_t offset = new_reftable[i] & REFT_OFFSET_MASK; ++ if (offset) { ++ qcow2_free_clusters(bs, offset, s->cluster_size, ++ QCOW2_DISCARD_OTHER); ++ } ++ } ++ g_free(new_reftable); ++ ++ if (new_reftable_offset > 0) { ++ qcow2_free_clusters(bs, new_reftable_offset, ++ new_reftable_size * REFTABLE_ENTRY_SIZE, ++ QCOW2_DISCARD_OTHER); ++ } ++ } ++ ++ qemu_vfree(new_refblock); ++ return ret; ++} ++ ++static int64_t coroutine_fn GRAPH_RDLOCK ++get_refblock_offset(BlockDriverState *bs, uint64_t offset) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint32_t index = offset_to_reftable_index(s, offset); ++ int64_t covering_refblock_offset = 0; ++ ++ if (index < s->refcount_table_size) { ++ covering_refblock_offset = s->refcount_table[index] & REFT_OFFSET_MASK; ++ } ++ if (!covering_refblock_offset) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Refblock at %#" PRIx64 " is " ++ "not covered by the refcount structures", ++ offset); ++ return -EIO; ++ } ++ ++ return covering_refblock_offset; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_discard_refcount_block(BlockDriverState *bs, uint64_t discard_block_offs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t refblock_offs; ++ uint64_t cluster_index = discard_block_offs >> s->cluster_bits; ++ uint32_t block_index = cluster_index & (s->refcount_block_size - 1); ++ void *refblock; ++ int ret; ++ ++ refblock_offs = get_refblock_offset(bs, discard_block_offs); ++ if (refblock_offs < 0) { ++ return refblock_offs; ++ } ++ ++ assert(discard_block_offs != 0); ++ ++ ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offs, ++ &refblock); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (s->get_refcount(refblock, block_index) != 1) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Invalid refcount:" ++ " refblock offset %#" PRIx64 ++ ", reftable index %u" ++ ", block offset %#" PRIx64 ++ ", refcount %#" PRIx64, ++ refblock_offs, ++ offset_to_reftable_index(s, discard_block_offs), ++ discard_block_offs, ++ s->get_refcount(refblock, block_index)); ++ qcow2_cache_put(s->refcount_block_cache, &refblock); ++ return -EINVAL; ++ } ++ s->set_refcount(refblock, block_index, 0); ++ ++ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refblock); ++ ++ qcow2_cache_put(s->refcount_block_cache, &refblock); ++ ++ if (cluster_index < s->free_cluster_index) { ++ s->free_cluster_index = cluster_index; ++ } ++ ++ refblock = qcow2_cache_is_table_offset(s->refcount_block_cache, ++ discard_block_offs); ++ if (refblock) { ++ /* discard refblock from the cache if refblock is cached */ ++ qcow2_cache_discard(s->refcount_block_cache, refblock); ++ } ++ update_refcount_discard(bs, discard_block_offs, s->cluster_size); ++ ++ return 0; ++} ++ ++int coroutine_fn qcow2_shrink_reftable(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *reftable_tmp = ++ g_malloc(s->refcount_table_size * REFTABLE_ENTRY_SIZE); ++ int i, ret; ++ ++ for (i = 0; i < s->refcount_table_size; i++) { ++ int64_t refblock_offs = s->refcount_table[i] & REFT_OFFSET_MASK; ++ void *refblock; ++ bool unused_block; ++ ++ if (refblock_offs == 0) { ++ reftable_tmp[i] = 0; ++ continue; ++ } ++ ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offs, ++ &refblock); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ /* the refblock has own reference */ ++ if (i == offset_to_reftable_index(s, refblock_offs)) { ++ uint64_t block_index = (refblock_offs >> s->cluster_bits) & ++ (s->refcount_block_size - 1); ++ uint64_t refcount = s->get_refcount(refblock, block_index); ++ ++ s->set_refcount(refblock, block_index, 0); ++ ++ unused_block = buffer_is_zero(refblock, s->cluster_size); ++ ++ s->set_refcount(refblock, block_index, refcount); ++ } else { ++ unused_block = buffer_is_zero(refblock, s->cluster_size); ++ } ++ qcow2_cache_put(s->refcount_block_cache, &refblock); ++ ++ reftable_tmp[i] = unused_block ? 0 : cpu_to_be64(s->refcount_table[i]); ++ } ++ ++ ret = bdrv_co_pwrite_sync(bs->file, s->refcount_table_offset, ++ s->refcount_table_size * REFTABLE_ENTRY_SIZE, ++ reftable_tmp, 0); ++ /* ++ * If the write in the reftable failed the image may contain a partially ++ * overwritten reftable. In this case it would be better to clear the ++ * reftable in memory to avoid possible image corruption. ++ */ ++ for (i = 0; i < s->refcount_table_size; i++) { ++ if (s->refcount_table[i] && !reftable_tmp[i]) { ++ if (ret == 0) { ++ ret = qcow2_discard_refcount_block(bs, s->refcount_table[i] & ++ REFT_OFFSET_MASK); ++ } ++ s->refcount_table[i] = 0; ++ } ++ } ++ ++ if (!s->cache_discards) { ++ qcow2_process_discards(bs, ret); ++ } ++ ++out: ++ g_free(reftable_tmp); ++ return ret; ++} ++ ++int64_t coroutine_fn qcow2_get_last_cluster(BlockDriverState *bs, int64_t size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t i; ++ ++ for (i = size_to_clusters(s, size) - 1; i >= 0; i--) { ++ uint64_t refcount; ++ int ret = qcow2_get_refcount(bs, i, &refcount); ++ if (ret < 0) { ++ fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n", ++ i, strerror(-ret)); ++ return ret; ++ } ++ if (refcount > 0) { ++ return i; ++ } ++ } ++ qcow2_signal_corruption(bs, true, -1, -1, ++ "There are no references in the refcount table."); ++ return -EIO; ++} ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_detect_metadata_preallocation(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t i, end_cluster, cluster_count = 0, threshold; ++ int64_t file_length, real_allocation, real_clusters; ++ ++ qemu_co_mutex_assert_locked(&s->lock); ++ ++ file_length = bdrv_co_getlength(bs->file->bs); ++ if (file_length < 0) { ++ return file_length; ++ } ++ ++ real_allocation = bdrv_co_get_allocated_file_size(bs->file->bs); ++ if (real_allocation < 0) { ++ return real_allocation; ++ } ++ ++ real_clusters = real_allocation / s->cluster_size; ++ threshold = MAX(real_clusters * 10 / 9, real_clusters + 2); ++ ++ end_cluster = size_to_clusters(s, file_length); ++ for (i = 0; i < end_cluster && cluster_count < threshold; i++) { ++ uint64_t refcount; ++ int ret = qcow2_get_refcount(bs, i, &refcount); ++ if (ret < 0) { ++ return ret; ++ } ++ cluster_count += !!refcount; ++ } ++ ++ return cluster_count >= threshold; ++} +diff --git a/qcow2/lib/qcow2-snapshot.c b/qcow2/lib/qcow2-snapshot.c +new file mode 100644 +index 00000000..92e47978 +--- /dev/null ++++ b/qcow2/lib/qcow2-snapshot.c +@@ -0,0 +1,1076 @@ ++/* ++ * Block driver for the QCOW version 2 format ++ * ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "sysemu/block-backend.h" ++#include "qapi/error.h" ++#include "qcow2.h" ++#include "qemu/bswap.h" ++#include "qemu/error-report.h" ++#include "qemu/cutils.h" ++#include "qemu/memalign.h" ++ ++static void qcow2_free_single_snapshot(BlockDriverState *bs, int i) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ assert(i >= 0 && i < s->nb_snapshots); ++ g_free(s->snapshots[i].name); ++ g_free(s->snapshots[i].id_str); ++ g_free(s->snapshots[i].unknown_extra_data); ++ memset(&s->snapshots[i], 0, sizeof(s->snapshots[i])); ++} ++ ++void qcow2_free_snapshots(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i; ++ ++ for(i = 0; i < s->nb_snapshots; i++) { ++ qcow2_free_single_snapshot(bs, i); ++ } ++ g_free(s->snapshots); ++ s->snapshots = NULL; ++ s->nb_snapshots = 0; ++} ++ ++/* ++ * If @repair is true, try to repair a broken snapshot table instead ++ * of just returning an error: ++ * ++ * - If the snapshot table was too long, set *nb_clusters_reduced to ++ * the number of snapshots removed off the end. ++ * The caller will update the on-disk nb_snapshots accordingly; ++ * this leaks clusters, but is safe. ++ * (The on-disk information must be updated before ++ * qcow2_check_refcounts(), because that function relies on ++ * s->nb_snapshots to reflect the on-disk value.) ++ * ++ * - If there were snapshots with too much extra metadata, increment ++ * *extra_data_dropped for each. ++ * This requires the caller to eventually rewrite the whole snapshot ++ * table, which requires cluster allocation. Therefore, this should ++ * be done only after qcow2_check_refcounts() made sure the refcount ++ * structures are valid. ++ * (In the meantime, the image is still valid because ++ * qcow2_check_refcounts() does not do anything with snapshots' ++ * extra data.) ++ */ ++static coroutine_fn GRAPH_RDLOCK ++int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, ++ int *nb_clusters_reduced, ++ int *extra_data_dropped, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshotHeader h; ++ QCowSnapshotExtraData extra; ++ QCowSnapshot *sn; ++ int i, id_str_size, name_size; ++ int64_t offset, pre_sn_offset; ++ uint64_t table_length = 0; ++ int ret; ++ ++ if (!s->nb_snapshots) { ++ s->snapshots = NULL; ++ s->snapshots_size = 0; ++ return 0; ++ } ++ ++ offset = s->snapshots_offset; ++ s->snapshots = g_new0(QCowSnapshot, s->nb_snapshots); ++ ++ for(i = 0; i < s->nb_snapshots; i++) { ++ bool truncate_unknown_extra_data = false; ++ ++ pre_sn_offset = offset; ++ table_length = ROUND_UP(table_length, 8); ++ ++ /* Read statically sized part of the snapshot header */ ++ offset = ROUND_UP(offset, 8); ++ ret = bdrv_co_pread(bs->file, offset, sizeof(h), &h, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to read snapshot table"); ++ goto fail; ++ } ++ ++ offset += sizeof(h); ++ sn = s->snapshots + i; ++ sn->l1_table_offset = be64_to_cpu(h.l1_table_offset); ++ sn->l1_size = be32_to_cpu(h.l1_size); ++ sn->vm_state_size = be32_to_cpu(h.vm_state_size); ++ sn->date_sec = be32_to_cpu(h.date_sec); ++ sn->date_nsec = be32_to_cpu(h.date_nsec); ++ sn->vm_clock_nsec = be64_to_cpu(h.vm_clock_nsec); ++ sn->extra_data_size = be32_to_cpu(h.extra_data_size); ++ ++ id_str_size = be16_to_cpu(h.id_str_size); ++ name_size = be16_to_cpu(h.name_size); ++ ++ if (sn->extra_data_size > QCOW_MAX_SNAPSHOT_EXTRA_DATA) { ++ if (!repair) { ++ ret = -EFBIG; ++ error_setg(errp, "Too much extra metadata in snapshot table " ++ "entry %i", i); ++ error_append_hint(errp, "You can force-remove this extra " ++ "metadata with qemu-img check -r all\n"); ++ goto fail; ++ } ++ ++ fprintf(stderr, "Discarding too much extra metadata in snapshot " ++ "table entry %i (%" PRIu32 " > %u)\n", ++ i, sn->extra_data_size, QCOW_MAX_SNAPSHOT_EXTRA_DATA); ++ ++ (*extra_data_dropped)++; ++ truncate_unknown_extra_data = true; ++ } ++ ++ /* Read known extra data */ ++ ret = bdrv_co_pread(bs->file, offset, ++ MIN(sizeof(extra), sn->extra_data_size), &extra, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to read snapshot table"); ++ goto fail; ++ } ++ offset += MIN(sizeof(extra), sn->extra_data_size); ++ ++ if (sn->extra_data_size >= endof(QCowSnapshotExtraData, ++ vm_state_size_large)) { ++ sn->vm_state_size = be64_to_cpu(extra.vm_state_size_large); ++ } ++ ++ if (sn->extra_data_size >= endof(QCowSnapshotExtraData, disk_size)) { ++ sn->disk_size = be64_to_cpu(extra.disk_size); ++ } else { ++ sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; ++ } ++ ++ if (sn->extra_data_size >= endof(QCowSnapshotExtraData, icount)) { ++ sn->icount = be64_to_cpu(extra.icount); ++ } else { ++ sn->icount = -1ULL; ++ } ++ ++ if (sn->extra_data_size > sizeof(extra)) { ++ uint64_t extra_data_end; ++ size_t unknown_extra_data_size; ++ ++ extra_data_end = offset + sn->extra_data_size - sizeof(extra); ++ ++ if (truncate_unknown_extra_data) { ++ sn->extra_data_size = QCOW_MAX_SNAPSHOT_EXTRA_DATA; ++ } ++ ++ /* Store unknown extra data */ ++ unknown_extra_data_size = sn->extra_data_size - sizeof(extra); ++ sn->unknown_extra_data = g_malloc(unknown_extra_data_size); ++ ret = bdrv_co_pread(bs->file, offset, unknown_extra_data_size, ++ sn->unknown_extra_data, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to read snapshot table"); ++ goto fail; ++ } ++ offset = extra_data_end; ++ } ++ ++ /* Read snapshot ID */ ++ sn->id_str = g_malloc(id_str_size + 1); ++ ret = bdrv_co_pread(bs->file, offset, id_str_size, sn->id_str, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to read snapshot table"); ++ goto fail; ++ } ++ offset += id_str_size; ++ sn->id_str[id_str_size] = '\0'; ++ ++ /* Read snapshot name */ ++ sn->name = g_malloc(name_size + 1); ++ ret = bdrv_co_pread(bs->file, offset, name_size, sn->name, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to read snapshot table"); ++ goto fail; ++ } ++ offset += name_size; ++ sn->name[name_size] = '\0'; ++ ++ /* Note that the extra data may have been truncated */ ++ table_length += sizeof(h) + sn->extra_data_size + id_str_size + ++ name_size; ++ if (!repair) { ++ assert(table_length == offset - s->snapshots_offset); ++ } ++ ++ if (table_length > QCOW_MAX_SNAPSHOTS_SIZE || ++ offset - s->snapshots_offset > INT_MAX) ++ { ++ if (!repair) { ++ ret = -EFBIG; ++ error_setg(errp, "Snapshot table is too big"); ++ error_append_hint(errp, "You can force-remove all %u " ++ "overhanging snapshots with qemu-img check " ++ "-r all\n", s->nb_snapshots - i); ++ goto fail; ++ } ++ ++ fprintf(stderr, "Discarding %u overhanging snapshots (snapshot " ++ "table is too big)\n", s->nb_snapshots - i); ++ ++ *nb_clusters_reduced += (s->nb_snapshots - i); ++ ++ /* Discard current snapshot also */ ++ qcow2_free_single_snapshot(bs, i); ++ ++ /* ++ * This leaks all the rest of the snapshot table and the ++ * snapshots' clusters, but we run in check -r all mode, ++ * so qcow2_check_refcounts() will take care of it. ++ */ ++ s->nb_snapshots = i; ++ offset = pre_sn_offset; ++ break; ++ } ++ } ++ ++ assert(offset - s->snapshots_offset <= INT_MAX); ++ s->snapshots_size = offset - s->snapshots_offset; ++ return 0; ++ ++fail: ++ qcow2_free_snapshots(bs); ++ return ret; ++} ++ ++int coroutine_fn qcow2_read_snapshots(BlockDriverState *bs, Error **errp) ++{ ++ return qcow2_do_read_snapshots(bs, false, NULL, NULL, errp); ++} ++ ++/* add at the end of the file a new list of snapshots */ ++int qcow2_write_snapshots(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshot *sn; ++ QCowSnapshotHeader h; ++ QCowSnapshotExtraData extra; ++ int i, name_size, id_str_size, snapshots_size; ++ struct { ++ uint32_t nb_snapshots; ++ uint64_t snapshots_offset; ++ } QEMU_PACKED header_data; ++ int64_t offset, snapshots_offset = 0; ++ int ret; ++ ++ /* compute the size of the snapshots */ ++ offset = 0; ++ for(i = 0; i < s->nb_snapshots; i++) { ++ sn = s->snapshots + i; ++ offset = ROUND_UP(offset, 8); ++ offset += sizeof(h); ++ offset += MAX(sizeof(extra), sn->extra_data_size); ++ offset += strlen(sn->id_str); ++ offset += strlen(sn->name); ++ ++ if (offset > QCOW_MAX_SNAPSHOTS_SIZE) { ++ ret = -EFBIG; ++ goto fail; ++ } ++ } ++ ++ assert(offset <= INT_MAX); ++ snapshots_size = offset; ++ ++ /* Allocate space for the new snapshot list */ ++ snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size); ++ offset = snapshots_offset; ++ if (offset < 0) { ++ ret = offset; ++ goto fail; ++ } ++ ret = bdrv_flush(bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* The snapshot list position has not yet been updated, so these clusters ++ * must indeed be completely free */ ++ ret = qcow2_pre_write_overlap_check(bs, 0, offset, snapshots_size, false); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ++ /* Write all snapshots to the new list */ ++ for(i = 0; i < s->nb_snapshots; i++) { ++ sn = s->snapshots + i; ++ memset(&h, 0, sizeof(h)); ++ h.l1_table_offset = cpu_to_be64(sn->l1_table_offset); ++ h.l1_size = cpu_to_be32(sn->l1_size); ++ /* If it doesn't fit in 32 bit, older implementations should treat it ++ * as a disk-only snapshot rather than truncate the VM state */ ++ if (sn->vm_state_size <= 0xffffffff) { ++ h.vm_state_size = cpu_to_be32(sn->vm_state_size); ++ } ++ h.date_sec = cpu_to_be32(sn->date_sec); ++ h.date_nsec = cpu_to_be32(sn->date_nsec); ++ h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec); ++ h.extra_data_size = cpu_to_be32(MAX(sizeof(extra), ++ sn->extra_data_size)); ++ ++ memset(&extra, 0, sizeof(extra)); ++ extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size); ++ extra.disk_size = cpu_to_be64(sn->disk_size); ++ extra.icount = cpu_to_be64(sn->icount); ++ ++ id_str_size = strlen(sn->id_str); ++ name_size = strlen(sn->name); ++ assert(id_str_size <= UINT16_MAX && name_size <= UINT16_MAX); ++ h.id_str_size = cpu_to_be16(id_str_size); ++ h.name_size = cpu_to_be16(name_size); ++ offset = ROUND_UP(offset, 8); ++ ++ ret = bdrv_pwrite(bs->file, offset, sizeof(h), &h, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ offset += sizeof(h); ++ ++ ret = bdrv_pwrite(bs->file, offset, sizeof(extra), &extra, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ offset += sizeof(extra); ++ ++ if (sn->extra_data_size > sizeof(extra)) { ++ size_t unknown_extra_data_size = ++ sn->extra_data_size - sizeof(extra); ++ ++ /* qcow2_read_snapshots() ensures no unbounded allocation */ ++ assert(unknown_extra_data_size <= BDRV_REQUEST_MAX_BYTES); ++ assert(sn->unknown_extra_data); ++ ++ ret = bdrv_pwrite(bs->file, offset, unknown_extra_data_size, ++ sn->unknown_extra_data, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ offset += unknown_extra_data_size; ++ } ++ ++ ret = bdrv_pwrite(bs->file, offset, id_str_size, sn->id_str, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ offset += id_str_size; ++ ++ ret = bdrv_pwrite(bs->file, offset, name_size, sn->name, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ offset += name_size; ++ } ++ ++ /* ++ * Update the header to point to the new snapshot table. This requires the ++ * new table and its refcounts to be stable on disk. ++ */ ++ ret = bdrv_flush(bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ QEMU_BUILD_BUG_ON(offsetof(QCowHeader, snapshots_offset) != ++ endof(QCowHeader, nb_snapshots)); ++ ++ header_data.nb_snapshots = cpu_to_be32(s->nb_snapshots); ++ header_data.snapshots_offset = cpu_to_be64(snapshots_offset); ++ ++ ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), ++ sizeof(header_data), &header_data, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* free the old snapshot table */ ++ qcow2_free_clusters(bs, s->snapshots_offset, s->snapshots_size, ++ QCOW2_DISCARD_SNAPSHOT); ++ s->snapshots_offset = snapshots_offset; ++ s->snapshots_size = snapshots_size; ++ return 0; ++ ++fail: ++ if (snapshots_offset > 0) { ++ qcow2_free_clusters(bs, snapshots_offset, snapshots_size, ++ QCOW2_DISCARD_ALWAYS); ++ } ++ return ret; ++} ++ ++int coroutine_fn qcow2_check_read_snapshot_table(BlockDriverState *bs, ++ BdrvCheckResult *result, ++ BdrvCheckMode fix) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Error *local_err = NULL; ++ int nb_clusters_reduced = 0; ++ int extra_data_dropped = 0; ++ int ret; ++ struct { ++ uint32_t nb_snapshots; ++ uint64_t snapshots_offset; ++ } QEMU_PACKED snapshot_table_pointer; ++ ++ /* qcow2_do_open() discards this information in check mode */ ++ ret = bdrv_co_pread(bs->file, offsetof(QCowHeader, nb_snapshots), ++ sizeof(snapshot_table_pointer), &snapshot_table_pointer, ++ 0); ++ if (ret < 0) { ++ result->check_errors++; ++ fprintf(stderr, "ERROR failed to read the snapshot table pointer from " ++ "the image header: %s\n", strerror(-ret)); ++ return ret; ++ } ++ ++ s->snapshots_offset = be64_to_cpu(snapshot_table_pointer.snapshots_offset); ++ s->nb_snapshots = be32_to_cpu(snapshot_table_pointer.nb_snapshots); ++ ++ if (s->nb_snapshots > QCOW_MAX_SNAPSHOTS && (fix & BDRV_FIX_ERRORS)) { ++ fprintf(stderr, "Discarding %u overhanging snapshots\n", ++ s->nb_snapshots - QCOW_MAX_SNAPSHOTS); ++ ++ nb_clusters_reduced += s->nb_snapshots - QCOW_MAX_SNAPSHOTS; ++ s->nb_snapshots = QCOW_MAX_SNAPSHOTS; ++ } ++ ++ ret = qcow2_validate_table(bs, s->snapshots_offset, s->nb_snapshots, ++ sizeof(QCowSnapshotHeader), ++ sizeof(QCowSnapshotHeader) * QCOW_MAX_SNAPSHOTS, ++ "snapshot table", &local_err); ++ if (ret < 0) { ++ result->check_errors++; ++ error_reportf_err(local_err, "ERROR "); ++ ++ if (s->nb_snapshots > QCOW_MAX_SNAPSHOTS) { ++ fprintf(stderr, "You can force-remove all %u overhanging snapshots " ++ "with qemu-img check -r all\n", ++ s->nb_snapshots - QCOW_MAX_SNAPSHOTS); ++ } ++ ++ /* We did not read the snapshot table, so invalidate this information */ ++ s->snapshots_offset = 0; ++ s->nb_snapshots = 0; ++ ++ return ret; ++ } ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ret = qcow2_do_read_snapshots(bs, fix & BDRV_FIX_ERRORS, ++ &nb_clusters_reduced, &extra_data_dropped, ++ &local_err); ++ qemu_co_mutex_lock(&s->lock); ++ if (ret < 0) { ++ result->check_errors++; ++ error_reportf_err(local_err, ++ "ERROR failed to read the snapshot table: "); ++ ++ /* We did not read the snapshot table, so invalidate this information */ ++ s->snapshots_offset = 0; ++ s->nb_snapshots = 0; ++ ++ return ret; ++ } ++ result->corruptions += nb_clusters_reduced + extra_data_dropped; ++ ++ if (nb_clusters_reduced) { ++ /* ++ * Update image header now, because: ++ * (1) qcow2_check_refcounts() relies on s->nb_snapshots to be ++ * the same as what the image header says, ++ * (2) this leaks clusters, but qcow2_check_refcounts() will ++ * fix that. ++ */ ++ assert(fix & BDRV_FIX_ERRORS); ++ ++ snapshot_table_pointer.nb_snapshots = cpu_to_be32(s->nb_snapshots); ++ ret = bdrv_co_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), ++ sizeof(snapshot_table_pointer.nb_snapshots), ++ &snapshot_table_pointer.nb_snapshots, 0); ++ if (ret < 0) { ++ result->check_errors++; ++ fprintf(stderr, "ERROR failed to update the snapshot count in the " ++ "image header: %s\n", strerror(-ret)); ++ return ret; ++ } ++ ++ result->corruptions_fixed += nb_clusters_reduced; ++ result->corruptions -= nb_clusters_reduced; ++ } ++ ++ /* ++ * All of v3 images' snapshot table entries need to have at least ++ * 16 bytes of extra data. ++ */ ++ if (s->qcow_version >= 3) { ++ int i; ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (s->snapshots[i].extra_data_size < ++ sizeof_field(QCowSnapshotExtraData, vm_state_size_large) + ++ sizeof_field(QCowSnapshotExtraData, disk_size)) ++ { ++ result->corruptions++; ++ fprintf(stderr, "%s snapshot table entry %i is incomplete\n", ++ fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++int coroutine_fn qcow2_check_fix_snapshot_table(BlockDriverState *bs, ++ BdrvCheckResult *result, ++ BdrvCheckMode fix) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ if (result->corruptions && (fix & BDRV_FIX_ERRORS)) { ++ qemu_co_mutex_unlock(&s->lock); ++ ret = qcow2_write_snapshots(bs); ++ qemu_co_mutex_lock(&s->lock); ++ if (ret < 0) { ++ result->check_errors++; ++ fprintf(stderr, "ERROR failed to update snapshot table: %s\n", ++ strerror(-ret)); ++ return ret; ++ } ++ ++ result->corruptions_fixed += result->corruptions; ++ result->corruptions = 0; ++ } ++ ++ return 0; ++} ++ ++static void find_new_snapshot_id(BlockDriverState *bs, ++ char *id_str, int id_str_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshot *sn; ++ int i; ++ unsigned long id, id_max = 0; ++ ++ for(i = 0; i < s->nb_snapshots; i++) { ++ sn = s->snapshots + i; ++ id = strtoul(sn->id_str, NULL, 10); ++ if (id > id_max) ++ id_max = id; ++ } ++ snprintf(id_str, id_str_size, "%lu", id_max + 1); ++} ++ ++static int find_snapshot_by_id_and_name(BlockDriverState *bs, ++ const char *id, ++ const char *name) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i; ++ ++ if (id && name) { ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (!strcmp(s->snapshots[i].id_str, id) && ++ !strcmp(s->snapshots[i].name, name)) { ++ return i; ++ } ++ } ++ } else if (id) { ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (!strcmp(s->snapshots[i].id_str, id)) { ++ return i; ++ } ++ } ++ } else if (name) { ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (!strcmp(s->snapshots[i].name, name)) { ++ return i; ++ } ++ } ++ } ++ ++ return -1; ++} ++ ++static int find_snapshot_by_id_or_name(BlockDriverState *bs, ++ const char *id_or_name) ++{ ++ int ret; ++ ++ ret = find_snapshot_by_id_and_name(bs, id_or_name, NULL); ++ if (ret >= 0) { ++ return ret; ++ } ++ return find_snapshot_by_id_and_name(bs, NULL, id_or_name); ++} ++ ++/* if no id is provided, a new one is constructed */ ++int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshot *new_snapshot_list = NULL; ++ QCowSnapshot *old_snapshot_list = NULL; ++ QCowSnapshot sn1, *sn = &sn1; ++ int i, ret; ++ uint64_t *l1_table = NULL; ++ int64_t l1_table_offset; ++ ++ if (s->nb_snapshots >= QCOW_MAX_SNAPSHOTS) { ++ return -EFBIG; ++ } ++ ++ if (has_data_file(bs)) { ++ return -ENOTSUP; ++ } ++ ++ memset(sn, 0, sizeof(*sn)); ++ ++ /* Generate an ID */ ++ find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str)); ++ ++ /* Populate sn with passed data */ ++ sn->id_str = g_strdup(sn_info->id_str); ++ sn->name = g_strdup(sn_info->name); ++ ++ sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; ++ sn->vm_state_size = sn_info->vm_state_size; ++ sn->date_sec = sn_info->date_sec; ++ sn->date_nsec = sn_info->date_nsec; ++ sn->vm_clock_nsec = sn_info->vm_clock_nsec; ++ sn->icount = sn_info->icount; ++ sn->extra_data_size = sizeof(QCowSnapshotExtraData); ++ ++ /* Allocate the L1 table of the snapshot and copy the current one there. */ ++ l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * L1E_SIZE); ++ if (l1_table_offset < 0) { ++ ret = l1_table_offset; ++ goto fail; ++ } ++ ++ sn->l1_table_offset = l1_table_offset; ++ sn->l1_size = s->l1_size; ++ ++ l1_table = g_try_new(uint64_t, s->l1_size); ++ if (s->l1_size && l1_table == NULL) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ for(i = 0; i < s->l1_size; i++) { ++ l1_table[i] = cpu_to_be64(s->l1_table[i]); ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, sn->l1_table_offset, ++ s->l1_size * L1E_SIZE, false); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = bdrv_pwrite(bs->file, sn->l1_table_offset, s->l1_size * L1E_SIZE, ++ l1_table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ g_free(l1_table); ++ l1_table = NULL; ++ ++ /* ++ * Increase the refcounts of all clusters and make sure everything is ++ * stable on disk before updating the snapshot table to contain a pointer ++ * to the new L1 table. ++ */ ++ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Append the new snapshot to the snapshot list */ ++ new_snapshot_list = g_new(QCowSnapshot, s->nb_snapshots + 1); ++ if (s->snapshots) { ++ memcpy(new_snapshot_list, s->snapshots, ++ s->nb_snapshots * sizeof(QCowSnapshot)); ++ old_snapshot_list = s->snapshots; ++ } ++ s->snapshots = new_snapshot_list; ++ s->snapshots[s->nb_snapshots++] = *sn; ++ ++ ret = qcow2_write_snapshots(bs); ++ if (ret < 0) { ++ g_free(s->snapshots); ++ s->snapshots = old_snapshot_list; ++ s->nb_snapshots--; ++ goto fail; ++ } ++ ++ g_free(old_snapshot_list); ++ ++ /* The VM state isn't needed any more in the active L1 table; in fact, it ++ * hurts by causing expensive COW for the next snapshot. */ ++ qcow2_cluster_discard(bs, qcow2_vm_state_offset(s), ++ ROUND_UP(sn->vm_state_size, s->cluster_size), ++ QCOW2_DISCARD_NEVER, false); ++ ++#ifdef DEBUG_ALLOC ++ { ++ BdrvCheckResult result = {0}; ++ qcow2_check_refcounts(bs, &result, 0); ++ } ++#endif ++ return 0; ++ ++fail: ++ g_free(sn->id_str); ++ g_free(sn->name); ++ g_free(l1_table); ++ ++ return ret; ++} ++ ++/* copy the snapshot 'snapshot_name' into the current disk image */ ++int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshot *sn; ++ Error *local_err = NULL; ++ int i, snapshot_index; ++ int cur_l1_bytes, sn_l1_bytes; ++ int ret; ++ uint64_t *sn_l1_table = NULL; ++ ++ if (has_data_file(bs)) { ++ return -ENOTSUP; ++ } ++ ++ /* Search the snapshot */ ++ snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id); ++ if (snapshot_index < 0) { ++ return -ENOENT; ++ } ++ sn = &s->snapshots[snapshot_index]; ++ ++ ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size, ++ L1E_SIZE, QCOW_MAX_L1_SIZE, ++ "Snapshot L1 table", &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto fail; ++ } ++ ++ if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) { ++ BlockBackend *blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, ++ &local_err); ++ if (!blk) { ++ error_report_err(local_err); ++ ret = -ENOTSUP; ++ goto fail; ++ } ++ ++ ret = blk_truncate(blk, sn->disk_size, true, PREALLOC_MODE_OFF, 0, ++ &local_err); ++ blk_unref(blk); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto fail; ++ } ++ } ++ ++ /* ++ * Make sure that the current L1 table is big enough to contain the whole ++ * L1 table of the snapshot. If the snapshot L1 table is smaller, the ++ * current one must be padded with zeros. ++ */ ++ ret = qcow2_grow_l1_table(bs, sn->l1_size, true); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ cur_l1_bytes = s->l1_size * L1E_SIZE; ++ sn_l1_bytes = sn->l1_size * L1E_SIZE; ++ ++ /* ++ * Copy the snapshot L1 table to the current L1 table. ++ * ++ * Before overwriting the old current L1 table on disk, make sure to ++ * increase all refcounts for the clusters referenced by the new one. ++ * Decrease the refcount referenced by the old one only when the L1 ++ * table is overwritten. ++ */ ++ sn_l1_table = g_try_malloc0(cur_l1_bytes); ++ if (cur_l1_bytes && sn_l1_table == NULL) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ ret = bdrv_pread(bs->file, sn->l1_table_offset, sn_l1_bytes, sn_l1_table, ++ 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_update_snapshot_refcount(bs, sn->l1_table_offset, ++ sn->l1_size, 1); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1, ++ s->l1_table_offset, cur_l1_bytes, ++ false); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, cur_l1_bytes, ++ sn_l1_table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* ++ * Decrease refcount of clusters of current L1 table. ++ * ++ * At this point, the in-memory s->l1_table points to the old L1 table, ++ * whereas on disk we already have the new one. ++ * ++ * qcow2_update_snapshot_refcount special cases the current L1 table to use ++ * the in-memory data instead of really using the offset to load a new one, ++ * which is why this works. ++ */ ++ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, ++ s->l1_size, -1); ++ ++ /* ++ * Now update the in-memory L1 table to be in sync with the on-disk one. We ++ * need to do this even if updating refcounts failed. ++ */ ++ for(i = 0;i < s->l1_size; i++) { ++ s->l1_table[i] = be64_to_cpu(sn_l1_table[i]); ++ } ++ ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ g_free(sn_l1_table); ++ sn_l1_table = NULL; ++ ++ /* ++ * Update QCOW_OFLAG_COPIED in the active L1 table (it may have changed ++ * when we decreased the refcount of the old snapshot. ++ */ ++ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++#ifdef DEBUG_ALLOC ++ { ++ BdrvCheckResult result = {0}; ++ qcow2_check_refcounts(bs, &result, 0); ++ } ++#endif ++ return 0; ++ ++fail: ++ g_free(sn_l1_table); ++ return ret; ++} ++ ++int qcow2_snapshot_delete(BlockDriverState *bs, ++ const char *snapshot_id, ++ const char *name, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshot sn; ++ int snapshot_index, ret; ++ ++ if (has_data_file(bs)) { ++ return -ENOTSUP; ++ } ++ ++ /* Search the snapshot */ ++ snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name); ++ if (snapshot_index < 0) { ++ error_setg(errp, "Can't find the snapshot"); ++ return -ENOENT; ++ } ++ sn = s->snapshots[snapshot_index]; ++ ++ ret = qcow2_validate_table(bs, sn.l1_table_offset, sn.l1_size, ++ L1E_SIZE, QCOW_MAX_L1_SIZE, ++ "Snapshot L1 table", errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Remove it from the snapshot list */ ++ memmove(s->snapshots + snapshot_index, ++ s->snapshots + snapshot_index + 1, ++ (s->nb_snapshots - snapshot_index - 1) * sizeof(sn)); ++ s->nb_snapshots--; ++ ret = qcow2_write_snapshots(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to remove snapshot from snapshot list"); ++ return ret; ++ } ++ ++ /* ++ * The snapshot is now unused, clean up. If we fail after this point, we ++ * won't recover but just leak clusters. ++ */ ++ g_free(sn.unknown_extra_data); ++ g_free(sn.id_str); ++ g_free(sn.name); ++ ++ /* ++ * Now decrease the refcounts of clusters referenced by the snapshot and ++ * free the L1 table. ++ */ ++ ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset, ++ sn.l1_size, -1); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to free the cluster and L1 table"); ++ return ret; ++ } ++ qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * L1E_SIZE, ++ QCOW2_DISCARD_SNAPSHOT); ++ ++ /* must update the copied flag on the current cluster offsets */ ++ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to update snapshot status in disk"); ++ return ret; ++ } ++ ++#ifdef DEBUG_ALLOC ++ { ++ BdrvCheckResult result = {0}; ++ qcow2_check_refcounts(bs, &result, 0); ++ } ++#endif ++ return 0; ++} ++ ++int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QEMUSnapshotInfo *sn_tab, *sn_info; ++ QCowSnapshot *sn; ++ int i; ++ ++ if (has_data_file(bs)) { ++ return -ENOTSUP; ++ } ++ if (!s->nb_snapshots) { ++ *psn_tab = NULL; ++ return s->nb_snapshots; ++ } ++ ++ sn_tab = g_new0(QEMUSnapshotInfo, s->nb_snapshots); ++ for(i = 0; i < s->nb_snapshots; i++) { ++ sn_info = sn_tab + i; ++ sn = s->snapshots + i; ++ pstrcpy(sn_info->id_str, sizeof(sn_info->id_str), ++ sn->id_str); ++ pstrcpy(sn_info->name, sizeof(sn_info->name), ++ sn->name); ++ sn_info->vm_state_size = sn->vm_state_size; ++ sn_info->date_sec = sn->date_sec; ++ sn_info->date_nsec = sn->date_nsec; ++ sn_info->vm_clock_nsec = sn->vm_clock_nsec; ++ sn_info->icount = sn->icount; ++ } ++ *psn_tab = sn_tab; ++ return s->nb_snapshots; ++} ++ ++int qcow2_snapshot_load_tmp(BlockDriverState *bs, ++ const char *snapshot_id, ++ const char *name, ++ Error **errp) ++{ ++ int i, snapshot_index; ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshot *sn; ++ uint64_t *new_l1_table; ++ int new_l1_bytes; ++ int ret; ++ ++ assert(bdrv_is_read_only(bs)); ++ ++ /* Search the snapshot */ ++ snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name); ++ if (snapshot_index < 0) { ++ error_setg(errp, ++ "Can't find snapshot"); ++ return -ENOENT; ++ } ++ sn = &s->snapshots[snapshot_index]; ++ ++ /* Allocate and read in the snapshot's L1 table */ ++ ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size, ++ L1E_SIZE, QCOW_MAX_L1_SIZE, ++ "Snapshot L1 table", errp); ++ if (ret < 0) { ++ return ret; ++ } ++ new_l1_bytes = sn->l1_size * L1E_SIZE; ++ new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_bytes); ++ if (new_l1_table == NULL) { ++ return -ENOMEM; ++ } ++ ++ ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_bytes, ++ new_l1_table, 0); ++ if (ret < 0) { ++ error_setg(errp, "Failed to read l1 table for snapshot"); ++ qemu_vfree(new_l1_table); ++ return ret; ++ } ++ ++ /* Switch the L1 table */ ++ qemu_vfree(s->l1_table); ++ ++ s->l1_size = sn->l1_size; ++ s->l1_table_offset = sn->l1_table_offset; ++ s->l1_table = new_l1_table; ++ ++ for(i = 0;i < s->l1_size; i++) { ++ be64_to_cpus(&s->l1_table[i]); ++ } ++ ++ return 0; ++} +diff --git a/qcow2/lib/qcow2-threads.c b/qcow2/lib/qcow2-threads.c +new file mode 100644 +index 00000000..d6071a1e +--- /dev/null ++++ b/qcow2/lib/qcow2-threads.c +@@ -0,0 +1,527 @@ ++/* ++ * Threaded data processing for Qcow2: compression, encryption ++ * ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * Copyright (c) 2018 Virtuozzo International GmbH. All rights reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++ ++#define ZLIB_CONST ++#include ++ ++#ifdef CONFIG_ZSTD ++#include ++#include ++#endif ++ ++#include "qcow2.h" ++#include "block/block-io.h" ++#include "block/thread-pool.h" ++#include "crypto.h" ++ ++static int coroutine_fn ++qcow2_co_process(BlockDriverState *bs, ThreadPoolFunc *func, void *arg) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ ++ qemu_co_mutex_lock(&s->lock); ++ while (s->nb_threads >= QCOW2_MAX_THREADS) { ++ qemu_co_queue_wait(&s->thread_task_queue, &s->lock); ++ } ++ s->nb_threads++; ++ qemu_co_mutex_unlock(&s->lock); ++ ++ ret = thread_pool_submit_co(func, arg); ++ ++ qemu_co_mutex_lock(&s->lock); ++ s->nb_threads--; ++ qemu_co_queue_next(&s->thread_task_queue); ++ qemu_co_mutex_unlock(&s->lock); ++ ++ return ret; ++} ++ ++ ++/* ++ * Compression ++ */ ++ ++typedef ssize_t (*Qcow2CompressFunc)(void *dest, size_t dest_size, ++ const void *src, size_t src_size); ++typedef struct Qcow2CompressData { ++ void *dest; ++ size_t dest_size; ++ const void *src; ++ size_t src_size; ++ ssize_t ret; ++ ++ Qcow2CompressFunc func; ++} Qcow2CompressData; ++ ++/* ++ * qcow2_zlib_compress() ++ * ++ * Compress @src_size bytes of data using zlib compression method ++ * ++ * @dest - destination buffer, @dest_size bytes ++ * @src - source buffer, @src_size bytes ++ * ++ * Returns: compressed size on success ++ * -ENOMEM destination buffer is not enough to store compressed data ++ * -EIO on any other error ++ */ ++static ssize_t qcow2_zlib_compress(void *dest, size_t dest_size, ++ const void *src, size_t src_size) ++{ ++ ssize_t ret; ++ z_stream strm; ++ ++ /* best compression, small window, no zlib header */ ++ memset(&strm, 0, sizeof(strm)); ++ ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, ++ -12, 9, Z_DEFAULT_STRATEGY); ++ if (ret != Z_OK) { ++ return -EIO; ++ } ++ ++ /* ++ * strm.next_in is not const in old zlib versions, such as those used on ++ * OpenBSD/NetBSD, so cast the const away ++ */ ++ strm.avail_in = src_size; ++ strm.next_in = (void *) src; ++ strm.avail_out = dest_size; ++ strm.next_out = dest; ++ ++ ret = deflate(&strm, Z_FINISH); ++ if (ret == Z_STREAM_END) { ++ ret = dest_size - strm.avail_out; ++ } else { ++ ret = (ret == Z_OK ? -ENOMEM : -EIO); ++ } ++ ++ deflateEnd(&strm); ++ ++ return ret; ++} ++ ++/* ++ * qcow2_zlib_decompress() ++ * ++ * Decompress some data (not more than @src_size bytes) to produce exactly ++ * @dest_size bytes using zlib compression method ++ * ++ * @dest - destination buffer, @dest_size bytes ++ * @src - source buffer, @src_size bytes ++ * ++ * Returns: 0 on success ++ * -EIO on fail ++ */ ++static ssize_t qcow2_zlib_decompress(void *dest, size_t dest_size, ++ const void *src, size_t src_size) ++{ ++ int ret; ++ z_stream strm; ++ ++ memset(&strm, 0, sizeof(strm)); ++ strm.avail_in = src_size; ++ strm.next_in = (void *) src; ++ strm.avail_out = dest_size; ++ strm.next_out = dest; ++ ++ ret = inflateInit2(&strm, -12); ++ if (ret != Z_OK) { ++ return -EIO; ++ } ++ ++ ret = inflate(&strm, Z_FINISH); ++ if ((ret == Z_STREAM_END || ret == Z_BUF_ERROR) && strm.avail_out == 0) { ++ /* ++ * We approve Z_BUF_ERROR because we need @dest buffer to be filled, but ++ * @src buffer may be processed partly (because in qcow2 we know size of ++ * compressed data with precision of one sector) ++ */ ++ ret = 0; ++ } else { ++ ret = -EIO; ++ } ++ ++ inflateEnd(&strm); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_ZSTD ++ ++/* ++ * qcow2_zstd_compress() ++ * ++ * Compress @src_size bytes of data using zstd compression method ++ * ++ * @dest - destination buffer, @dest_size bytes ++ * @src - source buffer, @src_size bytes ++ * ++ * Returns: compressed size on success ++ * -ENOMEM destination buffer is not enough to store compressed data ++ * -EIO on any other error ++ */ ++static ssize_t qcow2_zstd_compress(void *dest, size_t dest_size, ++ const void *src, size_t src_size) ++{ ++ ssize_t ret; ++ size_t zstd_ret; ++ ZSTD_outBuffer output = { ++ .dst = dest, ++ .size = dest_size, ++ .pos = 0 ++ }; ++ ZSTD_inBuffer input = { ++ .src = src, ++ .size = src_size, ++ .pos = 0 ++ }; ++ ZSTD_CCtx *cctx = ZSTD_createCCtx(); ++ ++ if (!cctx) { ++ return -EIO; ++ } ++ /* ++ * Use the zstd streamed interface for symmetry with decompression, ++ * where streaming is essential since we don't record the exact ++ * compressed size. ++ * ++ * ZSTD_compressStream2() tries to compress everything it could ++ * with a single call. Although, ZSTD docs says that: ++ * "You must continue calling ZSTD_compressStream2() with ZSTD_e_end ++ * until it returns 0, at which point you are free to start a new frame", ++ * in out tests we saw the only case when it returned with >0 - ++ * when the output buffer was too small. In that case, ++ * ZSTD_compressStream2() expects a bigger buffer on the next call. ++ * We can't provide a bigger buffer because we are limited with dest_size ++ * which we pass to the ZSTD_compressStream2() at once. ++ * So, we don't need any loops and just abort the compression when we ++ * don't get 0 result on the first call. ++ */ ++ zstd_ret = ZSTD_compressStream2(cctx, &output, &input, ZSTD_e_end); ++ ++ if (zstd_ret) { ++ if (zstd_ret > output.size - output.pos) { ++ ret = -ENOMEM; ++ } else { ++ ret = -EIO; ++ } ++ goto out; ++ } ++ ++ /* make sure that zstd didn't overflow the dest buffer */ ++ assert(output.pos <= dest_size); ++ ret = output.pos; ++out: ++ ZSTD_freeCCtx(cctx); ++ return ret; ++} ++ ++/* ++ * qcow2_zstd_decompress() ++ * ++ * Decompress some data (not more than @src_size bytes) to produce exactly ++ * @dest_size bytes using zstd compression method ++ * ++ * @dest - destination buffer, @dest_size bytes ++ * @src - source buffer, @src_size bytes ++ * ++ * Returns: 0 on success ++ * -EIO on any error ++ */ ++static ssize_t qcow2_zstd_decompress(void *dest, size_t dest_size, ++ const void *src, size_t src_size) ++{ ++ size_t zstd_ret = 0; ++ ssize_t ret = 0; ++ ZSTD_outBuffer output = { ++ .dst = dest, ++ .size = dest_size, ++ .pos = 0 ++ }; ++ ZSTD_inBuffer input = { ++ .src = src, ++ .size = src_size, ++ .pos = 0 ++ }; ++ ZSTD_DCtx *dctx = ZSTD_createDCtx(); ++ ++ if (!dctx) { ++ return -EIO; ++ } ++ ++ /* ++ * The compressed stream from the input buffer may consist of more ++ * than one zstd frame. So we iterate until we get a fully ++ * uncompressed cluster. ++ * From zstd docs related to ZSTD_decompressStream: ++ * "return : 0 when a frame is completely decoded and fully flushed" ++ * We suppose that this means: each time ZSTD_decompressStream reads ++ * only ONE full frame and returns 0 if and only if that frame ++ * is completely decoded and flushed. Only after returning 0, ++ * ZSTD_decompressStream reads another ONE full frame. ++ */ ++ while (output.pos < output.size) { ++ size_t last_in_pos = input.pos; ++ size_t last_out_pos = output.pos; ++ zstd_ret = ZSTD_decompressStream(dctx, &output, &input); ++ ++ if (ZSTD_isError(zstd_ret)) { ++ ret = -EIO; ++ break; ++ } ++ ++ /* ++ * The ZSTD manual is vague about what to do if it reads ++ * the buffer partially, and we don't want to get stuck ++ * in an infinite loop where ZSTD_decompressStream ++ * returns > 0 waiting for another input chunk. So, we add ++ * a check which ensures that the loop makes some progress ++ * on each step. ++ */ ++ if (last_in_pos >= input.pos && ++ last_out_pos >= output.pos) { ++ ret = -EIO; ++ break; ++ } ++ } ++ /* ++ * Make sure that we have the frame fully flushed here ++ * if not, we somehow managed to get uncompressed cluster ++ * greater then the cluster size, possibly because of its ++ * damage. ++ */ ++ if (zstd_ret > 0) { ++ ret = -EIO; ++ } ++ ++ ZSTD_freeDCtx(dctx); ++ assert(ret == 0 || ret == -EIO); ++ return ret; ++} ++#endif ++ ++static int qcow2_compress_pool_func(void *opaque) ++{ ++ Qcow2CompressData *data = opaque; ++ ++ data->ret = data->func(data->dest, data->dest_size, ++ data->src, data->src_size); ++ ++ return 0; ++} ++ ++static ssize_t coroutine_fn ++qcow2_co_do_compress(BlockDriverState *bs, void *dest, size_t dest_size, ++ const void *src, size_t src_size, Qcow2CompressFunc func) ++{ ++ Qcow2CompressData arg = { ++ .dest = dest, ++ .dest_size = dest_size, ++ .src = src, ++ .src_size = src_size, ++ .func = func, ++ }; ++ ++ qcow2_co_process(bs, qcow2_compress_pool_func, &arg); ++ ++ return arg.ret; ++} ++ ++/* ++ * qcow2_co_compress() ++ * ++ * Compress @src_size bytes of data using the compression ++ * method defined by the image compression type ++ * ++ * @dest - destination buffer, @dest_size bytes ++ * @src - source buffer, @src_size bytes ++ * ++ * Returns: compressed size on success ++ * a negative error code on failure ++ */ ++ssize_t coroutine_fn ++qcow2_co_compress(BlockDriverState *bs, void *dest, size_t dest_size, ++ const void *src, size_t src_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2CompressFunc fn; ++ ++ switch (s->compression_type) { ++ case QCOW2_COMPRESSION_TYPE_ZLIB: ++ fn = qcow2_zlib_compress; ++ break; ++ ++#ifdef CONFIG_ZSTD ++ case QCOW2_COMPRESSION_TYPE_ZSTD: ++ fn = qcow2_zstd_compress; ++ break; ++#endif ++ default: ++ abort(); ++ } ++ ++ return qcow2_co_do_compress(bs, dest, dest_size, src, src_size, fn); ++} ++ ++/* ++ * qcow2_co_decompress() ++ * ++ * Decompress some data (not more than @src_size bytes) to produce exactly ++ * @dest_size bytes using the compression method defined by the image ++ * compression type ++ * ++ * @dest - destination buffer, @dest_size bytes ++ * @src - source buffer, @src_size bytes ++ * ++ * Returns: 0 on success ++ * a negative error code on failure ++ */ ++ssize_t coroutine_fn ++qcow2_co_decompress(BlockDriverState *bs, void *dest, size_t dest_size, ++ const void *src, size_t src_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2CompressFunc fn; ++ ++ switch (s->compression_type) { ++ case QCOW2_COMPRESSION_TYPE_ZLIB: ++ fn = qcow2_zlib_decompress; ++ break; ++ ++#ifdef CONFIG_ZSTD ++ case QCOW2_COMPRESSION_TYPE_ZSTD: ++ fn = qcow2_zstd_decompress; ++ break; ++#endif ++ default: ++ abort(); ++ } ++ ++ return qcow2_co_do_compress(bs, dest, dest_size, src, src_size, fn); ++} ++ ++ ++/* ++ * Cryptography ++ */ ++ ++/* ++ * Qcow2EncDecFunc: common prototype of qcrypto_block_encrypt() and ++ * qcrypto_block_decrypt() functions. ++ */ ++typedef int (*Qcow2EncDecFunc)(QCryptoBlock *block, uint64_t offset, ++ uint8_t *buf, size_t len, Error **errp); ++ ++typedef struct Qcow2EncDecData { ++ QCryptoBlock *block; ++ uint64_t offset; ++ uint8_t *buf; ++ size_t len; ++ ++ Qcow2EncDecFunc func; ++} Qcow2EncDecData; ++ ++static int qcow2_encdec_pool_func(void *opaque) ++{ ++ Qcow2EncDecData *data = opaque; ++ ++ return data->func(data->block, data->offset, data->buf, data->len, NULL); ++} ++ ++static int coroutine_fn ++qcow2_co_encdec(BlockDriverState *bs, uint64_t host_offset, ++ uint64_t guest_offset, void *buf, size_t len, ++ Qcow2EncDecFunc func) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2EncDecData arg = { ++ .block = s->crypto, ++ .offset = s->crypt_physical_offset ? host_offset : guest_offset, ++ .buf = buf, ++ .len = len, ++ .func = func, ++ }; ++ uint64_t sector_size; ++ ++ assert(s->crypto); ++ ++ sector_size = qcrypto_block_get_sector_size(s->crypto); ++ assert(QEMU_IS_ALIGNED(guest_offset, sector_size)); ++ assert(QEMU_IS_ALIGNED(host_offset, sector_size)); ++ assert(QEMU_IS_ALIGNED(len, sector_size)); ++ ++ return len == 0 ? 0 : qcow2_co_process(bs, qcow2_encdec_pool_func, &arg); ++} ++ ++/* ++ * qcow2_co_encrypt() ++ * ++ * Encrypts one or more contiguous aligned sectors ++ * ++ * @host_offset - underlying storage offset of the first sector of the ++ * data to be encrypted ++ * ++ * @guest_offset - guest (virtual) offset of the first sector of the ++ * data to be encrypted ++ * ++ * @buf - buffer with the data to encrypt, that after encryption ++ * will be written to the underlying storage device at ++ * @host_offset ++ * ++ * @len - length of the buffer (must be a multiple of the encryption ++ * sector size) ++ * ++ * Depending on the encryption method, @host_offset and/or @guest_offset ++ * may be used for generating the initialization vector for ++ * encryption. ++ * ++ * Note that while the whole range must be aligned on sectors, it ++ * does not have to be aligned on clusters and can also cross cluster ++ * boundaries ++ */ ++int coroutine_fn ++qcow2_co_encrypt(BlockDriverState *bs, uint64_t host_offset, ++ uint64_t guest_offset, void *buf, size_t len) ++{ ++ return qcow2_co_encdec(bs, host_offset, guest_offset, buf, len, ++ qcrypto_block_encrypt); ++} ++ ++/* ++ * qcow2_co_decrypt() ++ * ++ * Decrypts one or more contiguous aligned sectors ++ * Similar to qcow2_co_encrypt ++ */ ++int coroutine_fn ++qcow2_co_decrypt(BlockDriverState *bs, uint64_t host_offset, ++ uint64_t guest_offset, void *buf, size_t len) ++{ ++ return qcow2_co_encdec(bs, host_offset, guest_offset, buf, len, ++ qcrypto_block_decrypt); ++} +diff --git a/qcow2/lib/qcow2.c b/qcow2/lib/qcow2.c +new file mode 100644 +index 00000000..70b19730 +--- /dev/null ++++ b/qcow2/lib/qcow2.c +@@ -0,0 +1,6214 @@ ++/* ++ * Block driver for the QCOW version 2 format ++ * ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "block/qdict.h" ++#include "sysemu/block-backend.h" ++#include "qemu/main-loop.h" ++#include "qemu/module.h" ++#include "qcow2.h" ++#include "qemu/error-report.h" ++#include "qapi/error.h" ++#include "qapi/qapi-events-block-core.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qstring.h" ++#include "trace.h" ++#include "qemu/option_int.h" ++#include "qemu/cutils.h" ++#include "qemu/bswap.h" ++#include "qemu/memalign.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qapi/qapi-visit-block-core.h" ++#include "crypto.h" ++#include "block/aio_task.h" ++#include "block/dirty-bitmap.h" ++ ++/* ++ Differences with QCOW: ++ ++ - Support for multiple incremental snapshots. ++ - Memory management by reference counts. ++ - Clusters which have a reference count of one have the bit ++ QCOW_OFLAG_COPIED to optimize write performance. ++ - Size of compressed clusters is stored in sectors to reduce bit usage ++ in the cluster offsets. ++ - Support for storing additional data (such as the VM state) in the ++ snapshots. ++ - If a backing store is used, the cluster size is not constrained ++ (could be backported to QCOW). ++ - L2 tables have always a size of one cluster. ++*/ ++ ++ ++typedef struct { ++ uint32_t magic; ++ uint32_t len; ++} QEMU_PACKED QCowExtension; ++ ++#define QCOW2_EXT_MAGIC_END 0 ++#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 ++#define QCOW2_EXT_MAGIC_DATA_FILE 0x44415441 ++ ++static int coroutine_fn ++qcow2_co_preadv_compressed(BlockDriverState *bs, ++ uint64_t l2_entry, ++ uint64_t offset, ++ uint64_t bytes, ++ QEMUIOVector *qiov, ++ size_t qiov_offset); ++ ++static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) ++{ ++ const QCowHeader *cow_header = (const void *)buf; ++ ++ if (buf_size >= sizeof(QCowHeader) && ++ be32_to_cpu(cow_header->magic) == QCOW_MAGIC && ++ be32_to_cpu(cow_header->version) >= 2) ++ return 100; ++ else ++ return 0; ++} ++ ++ ++static int GRAPH_RDLOCK ++qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset, ++ uint8_t *buf, size_t buflen, ++ void *opaque, Error **errp) ++{ ++ BlockDriverState *bs = opaque; ++ BDRVQcow2State *s = bs->opaque; ++ ssize_t ret; ++ ++ if ((offset + buflen) > s->crypto_header.length) { ++ error_setg(errp, "Request for data outside of extension header"); ++ return -1; ++ } ++ ++ ret = bdrv_pread(bs->file, s->crypto_header.offset + offset, buflen, buf, ++ 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not read encryption header"); ++ return -1; ++ } ++ return 0; ++} ++ ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, void *opaque, ++ Error **errp) ++{ ++ BlockDriverState *bs = opaque; ++ BDRVQcow2State *s = bs->opaque; ++ int64_t ret; ++ int64_t clusterlen; ++ ++ ret = qcow2_alloc_clusters(bs, headerlen); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Cannot allocate cluster for LUKS header size %zu", ++ headerlen); ++ return -1; ++ } ++ ++ s->crypto_header.length = headerlen; ++ s->crypto_header.offset = ret; ++ ++ /* ++ * Zero fill all space in cluster so it has predictable ++ * content, as we may not initialize some regions of the ++ * header (eg only 1 out of 8 key slots will be initialized) ++ */ ++ clusterlen = size_to_clusters(s, headerlen) * s->cluster_size; ++ assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen, false) == 0); ++ ret = bdrv_co_pwrite_zeroes(bs->file, ret, clusterlen, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not zero fill encryption header"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++/* The graph lock must be held when called in coroutine context */ ++static int coroutine_mixed_fn GRAPH_RDLOCK ++qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset, ++ const uint8_t *buf, size_t buflen, ++ void *opaque, Error **errp) ++{ ++ BlockDriverState *bs = opaque; ++ BDRVQcow2State *s = bs->opaque; ++ ssize_t ret; ++ ++ if ((offset + buflen) > s->crypto_header.length) { ++ error_setg(errp, "Request for data outside of extension header"); ++ return -1; ++ } ++ ++ ret = bdrv_pwrite(bs->file, s->crypto_header.offset + offset, buflen, buf, ++ 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not read encryption header"); ++ return -1; ++ } ++ return 0; ++} ++ ++static QDict* ++qcow2_extract_crypto_opts(QemuOpts *opts, const char *fmt, Error **errp) ++{ ++ QDict *cryptoopts_qdict; ++ QDict *opts_qdict; ++ ++ /* Extract "encrypt." options into a qdict */ ++ opts_qdict = qemu_opts_to_qdict(opts, NULL); ++ qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt."); ++ qobject_unref(opts_qdict); ++ qdict_put_str(cryptoopts_qdict, "format", fmt); ++ return cryptoopts_qdict; ++} ++ ++/* ++ * read qcow2 extension and fill bs ++ * start reading from start_offset ++ * finish reading upon magic of value 0 or when end_offset reached ++ * unknown magic is skipped (future extension this version knows nothing about) ++ * return 0 upon success, non-0 otherwise ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, ++ uint64_t end_offset, void **p_feature_table, ++ int flags, bool *need_update_header, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowExtension ext; ++ uint64_t offset; ++ int ret; ++ Qcow2BitmapHeaderExt bitmaps_ext; ++ ++ if (need_update_header != NULL) { ++ *need_update_header = false; ++ } ++ ++#ifdef DEBUG_EXT ++ printf("qcow2_read_extensions: start=%ld end=%ld\n", start_offset, end_offset); ++#endif ++ offset = start_offset; ++ while (offset < end_offset) { ++ ++#ifdef DEBUG_EXT ++ /* Sanity check */ ++ if (offset > s->cluster_size) ++ printf("qcow2_read_extension: suspicious offset %lu\n", offset); ++ ++ printf("attempting to read extended header in offset %lu\n", offset); ++#endif ++ ++ ret = bdrv_co_pread(bs->file, offset, sizeof(ext), &ext, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "qcow2_read_extension: ERROR: " ++ "pread fail from offset %" PRIu64, offset); ++ return 1; ++ } ++ ext.magic = be32_to_cpu(ext.magic); ++ ext.len = be32_to_cpu(ext.len); ++ offset += sizeof(ext); ++#ifdef DEBUG_EXT ++ printf("ext.magic = 0x%x\n", ext.magic); ++#endif ++ if (offset > end_offset || ext.len > end_offset - offset) { ++ error_setg(errp, "Header extension too large"); ++ return -EINVAL; ++ } ++ ++ switch (ext.magic) { ++ case QCOW2_EXT_MAGIC_END: ++ return 0; ++ ++ case QCOW2_EXT_MAGIC_BACKING_FORMAT: ++ if (ext.len >= sizeof(bs->backing_format)) { ++ error_setg(errp, "ERROR: ext_backing_format: len=%" PRIu32 ++ " too large (>=%zu)", ext.len, ++ sizeof(bs->backing_format)); ++ return 2; ++ } ++ ret = bdrv_co_pread(bs->file, offset, ext.len, bs->backing_format, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR: ext_backing_format: " ++ "Could not read format name"); ++ return 3; ++ } ++ bs->backing_format[ext.len] = '\0'; ++ s->image_backing_format = g_strdup(bs->backing_format); ++#ifdef DEBUG_EXT ++ printf("Qcow2: Got format extension %s\n", bs->backing_format); ++#endif ++ break; ++ ++ case QCOW2_EXT_MAGIC_FEATURE_TABLE: ++ if (p_feature_table != NULL) { ++ void *feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); ++ ret = bdrv_co_pread(bs->file, offset, ext.len, feature_table, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR: ext_feature_table: " ++ "Could not read table"); ++ g_free(feature_table); ++ return ret; ++ } ++ ++ *p_feature_table = feature_table; ++ } ++ break; ++ ++ case QCOW2_EXT_MAGIC_CRYPTO_HEADER: { ++ unsigned int cflags = 0; ++ if (s->crypt_method_header != QCOW_CRYPT_LUKS) { ++ error_setg(errp, "CRYPTO header extension only " ++ "expected with LUKS encryption method"); ++ return -EINVAL; ++ } ++ if (ext.len != sizeof(Qcow2CryptoHeaderExtension)) { ++ error_setg(errp, "CRYPTO header extension size %u, " ++ "but expected size %zu", ext.len, ++ sizeof(Qcow2CryptoHeaderExtension)); ++ return -EINVAL; ++ } ++ ++ ret = bdrv_co_pread(bs->file, offset, ext.len, &s->crypto_header, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Unable to read CRYPTO header extension"); ++ return ret; ++ } ++ s->crypto_header.offset = be64_to_cpu(s->crypto_header.offset); ++ s->crypto_header.length = be64_to_cpu(s->crypto_header.length); ++ ++ if ((s->crypto_header.offset % s->cluster_size) != 0) { ++ error_setg(errp, "Encryption header offset '%" PRIu64 "' is " ++ "not a multiple of cluster size '%u'", ++ s->crypto_header.offset, s->cluster_size); ++ return -EINVAL; ++ } ++ ++ if (flags & BDRV_O_NO_IO) { ++ cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; ++ } ++ s->crypto = qcrypto_block_open(s->crypto_opts, "encrypt.", ++ qcow2_crypto_hdr_read_func, ++ bs, cflags, errp); ++ if (!s->crypto) { ++ return -EINVAL; ++ } ++ } break; ++ ++ case QCOW2_EXT_MAGIC_BITMAPS: ++ if (ext.len != sizeof(bitmaps_ext)) { ++ error_setg_errno(errp, -ret, "bitmaps_ext: " ++ "Invalid extension length"); ++ return -EINVAL; ++ } ++ ++ if (!(s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS)) { ++ if (s->qcow_version < 3) { ++ /* Let's be a bit more specific */ ++ warn_report("This qcow2 v2 image contains bitmaps, but " ++ "they may have been modified by a program " ++ "without persistent bitmap support; so now " ++ "they must all be considered inconsistent"); ++ } else { ++ warn_report("a program lacking bitmap support " ++ "modified this file, so all bitmaps are now " ++ "considered inconsistent"); ++ } ++ error_printf("Some clusters may be leaked, " ++ "run 'qemu-img check -r' on the image " ++ "file to fix."); ++ if (need_update_header != NULL) { ++ /* Updating is needed to drop invalid bitmap extension. */ ++ *need_update_header = true; ++ } ++ break; ++ } ++ ++ ret = bdrv_co_pread(bs->file, offset, ext.len, &bitmaps_ext, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "bitmaps_ext: " ++ "Could not read ext header"); ++ return ret; ++ } ++ ++ if (bitmaps_ext.reserved32 != 0) { ++ error_setg_errno(errp, -ret, "bitmaps_ext: " ++ "Reserved field is not zero"); ++ return -EINVAL; ++ } ++ ++ bitmaps_ext.nb_bitmaps = be32_to_cpu(bitmaps_ext.nb_bitmaps); ++ bitmaps_ext.bitmap_directory_size = ++ be64_to_cpu(bitmaps_ext.bitmap_directory_size); ++ bitmaps_ext.bitmap_directory_offset = ++ be64_to_cpu(bitmaps_ext.bitmap_directory_offset); ++ ++ if (bitmaps_ext.nb_bitmaps > QCOW2_MAX_BITMAPS) { ++ error_setg(errp, ++ "bitmaps_ext: Image has %" PRIu32 " bitmaps, " ++ "exceeding the QEMU supported maximum of %d", ++ bitmaps_ext.nb_bitmaps, QCOW2_MAX_BITMAPS); ++ return -EINVAL; ++ } ++ ++ if (bitmaps_ext.nb_bitmaps == 0) { ++ error_setg(errp, "found bitmaps extension with zero bitmaps"); ++ return -EINVAL; ++ } ++ ++ if (offset_into_cluster(s, bitmaps_ext.bitmap_directory_offset)) { ++ error_setg(errp, "bitmaps_ext: " ++ "invalid bitmap directory offset"); ++ return -EINVAL; ++ } ++ ++ if (bitmaps_ext.bitmap_directory_size > ++ QCOW2_MAX_BITMAP_DIRECTORY_SIZE) { ++ error_setg(errp, "bitmaps_ext: " ++ "bitmap directory size (%" PRIu64 ") exceeds " ++ "the maximum supported size (%d)", ++ bitmaps_ext.bitmap_directory_size, ++ QCOW2_MAX_BITMAP_DIRECTORY_SIZE); ++ return -EINVAL; ++ } ++ ++ s->nb_bitmaps = bitmaps_ext.nb_bitmaps; ++ s->bitmap_directory_offset = ++ bitmaps_ext.bitmap_directory_offset; ++ s->bitmap_directory_size = ++ bitmaps_ext.bitmap_directory_size; ++ ++#ifdef DEBUG_EXT ++ printf("Qcow2: Got bitmaps extension: " ++ "offset=%" PRIu64 " nb_bitmaps=%" PRIu32 "\n", ++ s->bitmap_directory_offset, s->nb_bitmaps); ++#endif ++ break; ++ ++ case QCOW2_EXT_MAGIC_DATA_FILE: ++ { ++ s->image_data_file = g_malloc0(ext.len + 1); ++ ret = bdrv_co_pread(bs->file, offset, ext.len, s->image_data_file, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "ERROR: Could not read data file name"); ++ return ret; ++ } ++#ifdef DEBUG_EXT ++ printf("Qcow2: Got external data file %s\n", s->image_data_file); ++#endif ++ break; ++ } ++ ++ default: ++ /* unknown magic - save it in case we need to rewrite the header */ ++ /* If you add a new feature, make sure to also update the fast ++ * path of qcow2_make_empty() to deal with it. */ ++ { ++ Qcow2UnknownHeaderExtension *uext; ++ ++ uext = g_malloc0(sizeof(*uext) + ext.len); ++ uext->magic = ext.magic; ++ uext->len = ext.len; ++ QLIST_INSERT_HEAD(&s->unknown_header_ext, uext, next); ++ ++ ret = bdrv_co_pread(bs->file, offset, uext->len, uext->data, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR: unknown extension: " ++ "Could not read data"); ++ return ret; ++ } ++ } ++ break; ++ } ++ ++ offset += ((ext.len + 7) & ~7); ++ } ++ ++ return 0; ++} ++ ++static void cleanup_unknown_header_ext(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2UnknownHeaderExtension *uext, *next; ++ ++ QLIST_FOREACH_SAFE(uext, &s->unknown_header_ext, next, next) { ++ QLIST_REMOVE(uext, next); ++ g_free(uext); ++ } ++} ++ ++static void report_unsupported_feature(Error **errp, Qcow2Feature *table, ++ uint64_t mask) ++{ ++ g_autoptr(GString) features = g_string_sized_new(60); ++ ++ while (table && table->name[0] != '\0') { ++ if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) { ++ if (mask & (1ULL << table->bit)) { ++ if (features->len > 0) { ++ g_string_append(features, ", "); ++ } ++ g_string_append_printf(features, "%.46s", table->name); ++ mask &= ~(1ULL << table->bit); ++ } ++ } ++ table++; ++ } ++ ++ if (mask) { ++ if (features->len > 0) { ++ g_string_append(features, ", "); ++ } ++ g_string_append_printf(features, ++ "Unknown incompatible feature: %" PRIx64, mask); ++ } ++ ++ error_setg(errp, "Unsupported qcow2 feature(s): %s", features->str); ++} ++ ++/* ++ * Sets the dirty bit and flushes afterwards if necessary. ++ * ++ * The incompatible_features bit is only set if the image file header was ++ * updated successfully. Therefore it is not required to check the return ++ * value of this function. ++ */ ++int qcow2_mark_dirty(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t val; ++ int ret; ++ ++ assert(s->qcow_version >= 3); ++ ++ if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { ++ return 0; /* already dirty */ ++ } ++ ++ val = cpu_to_be64(s->incompatible_features | QCOW2_INCOMPAT_DIRTY); ++ ret = bdrv_pwrite_sync(bs->file, ++ offsetof(QCowHeader, incompatible_features), ++ sizeof(val), &val, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Only treat image as dirty if the header was updated successfully */ ++ s->incompatible_features |= QCOW2_INCOMPAT_DIRTY; ++ return 0; ++} ++ ++/* ++ * Clears the dirty bit and flushes before if necessary. Only call this ++ * function when there are no pending requests, it does not guard against ++ * concurrent requests dirtying the image. ++ */ ++static int GRAPH_RDLOCK qcow2_mark_clean(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { ++ int ret; ++ ++ s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY; ++ ++ ret = qcow2_flush_caches(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return qcow2_update_header(bs); ++ } ++ return 0; ++} ++ ++/* ++ * Marks the image as corrupt. ++ */ ++int qcow2_mark_corrupt(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ s->incompatible_features |= QCOW2_INCOMPAT_CORRUPT; ++ return qcow2_update_header(bs); ++} ++ ++/* ++ * Marks the image as consistent, i.e., unsets the corrupt bit, and flushes ++ * before if necessary. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_mark_consistent(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) { ++ int ret = qcow2_flush_caches(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ s->incompatible_features &= ~QCOW2_INCOMPAT_CORRUPT; ++ return qcow2_update_header(bs); ++ } ++ return 0; ++} ++ ++static void qcow2_add_check_result(BdrvCheckResult *out, ++ const BdrvCheckResult *src, ++ bool set_allocation_info) ++{ ++ out->corruptions += src->corruptions; ++ out->leaks += src->leaks; ++ out->check_errors += src->check_errors; ++ out->corruptions_fixed += src->corruptions_fixed; ++ out->leaks_fixed += src->leaks_fixed; ++ ++ if (set_allocation_info) { ++ out->image_end_offset = src->image_end_offset; ++ out->bfi = src->bfi; ++ } ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_check_locked(BlockDriverState *bs, BdrvCheckResult *result, ++ BdrvCheckMode fix) ++{ ++ BdrvCheckResult snapshot_res = {}; ++ BdrvCheckResult refcount_res = {}; ++ int ret; ++ ++ memset(result, 0, sizeof(*result)); ++ ++ ret = qcow2_check_read_snapshot_table(bs, &snapshot_res, fix); ++ if (ret < 0) { ++ qcow2_add_check_result(result, &snapshot_res, false); ++ return ret; ++ } ++ ++ ret = qcow2_check_refcounts(bs, &refcount_res, fix); ++ qcow2_add_check_result(result, &refcount_res, true); ++ if (ret < 0) { ++ qcow2_add_check_result(result, &snapshot_res, false); ++ return ret; ++ } ++ ++ ret = qcow2_check_fix_snapshot_table(bs, &snapshot_res, fix); ++ qcow2_add_check_result(result, &snapshot_res, false); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (fix && result->check_errors == 0 && result->corruptions == 0) { ++ ret = qcow2_mark_clean(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ return qcow2_mark_consistent(bs); ++ } ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_check(BlockDriverState *bs, BdrvCheckResult *result, ++ BdrvCheckMode fix) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ qemu_co_mutex_lock(&s->lock); ++ ret = qcow2_co_check_locked(bs, result, fix); ++ qemu_co_mutex_unlock(&s->lock); ++ return ret; ++} ++ ++int qcow2_validate_table(BlockDriverState *bs, uint64_t offset, ++ uint64_t entries, size_t entry_len, ++ int64_t max_size_bytes, const char *table_name, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ if (entries > max_size_bytes / entry_len) { ++ error_setg(errp, "%s too large", table_name); ++ return -EFBIG; ++ } ++ ++ /* Use signed INT64_MAX as the maximum even for uint64_t header fields, ++ * because values will be passed to qemu functions taking int64_t. */ ++ if ((INT64_MAX - entries * entry_len < offset) || ++ (offset_into_cluster(s, offset) != 0)) { ++ error_setg(errp, "%s offset invalid", table_name); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const char *const mutable_opts[] = { ++ QCOW2_OPT_LAZY_REFCOUNTS, ++ QCOW2_OPT_DISCARD_REQUEST, ++ QCOW2_OPT_DISCARD_SNAPSHOT, ++ QCOW2_OPT_DISCARD_OTHER, ++ QCOW2_OPT_DISCARD_NO_UNREF, ++ QCOW2_OPT_OVERLAP, ++ QCOW2_OPT_OVERLAP_TEMPLATE, ++ QCOW2_OPT_OVERLAP_MAIN_HEADER, ++ QCOW2_OPT_OVERLAP_ACTIVE_L1, ++ QCOW2_OPT_OVERLAP_ACTIVE_L2, ++ QCOW2_OPT_OVERLAP_REFCOUNT_TABLE, ++ QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK, ++ QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE, ++ QCOW2_OPT_OVERLAP_INACTIVE_L1, ++ QCOW2_OPT_OVERLAP_INACTIVE_L2, ++ QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY, ++ QCOW2_OPT_CACHE_SIZE, ++ QCOW2_OPT_L2_CACHE_SIZE, ++ QCOW2_OPT_L2_CACHE_ENTRY_SIZE, ++ QCOW2_OPT_REFCOUNT_CACHE_SIZE, ++ QCOW2_OPT_CACHE_CLEAN_INTERVAL, ++ NULL ++}; ++ ++static QemuOptsList qcow2_runtime_opts = { ++ .name = "qcow2", ++ .head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head), ++ .desc = { ++ { ++ .name = QCOW2_OPT_LAZY_REFCOUNTS, ++ .type = QEMU_OPT_BOOL, ++ .help = "Postpone refcount updates", ++ }, ++ { ++ .name = QCOW2_OPT_DISCARD_REQUEST, ++ .type = QEMU_OPT_BOOL, ++ .help = "Pass guest discard requests to the layer below", ++ }, ++ { ++ .name = QCOW2_OPT_DISCARD_SNAPSHOT, ++ .type = QEMU_OPT_BOOL, ++ .help = "Generate discard requests when snapshot related space " ++ "is freed", ++ }, ++ { ++ .name = QCOW2_OPT_DISCARD_OTHER, ++ .type = QEMU_OPT_BOOL, ++ .help = "Generate discard requests when other clusters are freed", ++ }, ++ { ++ .name = QCOW2_OPT_DISCARD_NO_UNREF, ++ .type = QEMU_OPT_BOOL, ++ .help = "Do not unreference discarded clusters", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP, ++ .type = QEMU_OPT_STRING, ++ .help = "Selects which overlap checks to perform from a range of " ++ "templates (none, constant, cached, all)", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_TEMPLATE, ++ .type = QEMU_OPT_STRING, ++ .help = "Selects which overlap checks to perform from a range of " ++ "templates (none, constant, cached, all)", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_MAIN_HEADER, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into the main qcow2 header", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_ACTIVE_L1, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into the active L1 table", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_ACTIVE_L2, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into an active L2 table", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into the refcount table", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into a refcount block", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into the snapshot table", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_INACTIVE_L1, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into an inactive L1 table", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_INACTIVE_L2, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into an inactive L2 table", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into the bitmap directory", ++ }, ++ { ++ .name = QCOW2_OPT_CACHE_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Maximum combined metadata (L2 tables and refcount blocks) " ++ "cache size", ++ }, ++ { ++ .name = QCOW2_OPT_L2_CACHE_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Maximum L2 table cache size", ++ }, ++ { ++ .name = QCOW2_OPT_L2_CACHE_ENTRY_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Size of each entry in the L2 cache", ++ }, ++ { ++ .name = QCOW2_OPT_REFCOUNT_CACHE_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Maximum refcount block cache size", ++ }, ++ { ++ .name = QCOW2_OPT_CACHE_CLEAN_INTERVAL, ++ .type = QEMU_OPT_NUMBER, ++ .help = "Clean unused cache entries after this time (in seconds)", ++ }, ++ BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", ++ "ID of secret providing qcow2 AES key or LUKS passphrase"), ++ { /* end of list */ } ++ }, ++}; ++ ++static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = { ++ [QCOW2_OL_MAIN_HEADER_BITNR] = QCOW2_OPT_OVERLAP_MAIN_HEADER, ++ [QCOW2_OL_ACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L1, ++ [QCOW2_OL_ACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L2, ++ [QCOW2_OL_REFCOUNT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE, ++ [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK, ++ [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE, ++ [QCOW2_OL_INACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L1, ++ [QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2, ++ [QCOW2_OL_BITMAP_DIRECTORY_BITNR] = QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY, ++}; ++ ++static void cache_clean_timer_cb(void *opaque) ++{ ++ BlockDriverState *bs = opaque; ++ BDRVQcow2State *s = bs->opaque; ++ qcow2_cache_clean_unused(s->l2_table_cache); ++ qcow2_cache_clean_unused(s->refcount_block_cache); ++ timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + ++ (int64_t) s->cache_clean_interval * 1000); ++} ++ ++static void cache_clean_timer_init(BlockDriverState *bs, AioContext *context) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ if (s->cache_clean_interval > 0) { ++ s->cache_clean_timer = ++ aio_timer_new_with_attrs(context, QEMU_CLOCK_VIRTUAL, ++ SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL, ++ cache_clean_timer_cb, bs); ++ timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + ++ (int64_t) s->cache_clean_interval * 1000); ++ } ++} ++ ++static void cache_clean_timer_del(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ if (s->cache_clean_timer) { ++ timer_free(s->cache_clean_timer); ++ s->cache_clean_timer = NULL; ++ } ++} ++ ++static void qcow2_detach_aio_context(BlockDriverState *bs) ++{ ++ cache_clean_timer_del(bs); ++} ++ ++static void qcow2_attach_aio_context(BlockDriverState *bs, ++ AioContext *new_context) ++{ ++ cache_clean_timer_init(bs, new_context); ++} ++ ++static bool read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, ++ uint64_t *l2_cache_size, ++ uint64_t *l2_cache_entry_size, ++ uint64_t *refcount_cache_size, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t combined_cache_size, l2_cache_max_setting; ++ bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set; ++ bool l2_cache_entry_size_set; ++ int min_refcount_cache = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; ++ uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; ++ uint64_t max_l2_entries = DIV_ROUND_UP(virtual_disk_size, s->cluster_size); ++ /* An L2 table is always one cluster in size so the max cache size ++ * should be a multiple of the cluster size. */ ++ uint64_t max_l2_cache = ROUND_UP(max_l2_entries * l2_entry_size(s), ++ s->cluster_size); ++ ++ combined_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_CACHE_SIZE); ++ l2_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_SIZE); ++ refcount_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_REFCOUNT_CACHE_SIZE); ++ l2_cache_entry_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE); ++ ++ combined_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_CACHE_SIZE, 0); ++ l2_cache_max_setting = qemu_opt_get_size(opts, QCOW2_OPT_L2_CACHE_SIZE, ++ DEFAULT_L2_CACHE_MAX_SIZE); ++ *refcount_cache_size = qemu_opt_get_size(opts, ++ QCOW2_OPT_REFCOUNT_CACHE_SIZE, 0); ++ ++ *l2_cache_entry_size = qemu_opt_get_size( ++ opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE, s->cluster_size); ++ ++ *l2_cache_size = MIN(max_l2_cache, l2_cache_max_setting); ++ ++ if (combined_cache_size_set) { ++ if (l2_cache_size_set && refcount_cache_size_set) { ++ error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE ++ " and " QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not be set " ++ "at the same time"); ++ return false; ++ } else if (l2_cache_size_set && ++ (l2_cache_max_setting > combined_cache_size)) { ++ error_setg(errp, QCOW2_OPT_L2_CACHE_SIZE " may not exceed " ++ QCOW2_OPT_CACHE_SIZE); ++ return false; ++ } else if (*refcount_cache_size > combined_cache_size) { ++ error_setg(errp, QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not exceed " ++ QCOW2_OPT_CACHE_SIZE); ++ return false; ++ } ++ ++ if (l2_cache_size_set) { ++ *refcount_cache_size = combined_cache_size - *l2_cache_size; ++ } else if (refcount_cache_size_set) { ++ *l2_cache_size = combined_cache_size - *refcount_cache_size; ++ } else { ++ /* Assign as much memory as possible to the L2 cache, and ++ * use the remainder for the refcount cache */ ++ if (combined_cache_size >= max_l2_cache + min_refcount_cache) { ++ *l2_cache_size = max_l2_cache; ++ *refcount_cache_size = combined_cache_size - *l2_cache_size; ++ } else { ++ *refcount_cache_size = ++ MIN(combined_cache_size, min_refcount_cache); ++ *l2_cache_size = combined_cache_size - *refcount_cache_size; ++ } ++ } ++ } ++ ++ /* ++ * If the L2 cache is not enough to cover the whole disk then ++ * default to 4KB entries. Smaller entries reduce the cost of ++ * loads and evictions and increase I/O performance. ++ */ ++ if (*l2_cache_size < max_l2_cache && !l2_cache_entry_size_set) { ++ *l2_cache_entry_size = MIN(s->cluster_size, 4096); ++ } ++ ++ /* l2_cache_size and refcount_cache_size are ensured to have at least ++ * their minimum values in qcow2_update_options_prepare() */ ++ ++ if (*l2_cache_entry_size < (1 << MIN_CLUSTER_BITS) || ++ *l2_cache_entry_size > s->cluster_size || ++ !is_power_of_2(*l2_cache_entry_size)) { ++ error_setg(errp, "L2 cache entry size must be a power of two " ++ "between %d and the cluster size (%d)", ++ 1 << MIN_CLUSTER_BITS, s->cluster_size); ++ return false; ++ } ++ ++ return true; ++} ++ ++typedef struct Qcow2ReopenState { ++ Qcow2Cache *l2_table_cache; ++ Qcow2Cache *refcount_block_cache; ++ int l2_slice_size; /* Number of entries in a slice of the L2 table */ ++ bool use_lazy_refcounts; ++ int overlap_check; ++ bool discard_passthrough[QCOW2_DISCARD_MAX]; ++ bool discard_no_unref; ++ uint64_t cache_clean_interval; ++ QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ ++} Qcow2ReopenState; ++ ++static int GRAPH_RDLOCK ++qcow2_update_options_prepare(BlockDriverState *bs, Qcow2ReopenState *r, ++ QDict *options, int flags, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QemuOpts *opts = NULL; ++ const char *opt_overlap_check, *opt_overlap_check_template; ++ int overlap_check_template = 0; ++ uint64_t l2_cache_size, l2_cache_entry_size, refcount_cache_size; ++ int i; ++ const char *encryptfmt; ++ QDict *encryptopts = NULL; ++ int ret; ++ ++ qdict_extract_subqdict(options, &encryptopts, "encrypt."); ++ encryptfmt = qdict_get_try_str(encryptopts, "format"); ++ ++ opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_absorb_qdict(opts, options, errp)) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* get L2 table/refcount block cache size from command line options */ ++ if (!read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size, ++ &refcount_cache_size, errp)) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ l2_cache_size /= l2_cache_entry_size; ++ if (l2_cache_size < MIN_L2_CACHE_SIZE) { ++ l2_cache_size = MIN_L2_CACHE_SIZE; ++ } ++ if (l2_cache_size > INT_MAX) { ++ error_setg(errp, "L2 cache size too big"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ refcount_cache_size /= s->cluster_size; ++ if (refcount_cache_size < MIN_REFCOUNT_CACHE_SIZE) { ++ refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE; ++ } ++ if (refcount_cache_size > INT_MAX) { ++ error_setg(errp, "Refcount cache size too big"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* alloc new L2 table/refcount block cache, flush old one */ ++ if (s->l2_table_cache) { ++ ret = qcow2_cache_flush(bs, s->l2_table_cache); ++ if (ret) { ++ error_setg_errno(errp, -ret, "Failed to flush the L2 table cache"); ++ goto fail; ++ } ++ } ++ ++ if (s->refcount_block_cache) { ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret) { ++ error_setg_errno(errp, -ret, ++ "Failed to flush the refcount block cache"); ++ goto fail; ++ } ++ } ++ ++ r->l2_slice_size = l2_cache_entry_size / l2_entry_size(s); ++ r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size, ++ l2_cache_entry_size); ++ r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size, ++ s->cluster_size); ++ if (r->l2_table_cache == NULL || r->refcount_block_cache == NULL) { ++ error_setg(errp, "Could not allocate metadata caches"); ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ /* New interval for cache cleanup timer */ ++ r->cache_clean_interval = ++ qemu_opt_get_number(opts, QCOW2_OPT_CACHE_CLEAN_INTERVAL, ++ DEFAULT_CACHE_CLEAN_INTERVAL); ++#ifndef CONFIG_LINUX ++ if (r->cache_clean_interval != 0) { ++ error_setg(errp, QCOW2_OPT_CACHE_CLEAN_INTERVAL ++ " not supported on this host"); ++ ret = -EINVAL; ++ goto fail; ++ } ++#endif ++ if (r->cache_clean_interval > UINT_MAX) { ++ error_setg(errp, "Cache clean interval too big"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* lazy-refcounts; flush if going from enabled to disabled */ ++ r->use_lazy_refcounts = qemu_opt_get_bool(opts, QCOW2_OPT_LAZY_REFCOUNTS, ++ (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)); ++ if (r->use_lazy_refcounts && s->qcow_version < 3) { ++ error_setg(errp, "Lazy refcounts require a qcow2 image with at least " ++ "qemu 1.1 compatibility level"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ if (s->use_lazy_refcounts && !r->use_lazy_refcounts) { ++ ret = qcow2_mark_clean(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to disable lazy refcounts"); ++ goto fail; ++ } ++ } ++ ++ /* Overlap check options */ ++ opt_overlap_check = qemu_opt_get(opts, QCOW2_OPT_OVERLAP); ++ opt_overlap_check_template = qemu_opt_get(opts, QCOW2_OPT_OVERLAP_TEMPLATE); ++ if (opt_overlap_check_template && opt_overlap_check && ++ strcmp(opt_overlap_check_template, opt_overlap_check)) ++ { ++ error_setg(errp, "Conflicting values for qcow2 options '" ++ QCOW2_OPT_OVERLAP "' ('%s') and '" QCOW2_OPT_OVERLAP_TEMPLATE ++ "' ('%s')", opt_overlap_check, opt_overlap_check_template); ++ ret = -EINVAL; ++ goto fail; ++ } ++ if (!opt_overlap_check) { ++ opt_overlap_check = opt_overlap_check_template ?: "cached"; ++ } ++ ++ if (!strcmp(opt_overlap_check, "none")) { ++ overlap_check_template = 0; ++ } else if (!strcmp(opt_overlap_check, "constant")) { ++ overlap_check_template = QCOW2_OL_CONSTANT; ++ } else if (!strcmp(opt_overlap_check, "cached")) { ++ overlap_check_template = QCOW2_OL_CACHED; ++ } else if (!strcmp(opt_overlap_check, "all")) { ++ overlap_check_template = QCOW2_OL_ALL; ++ } else { ++ error_setg(errp, "Unsupported value '%s' for qcow2 option " ++ "'overlap-check'. Allowed are any of the following: " ++ "none, constant, cached, all", opt_overlap_check); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ r->overlap_check = 0; ++ for (i = 0; i < QCOW2_OL_MAX_BITNR; i++) { ++ /* overlap-check defines a template bitmask, but every flag may be ++ * overwritten through the associated boolean option */ ++ r->overlap_check |= ++ qemu_opt_get_bool(opts, overlap_bool_option_names[i], ++ overlap_check_template & (1 << i)) << i; ++ } ++ ++ r->discard_passthrough[QCOW2_DISCARD_NEVER] = false; ++ r->discard_passthrough[QCOW2_DISCARD_ALWAYS] = true; ++ r->discard_passthrough[QCOW2_DISCARD_REQUEST] = ++ qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_REQUEST, ++ flags & BDRV_O_UNMAP); ++ r->discard_passthrough[QCOW2_DISCARD_SNAPSHOT] = ++ qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_SNAPSHOT, true); ++ r->discard_passthrough[QCOW2_DISCARD_OTHER] = ++ qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false); ++ ++ r->discard_no_unref = qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_NO_UNREF, ++ false); ++ if (r->discard_no_unref && s->qcow_version < 3) { ++ error_setg(errp, ++ "discard-no-unref is only supported since qcow2 version 3"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ switch (s->crypt_method_header) { ++ case QCOW_CRYPT_NONE: ++ if (encryptfmt) { ++ error_setg(errp, "No encryption in image header, but options " ++ "specified format '%s'", encryptfmt); ++ ret = -EINVAL; ++ goto fail; ++ } ++ break; ++ ++ case QCOW_CRYPT_AES: ++ if (encryptfmt && !g_str_equal(encryptfmt, "aes")) { ++ error_setg(errp, ++ "Header reported 'aes' encryption format but " ++ "options specify '%s'", encryptfmt); ++ ret = -EINVAL; ++ goto fail; ++ } ++ qdict_put_str(encryptopts, "format", "qcow"); ++ r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); ++ if (!r->crypto_opts) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ break; ++ ++ case QCOW_CRYPT_LUKS: ++ if (encryptfmt && !g_str_equal(encryptfmt, "luks")) { ++ error_setg(errp, ++ "Header reported 'luks' encryption format but " ++ "options specify '%s'", encryptfmt); ++ ret = -EINVAL; ++ goto fail; ++ } ++ qdict_put_str(encryptopts, "format", "luks"); ++ r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); ++ if (!r->crypto_opts) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ break; ++ ++ default: ++ error_setg(errp, "Unsupported encryption method %d", ++ s->crypt_method_header); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ ret = 0; ++fail: ++ qobject_unref(encryptopts); ++ qemu_opts_del(opts); ++ opts = NULL; ++ return ret; ++} ++ ++static void qcow2_update_options_commit(BlockDriverState *bs, ++ Qcow2ReopenState *r) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i; ++ ++ if (s->l2_table_cache) { ++ qcow2_cache_destroy(s->l2_table_cache); ++ } ++ if (s->refcount_block_cache) { ++ qcow2_cache_destroy(s->refcount_block_cache); ++ } ++ s->l2_table_cache = r->l2_table_cache; ++ s->refcount_block_cache = r->refcount_block_cache; ++ s->l2_slice_size = r->l2_slice_size; ++ ++ s->overlap_check = r->overlap_check; ++ s->use_lazy_refcounts = r->use_lazy_refcounts; ++ ++ for (i = 0; i < QCOW2_DISCARD_MAX; i++) { ++ s->discard_passthrough[i] = r->discard_passthrough[i]; ++ } ++ ++ s->discard_no_unref = r->discard_no_unref; ++ ++ if (s->cache_clean_interval != r->cache_clean_interval) { ++ cache_clean_timer_del(bs); ++ s->cache_clean_interval = r->cache_clean_interval; ++ cache_clean_timer_init(bs, bdrv_get_aio_context(bs)); ++ } ++ ++ qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); ++ s->crypto_opts = r->crypto_opts; ++} ++ ++static void qcow2_update_options_abort(BlockDriverState *bs, ++ Qcow2ReopenState *r) ++{ ++ if (r->l2_table_cache) { ++ qcow2_cache_destroy(r->l2_table_cache); ++ } ++ if (r->refcount_block_cache) { ++ qcow2_cache_destroy(r->refcount_block_cache); ++ } ++ qapi_free_QCryptoBlockOpenOptions(r->crypto_opts); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_update_options(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ Qcow2ReopenState r = {}; ++ int ret; ++ ++ ret = qcow2_update_options_prepare(bs, &r, options, flags, errp); ++ if (ret >= 0) { ++ qcow2_update_options_commit(bs, &r); ++ } else { ++ qcow2_update_options_abort(bs, &r); ++ } ++ ++ return ret; ++} ++ ++static int validate_compression_type(BDRVQcow2State *s, Error **errp) ++{ ++ switch (s->compression_type) { ++ case QCOW2_COMPRESSION_TYPE_ZLIB: ++#ifdef CONFIG_ZSTD ++ case QCOW2_COMPRESSION_TYPE_ZSTD: ++#endif ++ break; ++ ++ default: ++ error_setg(errp, "qcow2: unknown compression type: %u", ++ s->compression_type); ++ return -ENOTSUP; ++ } ++ ++ /* ++ * if the compression type differs from QCOW2_COMPRESSION_TYPE_ZLIB ++ * the incompatible feature flag must be set ++ */ ++ if (s->compression_type == QCOW2_COMPRESSION_TYPE_ZLIB) { ++ if (s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION) { ++ error_setg(errp, "qcow2: Compression type incompatible feature " ++ "bit must not be set"); ++ return -EINVAL; ++ } ++ } else { ++ if (!(s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION)) { ++ error_setg(errp, "qcow2: Compression type incompatible feature " ++ "bit must be set"); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Called with s->lock held. */ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, ++ bool open_data_file, Error **errp) ++{ ++ ERRP_GUARD(); ++ BDRVQcow2State *s = bs->opaque; ++ unsigned int len, i; ++ int ret = 0; ++ QCowHeader header; ++ uint64_t ext_end; ++ uint64_t l1_vm_state_index; ++ bool update_header = false; ++ ++ ret = bdrv_co_pread(bs->file, 0, sizeof(header), &header, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not read qcow2 header"); ++ goto fail; ++ } ++ header.magic = be32_to_cpu(header.magic); ++ header.version = be32_to_cpu(header.version); ++ header.backing_file_offset = be64_to_cpu(header.backing_file_offset); ++ header.backing_file_size = be32_to_cpu(header.backing_file_size); ++ header.size = be64_to_cpu(header.size); ++ header.cluster_bits = be32_to_cpu(header.cluster_bits); ++ header.crypt_method = be32_to_cpu(header.crypt_method); ++ header.l1_table_offset = be64_to_cpu(header.l1_table_offset); ++ header.l1_size = be32_to_cpu(header.l1_size); ++ header.refcount_table_offset = be64_to_cpu(header.refcount_table_offset); ++ header.refcount_table_clusters = ++ be32_to_cpu(header.refcount_table_clusters); ++ header.snapshots_offset = be64_to_cpu(header.snapshots_offset); ++ header.nb_snapshots = be32_to_cpu(header.nb_snapshots); ++ ++ if (header.magic != QCOW_MAGIC) { ++ error_setg(errp, "Image is not in qcow2 format"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ if (header.version < 2 || header.version > 3) { ++ error_setg(errp, "Unsupported qcow2 version %" PRIu32, header.version); ++ ret = -ENOTSUP; ++ goto fail; ++ } ++ ++ s->qcow_version = header.version; ++ ++ /* Initialise cluster size */ ++ if (header.cluster_bits < MIN_CLUSTER_BITS || ++ header.cluster_bits > MAX_CLUSTER_BITS) { ++ error_setg(errp, "Unsupported cluster size: 2^%" PRIu32, ++ header.cluster_bits); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ s->cluster_bits = header.cluster_bits; ++ s->cluster_size = 1 << s->cluster_bits; ++ ++ /* Initialise version 3 header fields */ ++ if (header.version == 2) { ++ header.incompatible_features = 0; ++ header.compatible_features = 0; ++ header.autoclear_features = 0; ++ header.refcount_order = 4; ++ header.header_length = 72; ++ } else { ++ header.incompatible_features = ++ be64_to_cpu(header.incompatible_features); ++ header.compatible_features = be64_to_cpu(header.compatible_features); ++ header.autoclear_features = be64_to_cpu(header.autoclear_features); ++ header.refcount_order = be32_to_cpu(header.refcount_order); ++ header.header_length = be32_to_cpu(header.header_length); ++ ++ if (header.header_length < 104) { ++ error_setg(errp, "qcow2 header too short"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ } ++ ++ if (header.header_length > s->cluster_size) { ++ error_setg(errp, "qcow2 header exceeds cluster size"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ if (header.header_length > sizeof(header)) { ++ s->unknown_header_fields_size = header.header_length - sizeof(header); ++ s->unknown_header_fields = g_malloc(s->unknown_header_fields_size); ++ ret = bdrv_co_pread(bs->file, sizeof(header), ++ s->unknown_header_fields_size, ++ s->unknown_header_fields, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not read unknown qcow2 header " ++ "fields"); ++ goto fail; ++ } ++ } ++ ++ if (header.backing_file_offset > s->cluster_size) { ++ error_setg(errp, "Invalid backing file offset"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ if (header.backing_file_offset) { ++ ext_end = header.backing_file_offset; ++ } else { ++ ext_end = 1 << header.cluster_bits; ++ } ++ ++ /* Handle feature bits */ ++ s->incompatible_features = header.incompatible_features; ++ s->compatible_features = header.compatible_features; ++ s->autoclear_features = header.autoclear_features; ++ ++ /* ++ * Handle compression type ++ * Older qcow2 images don't contain the compression type header. ++ * Distinguish them by the header length and use ++ * the only valid (default) compression type in that case ++ */ ++ if (header.header_length > offsetof(QCowHeader, compression_type)) { ++ s->compression_type = header.compression_type; ++ } else { ++ s->compression_type = QCOW2_COMPRESSION_TYPE_ZLIB; ++ } ++ ++ ret = validate_compression_type(s, errp); ++ if (ret) { ++ goto fail; ++ } ++ ++ if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) { ++ void *feature_table = NULL; ++ qcow2_read_extensions(bs, header.header_length, ext_end, ++ &feature_table, flags, NULL, NULL); ++ report_unsupported_feature(errp, feature_table, ++ s->incompatible_features & ++ ~QCOW2_INCOMPAT_MASK); ++ ret = -ENOTSUP; ++ g_free(feature_table); ++ goto fail; ++ } ++ ++ if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) { ++ /* Corrupt images may not be written to unless they are being repaired ++ */ ++ if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_CHECK)) { ++ error_setg(errp, "qcow2: Image is corrupt; cannot be opened " ++ "read/write"); ++ ret = -EACCES; ++ goto fail; ++ } ++ } ++ ++ s->subclusters_per_cluster = ++ has_subclusters(s) ? QCOW_EXTL2_SUBCLUSTERS_PER_CLUSTER : 1; ++ s->subcluster_size = s->cluster_size / s->subclusters_per_cluster; ++ s->subcluster_bits = ctz32(s->subcluster_size); ++ ++ if (s->subcluster_size < (1 << MIN_CLUSTER_BITS)) { ++ error_setg(errp, "Unsupported subcluster size: %d", s->subcluster_size); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* Check support for various header values */ ++ if (header.refcount_order > 6) { ++ error_setg(errp, "Reference count entry width too large; may not " ++ "exceed 64 bits"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ s->refcount_order = header.refcount_order; ++ s->refcount_bits = 1 << s->refcount_order; ++ s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1); ++ s->refcount_max += s->refcount_max - 1; ++ ++ s->crypt_method_header = header.crypt_method; ++ if (s->crypt_method_header) { ++ if (bdrv_uses_whitelist() && ++ s->crypt_method_header == QCOW_CRYPT_AES) { ++ error_setg(errp, ++ "Use of AES-CBC encrypted qcow2 images is no longer " ++ "supported in system emulators"); ++ error_append_hint(errp, ++ "You can use 'qemu-img convert' to convert your " ++ "image to an alternative supported format, such " ++ "as unencrypted qcow2, or raw with the LUKS " ++ "format instead.\n"); ++ ret = -ENOSYS; ++ goto fail; ++ } ++ ++ if (s->crypt_method_header == QCOW_CRYPT_AES) { ++ s->crypt_physical_offset = false; ++ } else { ++ /* Assuming LUKS and any future crypt methods we ++ * add will all use physical offsets, due to the ++ * fact that the alternative is insecure... */ ++ s->crypt_physical_offset = true; ++ } ++ ++ bs->encrypted = true; ++ } ++ ++ s->l2_bits = s->cluster_bits - ctz32(l2_entry_size(s)); ++ s->l2_size = 1 << s->l2_bits; ++ /* 2^(s->refcount_order - 3) is the refcount width in bytes */ ++ s->refcount_block_bits = s->cluster_bits - (s->refcount_order - 3); ++ s->refcount_block_size = 1 << s->refcount_block_bits; ++ bs->total_sectors = header.size / BDRV_SECTOR_SIZE; ++ s->csize_shift = (62 - (s->cluster_bits - 8)); ++ s->csize_mask = (1 << (s->cluster_bits - 8)) - 1; ++ s->cluster_offset_mask = (1LL << s->csize_shift) - 1; ++ ++ s->refcount_table_offset = header.refcount_table_offset; ++ s->refcount_table_size = ++ header.refcount_table_clusters << (s->cluster_bits - 3); ++ ++ if (header.refcount_table_clusters == 0 && !(flags & BDRV_O_CHECK)) { ++ error_setg(errp, "Image does not contain a reference count table"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ ret = qcow2_validate_table(bs, s->refcount_table_offset, ++ header.refcount_table_clusters, ++ s->cluster_size, QCOW_MAX_REFTABLE_SIZE, ++ "Reference count table", errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if (!(flags & BDRV_O_CHECK)) { ++ /* ++ * The total size in bytes of the snapshot table is checked in ++ * qcow2_read_snapshots() because the size of each snapshot is ++ * variable and we don't know it yet. ++ * Here we only check the offset and number of snapshots. ++ */ ++ ret = qcow2_validate_table(bs, header.snapshots_offset, ++ header.nb_snapshots, ++ sizeof(QCowSnapshotHeader), ++ sizeof(QCowSnapshotHeader) * ++ QCOW_MAX_SNAPSHOTS, ++ "Snapshot table", errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ /* read the level 1 table */ ++ ret = qcow2_validate_table(bs, header.l1_table_offset, ++ header.l1_size, L1E_SIZE, ++ QCOW_MAX_L1_SIZE, "Active L1 table", errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ s->l1_size = header.l1_size; ++ s->l1_table_offset = header.l1_table_offset; ++ ++ l1_vm_state_index = size_to_l1(s, header.size); ++ if (l1_vm_state_index > INT_MAX) { ++ error_setg(errp, "Image is too big"); ++ ret = -EFBIG; ++ goto fail; ++ } ++ s->l1_vm_state_index = l1_vm_state_index; ++ ++ /* the L1 table must contain at least enough entries to put ++ header.size bytes */ ++ if (s->l1_size < s->l1_vm_state_index) { ++ error_setg(errp, "L1 table is too small"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ if (s->l1_size > 0) { ++ s->l1_table = qemu_try_blockalign(bs->file->bs, s->l1_size * L1E_SIZE); ++ if (s->l1_table == NULL) { ++ error_setg(errp, "Could not allocate L1 table"); ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ret = bdrv_co_pread(bs->file, s->l1_table_offset, s->l1_size * L1E_SIZE, ++ s->l1_table, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not read L1 table"); ++ goto fail; ++ } ++ for(i = 0;i < s->l1_size; i++) { ++ s->l1_table[i] = be64_to_cpu(s->l1_table[i]); ++ } ++ } ++ ++ /* Parse driver-specific options */ ++ ret = qcow2_update_options(bs, options, flags, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ s->flags = flags; ++ ++ ret = qcow2_refcount_init(bs); ++ if (ret != 0) { ++ error_setg_errno(errp, -ret, "Could not initialize refcount handling"); ++ goto fail; ++ } ++ ++ QLIST_INIT(&s->cluster_allocs); ++ QTAILQ_INIT(&s->discards); ++ ++ /* read qcow2 extensions */ ++ if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL, ++ flags, &update_header, errp)) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ if (open_data_file && (flags & BDRV_O_NO_IO)) { ++ /* ++ * Don't open the data file for 'qemu-img info' so that it can be used ++ * to verify that an untrusted qcow2 image doesn't refer to external ++ * files. ++ * ++ * Note: This still makes has_data_file() return true. ++ */ ++ if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) { ++ s->data_file = NULL; ++ } else { ++ s->data_file = bs->file; ++ } ++ qdict_extract_subqdict(options, NULL, "data-file."); ++ qdict_del(options, "data-file"); ++ } else if (open_data_file) { ++ /* Open external data file */ ++ bdrv_graph_co_rdunlock(); ++ s->data_file = bdrv_co_open_child(NULL, options, "data-file", bs, ++ &child_of_bds, BDRV_CHILD_DATA, ++ true, errp); ++ bdrv_graph_co_rdlock(); ++ if (*errp) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) { ++ if (!s->data_file && s->image_data_file) { ++ bdrv_graph_co_rdunlock(); ++ s->data_file = bdrv_co_open_child(s->image_data_file, options, ++ "data-file", bs, ++ &child_of_bds, ++ BDRV_CHILD_DATA, false, errp); ++ bdrv_graph_co_rdlock(); ++ if (!s->data_file) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ } ++ if (!s->data_file) { ++ error_setg(errp, "'data-file' is required for this image"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* No data here */ ++ bs->file->role &= ~BDRV_CHILD_DATA; ++ ++ /* Must succeed because we have given up permissions if anything */ ++ bdrv_child_refresh_perms(bs, bs->file, &error_abort); ++ } else { ++ if (s->data_file) { ++ error_setg(errp, "'data-file' can only be set for images with " ++ "an external data file"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ s->data_file = bs->file; ++ ++ if (data_file_is_raw(bs)) { ++ error_setg(errp, "data-file-raw requires a data file"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ } ++ } ++ ++ /* qcow2_read_extension may have set up the crypto context ++ * if the crypt method needs a header region, some methods ++ * don't need header extensions, so must check here ++ */ ++ if (s->crypt_method_header && !s->crypto) { ++ if (s->crypt_method_header == QCOW_CRYPT_AES) { ++ unsigned int cflags = 0; ++ if (flags & BDRV_O_NO_IO) { ++ cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; ++ } ++ s->crypto = qcrypto_block_open(s->crypto_opts, "encrypt.", ++ NULL, NULL, cflags, errp); ++ if (!s->crypto) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ } else if (!(flags & BDRV_O_NO_IO)) { ++ error_setg(errp, "Missing CRYPTO header for crypt method %d", ++ s->crypt_method_header); ++ ret = -EINVAL; ++ goto fail; ++ } ++ } ++ ++ /* read the backing file name */ ++ if (header.backing_file_offset != 0) { ++ len = header.backing_file_size; ++ if (len > MIN(1023, s->cluster_size - header.backing_file_offset) || ++ len >= sizeof(bs->backing_file)) { ++ error_setg(errp, "Backing file name too long"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ s->image_backing_file = g_malloc(len + 1); ++ ret = bdrv_co_pread(bs->file, header.backing_file_offset, len, ++ s->image_backing_file, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not read backing file name"); ++ goto fail; ++ } ++ s->image_backing_file[len] = '\0'; ++ ++ /* ++ * Update only when something has changed. This function is called by ++ * qcow2_co_invalidate_cache(), and we do not want to reset ++ * auto_backing_file unless necessary. ++ */ ++ if (!g_str_equal(s->image_backing_file, bs->backing_file)) { ++ pstrcpy(bs->backing_file, sizeof(bs->backing_file), ++ s->image_backing_file); ++ pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), ++ s->image_backing_file); ++ } ++ } ++ ++ /* ++ * Internal snapshots; skip reading them in check mode, because ++ * we do not need them then, and we do not want to abort because ++ * of a broken table. ++ */ ++ if (!(flags & BDRV_O_CHECK)) { ++ s->snapshots_offset = header.snapshots_offset; ++ s->nb_snapshots = header.nb_snapshots; ++ ++ ret = qcow2_read_snapshots(bs, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ /* Clear unknown autoclear feature bits */ ++ update_header |= s->autoclear_features & ~QCOW2_AUTOCLEAR_MASK; ++ update_header = update_header && bdrv_is_writable(bs); ++ if (update_header) { ++ s->autoclear_features &= QCOW2_AUTOCLEAR_MASK; ++ } ++ ++ /* == Handle persistent dirty bitmaps == ++ * ++ * We want load dirty bitmaps in three cases: ++ * ++ * 1. Normal open of the disk in active mode, not related to invalidation ++ * after migration. ++ * ++ * 2. Invalidation of the target vm after pre-copy phase of migration, if ++ * bitmaps are _not_ migrating through migration channel, i.e. ++ * 'dirty-bitmaps' capability is disabled. ++ * ++ * 3. Invalidation of source vm after failed or canceled migration. ++ * This is a very interesting case. There are two possible types of ++ * bitmaps: ++ * ++ * A. Stored on inactivation and removed. They should be loaded from the ++ * image. ++ * ++ * B. Not stored: not-persistent bitmaps and bitmaps, migrated through ++ * the migration channel (with dirty-bitmaps capability). ++ * ++ * On the other hand, there are two possible sub-cases: ++ * ++ * 3.1 disk was changed by somebody else while were inactive. In this ++ * case all in-RAM dirty bitmaps (both persistent and not) are ++ * definitely invalid. And we don't have any method to determine ++ * this. ++ * ++ * Simple and safe thing is to just drop all the bitmaps of type B on ++ * inactivation. But in this case we lose bitmaps in valid 4.2 case. ++ * ++ * On the other hand, resuming source vm, if disk was already changed ++ * is a bad thing anyway: not only bitmaps, the whole vm state is ++ * out of sync with disk. ++ * ++ * This means, that user or management tool, who for some reason ++ * decided to resume source vm, after disk was already changed by ++ * target vm, should at least drop all dirty bitmaps by hand. ++ * ++ * So, we can ignore this case for now, but TODO: "generation" ++ * extension for qcow2, to determine, that image was changed after ++ * last inactivation. And if it is changed, we will drop (or at least ++ * mark as 'invalid' all the bitmaps of type B, both persistent ++ * and not). ++ * ++ * 3.2 disk was _not_ changed while were inactive. Bitmaps may be saved ++ * to disk ('dirty-bitmaps' capability disabled), or not saved ++ * ('dirty-bitmaps' capability enabled), but we don't need to care ++ * of: let's load bitmaps as always: stored bitmaps will be loaded, ++ * and not stored has flag IN_USE=1 in the image and will be skipped ++ * on loading. ++ * ++ * One remaining possible case when we don't want load bitmaps: ++ * ++ * 4. Open disk in inactive mode in target vm (bitmaps are migrating or ++ * will be loaded on invalidation, no needs try loading them before) ++ */ ++ ++ if (!(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) { ++ /* It's case 1, 2 or 3.2. Or 3.1 which is BUG in management layer. */ ++ bool header_updated; ++ if (!qcow2_load_dirty_bitmaps(bs, &header_updated, errp)) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ update_header = update_header && !header_updated; ++ } ++ ++ if (update_header) { ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not update qcow2 header"); ++ goto fail; ++ } ++ } ++ ++ bs->supported_zero_flags = header.version >= 3 ? ++ BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK : 0; ++ bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; ++ ++ /* Repair image if dirty */ ++ if (!(flags & BDRV_O_CHECK) && bdrv_is_writable(bs) && ++ (s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) { ++ BdrvCheckResult result = {0}; ++ ++ ret = qcow2_co_check_locked(bs, &result, ++ BDRV_FIX_ERRORS | BDRV_FIX_LEAKS); ++ if (ret < 0 || result.check_errors) { ++ if (ret >= 0) { ++ ret = -EIO; ++ } ++ error_setg_errno(errp, -ret, "Could not repair dirty image"); ++ goto fail; ++ } ++ } ++ ++#ifdef DEBUG_ALLOC ++ { ++ BdrvCheckResult result = {0}; ++ qcow2_check_refcounts(bs, &result, 0); ++ } ++#endif ++ ++ qemu_co_queue_init(&s->thread_task_queue); ++ ++ return ret; ++ ++ fail: ++ g_free(s->image_data_file); ++ if (open_data_file && has_data_file(bs)) { ++ bdrv_graph_co_rdunlock(); ++ bdrv_co_unref_child(bs, s->data_file); ++ bdrv_graph_co_rdlock(); ++ s->data_file = NULL; ++ } ++ g_free(s->unknown_header_fields); ++ cleanup_unknown_header_ext(bs); ++ qcow2_free_snapshots(bs); ++ qcow2_refcount_close(bs); ++ qemu_vfree(s->l1_table); ++ /* else pre-write overlap checks in cache_destroy may crash */ ++ s->l1_table = NULL; ++ cache_clean_timer_del(bs); ++ if (s->l2_table_cache) { ++ qcow2_cache_destroy(s->l2_table_cache); ++ } ++ if (s->refcount_block_cache) { ++ qcow2_cache_destroy(s->refcount_block_cache); ++ } ++ qcrypto_block_free(s->crypto); ++ qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); ++ return ret; ++} ++ ++typedef struct QCow2OpenCo { ++ BlockDriverState *bs; ++ QDict *options; ++ int flags; ++ Error **errp; ++ int ret; ++} QCow2OpenCo; ++ ++static void coroutine_fn qcow2_open_entry(void *opaque) ++{ ++ QCow2OpenCo *qoc = opaque; ++ BDRVQcow2State *s = qoc->bs->opaque; ++ ++ GRAPH_RDLOCK_GUARD(); ++ ++ qemu_co_mutex_lock(&s->lock); ++ qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, true, ++ qoc->errp); ++ qemu_co_mutex_unlock(&s->lock); ++ ++ aio_wait_kick(); ++} ++ ++static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCow2OpenCo qoc = { ++ .bs = bs, ++ .options = options, ++ .flags = flags, ++ .errp = errp, ++ .ret = -EINPROGRESS ++ }; ++ int ret; ++ ++ ret = bdrv_open_file_child(NULL, options, "file", bs, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Initialise locks */ ++ qemu_co_mutex_init(&s->lock); ++ ++ assert(!qemu_in_coroutine()); ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ ++ aio_co_enter(bdrv_get_aio_context(bs), ++ qemu_coroutine_create(qcow2_open_entry, &qoc)); ++ AIO_WAIT_WHILE_UNLOCKED(NULL, qoc.ret == -EINPROGRESS); ++ ++ return qoc.ret; ++} ++ ++static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ if (bs->encrypted) { ++ /* Encryption works on a sector granularity */ ++ bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto); ++ } ++ bs->bl.pwrite_zeroes_alignment = s->subcluster_size; ++ bs->bl.pdiscard_alignment = s->cluster_size; ++} ++ ++static int GRAPH_UNLOCKED ++qcow2_reopen_prepare(BDRVReopenState *state,BlockReopenQueue *queue, ++ Error **errp) ++{ ++ BDRVQcow2State *s = state->bs->opaque; ++ Qcow2ReopenState *r; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ r = g_new0(Qcow2ReopenState, 1); ++ state->opaque = r; ++ ++ ret = qcow2_update_options_prepare(state->bs, r, state->options, ++ state->flags, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* We need to write out any unwritten data if we reopen read-only. */ ++ if ((state->flags & BDRV_O_RDWR) == 0) { ++ ret = qcow2_reopen_bitmaps_ro(state->bs, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = bdrv_flush(state->bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_mark_clean(state->bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ /* ++ * Without an external data file, s->data_file points to the same BdrvChild ++ * as bs->file. It needs to be resynced after reopen because bs->file may ++ * be changed. We can't use it in the meantime. ++ */ ++ if (!has_data_file(state->bs)) { ++ assert(s->data_file == state->bs->file); ++ s->data_file = NULL; ++ } ++ ++ return 0; ++ ++fail: ++ qcow2_update_options_abort(state->bs, r); ++ g_free(r); ++ return ret; ++} ++ ++static void qcow2_reopen_commit(BDRVReopenState *state) ++{ ++ BDRVQcow2State *s = state->bs->opaque; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ qcow2_update_options_commit(state->bs, state->opaque); ++ if (!s->data_file) { ++ /* ++ * If we don't have an external data file, s->data_file was cleared by ++ * qcow2_reopen_prepare() and needs to be updated. ++ */ ++ s->data_file = state->bs->file; ++ } ++ g_free(state->opaque); ++} ++ ++static void qcow2_reopen_commit_post(BDRVReopenState *state) ++{ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (state->flags & BDRV_O_RDWR) { ++ Error *local_err = NULL; ++ ++ if (qcow2_reopen_bitmaps_rw(state->bs, &local_err) < 0) { ++ /* ++ * This is not fatal, bitmaps just left read-only, so all following ++ * writes will fail. User can remove read-only bitmaps to unblock ++ * writes or retry reopen. ++ */ ++ error_reportf_err(local_err, ++ "%s: Failed to make dirty bitmaps writable: ", ++ bdrv_get_node_name(state->bs)); ++ } ++ } ++} ++ ++static void qcow2_reopen_abort(BDRVReopenState *state) ++{ ++ BDRVQcow2State *s = state->bs->opaque; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!s->data_file) { ++ /* ++ * If we don't have an external data file, s->data_file was cleared by ++ * qcow2_reopen_prepare() and needs to be restored. ++ */ ++ s->data_file = state->bs->file; ++ } ++ qcow2_update_options_abort(state->bs, state->opaque); ++ g_free(state->opaque); ++} ++ ++static void qcow2_join_options(QDict *options, QDict *old_options) ++{ ++ bool has_new_overlap_template = ++ qdict_haskey(options, QCOW2_OPT_OVERLAP) || ++ qdict_haskey(options, QCOW2_OPT_OVERLAP_TEMPLATE); ++ bool has_new_total_cache_size = ++ qdict_haskey(options, QCOW2_OPT_CACHE_SIZE); ++ bool has_all_cache_options; ++ ++ /* New overlap template overrides all old overlap options */ ++ if (has_new_overlap_template) { ++ qdict_del(old_options, QCOW2_OPT_OVERLAP); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_TEMPLATE); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_MAIN_HEADER); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_ACTIVE_L1); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_ACTIVE_L2); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_REFCOUNT_TABLE); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_INACTIVE_L1); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_INACTIVE_L2); ++ } ++ ++ /* New total cache size overrides all old options */ ++ if (qdict_haskey(options, QCOW2_OPT_CACHE_SIZE)) { ++ qdict_del(old_options, QCOW2_OPT_L2_CACHE_SIZE); ++ qdict_del(old_options, QCOW2_OPT_REFCOUNT_CACHE_SIZE); ++ } ++ ++ qdict_join(options, old_options, false); ++ ++ /* ++ * If after merging all cache size options are set, an old total size is ++ * overwritten. Do keep all options, however, if all three are new. The ++ * resulting error message is what we want to happen. ++ */ ++ has_all_cache_options = ++ qdict_haskey(options, QCOW2_OPT_CACHE_SIZE) || ++ qdict_haskey(options, QCOW2_OPT_L2_CACHE_SIZE) || ++ qdict_haskey(options, QCOW2_OPT_REFCOUNT_CACHE_SIZE); ++ ++ if (has_all_cache_options && !has_new_total_cache_size) { ++ qdict_del(options, QCOW2_OPT_CACHE_SIZE); ++ } ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, ++ int64_t count, int64_t *pnum, int64_t *map, ++ BlockDriverState **file) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t host_offset; ++ unsigned int bytes; ++ QCow2SubclusterType type; ++ int ret, status = 0; ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++ if (!s->metadata_preallocation_checked) { ++ ret = qcow2_detect_metadata_preallocation(bs); ++ s->metadata_preallocation = (ret == 1); ++ s->metadata_preallocation_checked = true; ++ } ++ ++ bytes = MIN(INT_MAX, count); ++ ret = qcow2_get_host_offset(bs, offset, &bytes, &host_offset, &type); ++ qemu_co_mutex_unlock(&s->lock); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ *pnum = bytes; ++ ++ if ((type == QCOW2_SUBCLUSTER_NORMAL || ++ type == QCOW2_SUBCLUSTER_ZERO_ALLOC || ++ type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC) && !s->crypto) { ++ *map = host_offset; ++ *file = s->data_file->bs; ++ status |= BDRV_BLOCK_OFFSET_VALID; ++ } ++ if (type == QCOW2_SUBCLUSTER_ZERO_PLAIN || ++ type == QCOW2_SUBCLUSTER_ZERO_ALLOC) { ++ status |= BDRV_BLOCK_ZERO; ++ } else if (type != QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN && ++ type != QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC) { ++ status |= BDRV_BLOCK_DATA; ++ } ++ if (s->metadata_preallocation && (status & BDRV_BLOCK_DATA) && ++ (status & BDRV_BLOCK_OFFSET_VALID)) ++ { ++ status |= BDRV_BLOCK_RECURSE; ++ } ++ if (type == QCOW2_SUBCLUSTER_COMPRESSED) { ++ status |= BDRV_BLOCK_COMPRESSED; ++ } ++ return status; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_handle_l2meta(BlockDriverState *bs, QCowL2Meta **pl2meta, bool link_l2) ++{ ++ int ret = 0; ++ QCowL2Meta *l2meta = *pl2meta; ++ ++ while (l2meta != NULL) { ++ QCowL2Meta *next; ++ ++ if (link_l2) { ++ ret = qcow2_alloc_cluster_link_l2(bs, l2meta); ++ if (ret) { ++ goto out; ++ } ++ } else { ++ qcow2_alloc_cluster_abort(bs, l2meta); ++ } ++ ++ /* Take the request off the list of running requests */ ++ QLIST_REMOVE(l2meta, next_in_flight); ++ ++ qemu_co_queue_restart_all(&l2meta->dependent_requests); ++ ++ next = l2meta->next; ++ g_free(l2meta); ++ l2meta = next; ++ } ++out: ++ *pl2meta = l2meta; ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_preadv_encrypted(BlockDriverState *bs, ++ uint64_t host_offset, ++ uint64_t offset, ++ uint64_t bytes, ++ QEMUIOVector *qiov, ++ uint64_t qiov_offset) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ uint8_t *buf; ++ ++ assert(bs->encrypted && s->crypto); ++ assert(bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); ++ ++ /* ++ * For encrypted images, read everything into a temporary ++ * contiguous buffer on which the AES functions can work. ++ * Also, decryption in a separate buffer is better as it ++ * prevents the guest from learning information about the ++ * encrypted nature of the virtual disk. ++ */ ++ ++ buf = qemu_try_blockalign(s->data_file->bs, bytes); ++ if (buf == NULL) { ++ return -ENOMEM; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_AIO); ++ ret = bdrv_co_pread(s->data_file, host_offset, bytes, buf, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if (qcow2_co_decrypt(bs, host_offset, offset, buf, bytes) < 0) ++ { ++ ret = -EIO; ++ goto fail; ++ } ++ qemu_iovec_from_buf(qiov, qiov_offset, buf, bytes); ++ ++fail: ++ qemu_vfree(buf); ++ ++ return ret; ++} ++ ++typedef struct Qcow2AioTask { ++ AioTask task; ++ ++ BlockDriverState *bs; ++ QCow2SubclusterType subcluster_type; /* only for read */ ++ uint64_t host_offset; /* or l2_entry for compressed read */ ++ uint64_t offset; ++ uint64_t bytes; ++ QEMUIOVector *qiov; ++ uint64_t qiov_offset; ++ QCowL2Meta *l2meta; /* only for write */ ++} Qcow2AioTask; ++ ++static coroutine_fn int qcow2_co_preadv_task_entry(AioTask *task); ++static coroutine_fn int qcow2_add_task(BlockDriverState *bs, ++ AioTaskPool *pool, ++ AioTaskFunc func, ++ QCow2SubclusterType subcluster_type, ++ uint64_t host_offset, ++ uint64_t offset, ++ uint64_t bytes, ++ QEMUIOVector *qiov, ++ size_t qiov_offset, ++ QCowL2Meta *l2meta) ++{ ++ Qcow2AioTask local_task; ++ Qcow2AioTask *task = pool ? g_new(Qcow2AioTask, 1) : &local_task; ++ ++ *task = (Qcow2AioTask) { ++ .task.func = func, ++ .bs = bs, ++ .subcluster_type = subcluster_type, ++ .qiov = qiov, ++ .host_offset = host_offset, ++ .offset = offset, ++ .bytes = bytes, ++ .qiov_offset = qiov_offset, ++ .l2meta = l2meta, ++ }; ++ ++ trace_qcow2_add_task(qemu_coroutine_self(), bs, pool, ++ func == qcow2_co_preadv_task_entry ? "read" : "write", ++ subcluster_type, host_offset, offset, bytes, ++ qiov, qiov_offset); ++ ++ if (!pool) { ++ return func(&task->task); ++ } ++ ++ aio_task_pool_start_task(pool, &task->task); ++ ++ return 0; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_preadv_task(BlockDriverState *bs, QCow2SubclusterType subc_type, ++ uint64_t host_offset, uint64_t offset, uint64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ switch (subc_type) { ++ case QCOW2_SUBCLUSTER_ZERO_PLAIN: ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ /* Both zero types are handled in qcow2_co_preadv_part */ ++ g_assert_not_reached(); ++ ++ case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ assert(bs->backing); /* otherwise handled in qcow2_co_preadv_part */ ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); ++ return bdrv_co_preadv_part(bs->backing, offset, bytes, ++ qiov, qiov_offset, 0); ++ ++ case QCOW2_SUBCLUSTER_COMPRESSED: ++ return qcow2_co_preadv_compressed(bs, host_offset, ++ offset, bytes, qiov, qiov_offset); ++ ++ case QCOW2_SUBCLUSTER_NORMAL: ++ if (bs->encrypted) { ++ return qcow2_co_preadv_encrypted(bs, host_offset, ++ offset, bytes, qiov, qiov_offset); ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_AIO); ++ return bdrv_co_preadv_part(s->data_file, host_offset, ++ bytes, qiov, qiov_offset, 0); ++ ++ default: ++ g_assert_not_reached(); ++ } ++ ++ g_assert_not_reached(); ++} ++ ++/* ++ * This function can count as GRAPH_RDLOCK because qcow2_co_preadv_part() holds ++ * the graph lock and keeps it until this coroutine has terminated. ++ */ ++static int coroutine_fn GRAPH_RDLOCK qcow2_co_preadv_task_entry(AioTask *task) ++{ ++ Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); ++ ++ assert(!t->l2meta); ++ ++ return qcow2_co_preadv_task(t->bs, t->subcluster_type, ++ t->host_offset, t->offset, t->bytes, ++ t->qiov, t->qiov_offset); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_preadv_part(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret = 0; ++ unsigned int cur_bytes; /* number of bytes in current iteration */ ++ uint64_t host_offset = 0; ++ QCow2SubclusterType type; ++ AioTaskPool *aio = NULL; ++ ++ while (bytes != 0 && aio_task_pool_status(aio) == 0) { ++ /* prepare next request */ ++ cur_bytes = MIN(bytes, INT_MAX); ++ if (s->crypto) { ++ cur_bytes = MIN(cur_bytes, ++ QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ret = qcow2_get_host_offset(bs, offset, &cur_bytes, ++ &host_offset, &type); ++ qemu_co_mutex_unlock(&s->lock); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ if (type == QCOW2_SUBCLUSTER_ZERO_PLAIN || ++ type == QCOW2_SUBCLUSTER_ZERO_ALLOC || ++ (type == QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN && !bs->backing) || ++ (type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC && !bs->backing)) ++ { ++ qemu_iovec_memset(qiov, qiov_offset, 0, cur_bytes); ++ } else { ++ if (!aio && cur_bytes != bytes) { ++ aio = aio_task_pool_new(QCOW2_MAX_WORKERS); ++ } ++ ret = qcow2_add_task(bs, aio, qcow2_co_preadv_task_entry, type, ++ host_offset, offset, cur_bytes, ++ qiov, qiov_offset, NULL); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ ++ bytes -= cur_bytes; ++ offset += cur_bytes; ++ qiov_offset += cur_bytes; ++ } ++ ++out: ++ if (aio) { ++ aio_task_pool_wait_all(aio); ++ if (ret == 0) { ++ ret = aio_task_pool_status(aio); ++ } ++ g_free(aio); ++ } ++ ++ return ret; ++} ++ ++/* Check if it's possible to merge a write request with the writing of ++ * the data from the COW regions */ ++static bool merge_cow(uint64_t offset, unsigned bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ QCowL2Meta *l2meta) ++{ ++ QCowL2Meta *m; ++ ++ for (m = l2meta; m != NULL; m = m->next) { ++ /* If both COW regions are empty then there's nothing to merge */ ++ if (m->cow_start.nb_bytes == 0 && m->cow_end.nb_bytes == 0) { ++ continue; ++ } ++ ++ /* If COW regions are handled already, skip this too */ ++ if (m->skip_cow) { ++ continue; ++ } ++ ++ /* ++ * The write request should start immediately after the first ++ * COW region. This does not always happen because the area ++ * touched by the request can be larger than the one defined ++ * by @m (a single request can span an area consisting of a ++ * mix of previously unallocated and allocated clusters, that ++ * is why @l2meta is a list). ++ */ ++ if (l2meta_cow_start(m) + m->cow_start.nb_bytes != offset) { ++ /* In this case the request starts before this region */ ++ assert(offset < l2meta_cow_start(m)); ++ assert(m->cow_start.nb_bytes == 0); ++ continue; ++ } ++ ++ /* The write request should end immediately before the second ++ * COW region (see above for why it does not always happen) */ ++ if (m->offset + m->cow_end.offset != offset + bytes) { ++ assert(offset + bytes > m->offset + m->cow_end.offset); ++ assert(m->cow_end.nb_bytes == 0); ++ continue; ++ } ++ ++ /* Make sure that adding both COW regions to the QEMUIOVector ++ * does not exceed IOV_MAX */ ++ if (qemu_iovec_subvec_niov(qiov, qiov_offset, bytes) > IOV_MAX - 2) { ++ continue; ++ } ++ ++ m->data_qiov = qiov; ++ m->data_qiov_offset = qiov_offset; ++ return true; ++ } ++ ++ return false; ++} ++ ++/* ++ * Return 1 if the COW regions read as zeroes, 0 if not, < 0 on error. ++ * Note that returning 0 does not guarantee non-zero data. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++is_zero_cow(BlockDriverState *bs, QCowL2Meta *m) ++{ ++ /* ++ * This check is designed for optimization shortcut so it must be ++ * efficient. ++ * Instead of is_zero(), use bdrv_co_is_zero_fast() as it is ++ * faster (but not as accurate and can result in false negatives). ++ */ ++ int ret = bdrv_co_is_zero_fast(bs, m->offset + m->cow_start.offset, ++ m->cow_start.nb_bytes); ++ if (ret <= 0) { ++ return ret; ++ } ++ ++ return bdrv_co_is_zero_fast(bs, m->offset + m->cow_end.offset, ++ m->cow_end.nb_bytes); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowL2Meta *m; ++ ++ if (!(s->data_file->bs->supported_zero_flags & BDRV_REQ_NO_FALLBACK)) { ++ return 0; ++ } ++ ++ if (bs->encrypted) { ++ return 0; ++ } ++ ++ for (m = l2meta; m != NULL; m = m->next) { ++ int ret; ++ uint64_t start_offset = m->alloc_offset + m->cow_start.offset; ++ unsigned nb_bytes = m->cow_end.offset + m->cow_end.nb_bytes - ++ m->cow_start.offset; ++ ++ if (!m->cow_start.nb_bytes && !m->cow_end.nb_bytes) { ++ continue; ++ } ++ ++ ret = is_zero_cow(bs, m); ++ if (ret < 0) { ++ return ret; ++ } else if (ret == 0) { ++ continue; ++ } ++ ++ /* ++ * instead of writing zero COW buffers, ++ * efficiently zero out the whole clusters ++ */ ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, start_offset, nb_bytes, ++ true); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_SPACE); ++ ret = bdrv_co_pwrite_zeroes(s->data_file, start_offset, nb_bytes, ++ BDRV_REQ_NO_FALLBACK); ++ if (ret < 0) { ++ if (ret != -ENOTSUP && ret != -EAGAIN) { ++ return ret; ++ } ++ continue; ++ } ++ ++ trace_qcow2_skip_cow(qemu_coroutine_self(), m->offset, m->nb_clusters); ++ m->skip_cow = true; ++ } ++ return 0; ++} ++ ++/* ++ * qcow2_co_pwritev_task ++ * Called with s->lock unlocked ++ * l2meta - if not NULL, qcow2_co_pwritev_task() will consume it. Caller must ++ * not use it somehow after qcow2_co_pwritev_task() call ++ */ ++static coroutine_fn GRAPH_RDLOCK ++int qcow2_co_pwritev_task(BlockDriverState *bs, uint64_t host_offset, ++ uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, ++ uint64_t qiov_offset, QCowL2Meta *l2meta) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ void *crypt_buf = NULL; ++ QEMUIOVector encrypted_qiov; ++ ++ if (bs->encrypted) { ++ assert(s->crypto); ++ assert(bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); ++ crypt_buf = qemu_try_blockalign(bs->file->bs, bytes); ++ if (crypt_buf == NULL) { ++ ret = -ENOMEM; ++ goto out_unlocked; ++ } ++ qemu_iovec_to_buf(qiov, qiov_offset, crypt_buf, bytes); ++ ++ if (qcow2_co_encrypt(bs, host_offset, offset, crypt_buf, bytes) < 0) { ++ ret = -EIO; ++ goto out_unlocked; ++ } ++ ++ qemu_iovec_init_buf(&encrypted_qiov, crypt_buf, bytes); ++ qiov = &encrypted_qiov; ++ qiov_offset = 0; ++ } ++ ++ /* Try to efficiently initialize the physical space with zeroes */ ++ ret = handle_alloc_space(bs, l2meta); ++ if (ret < 0) { ++ goto out_unlocked; ++ } ++ ++ /* ++ * If we need to do COW, check if it's possible to merge the ++ * writing of the guest data together with that of the COW regions. ++ * If it's not possible (or not necessary) then write the ++ * guest data now. ++ */ ++ if (!merge_cow(offset, bytes, qiov, qiov_offset, l2meta)) { ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); ++ trace_qcow2_writev_data(qemu_coroutine_self(), host_offset); ++ ret = bdrv_co_pwritev_part(s->data_file, host_offset, ++ bytes, qiov, qiov_offset, 0); ++ if (ret < 0) { ++ goto out_unlocked; ++ } ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++ ret = qcow2_handle_l2meta(bs, &l2meta, true); ++ goto out_locked; ++ ++out_unlocked: ++ qemu_co_mutex_lock(&s->lock); ++ ++out_locked: ++ qcow2_handle_l2meta(bs, &l2meta, false); ++ qemu_co_mutex_unlock(&s->lock); ++ ++ qemu_vfree(crypt_buf); ++ ++ return ret; ++} ++ ++/* ++ * This function can count as GRAPH_RDLOCK because qcow2_co_pwritev_part() holds ++ * the graph lock and keeps it until this coroutine has terminated. ++ */ ++static coroutine_fn GRAPH_RDLOCK int qcow2_co_pwritev_task_entry(AioTask *task) ++{ ++ Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); ++ ++ assert(!t->subcluster_type); ++ ++ return qcow2_co_pwritev_task(t->bs, t->host_offset, ++ t->offset, t->bytes, t->qiov, t->qiov_offset, ++ t->l2meta); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int offset_in_cluster; ++ int ret; ++ unsigned int cur_bytes; /* number of sectors in current iteration */ ++ uint64_t host_offset; ++ QCowL2Meta *l2meta = NULL; ++ AioTaskPool *aio = NULL; ++ ++ trace_qcow2_writev_start_req(qemu_coroutine_self(), offset, bytes); ++ ++ while (bytes != 0 && aio_task_pool_status(aio) == 0) { ++ ++ l2meta = NULL; ++ ++ trace_qcow2_writev_start_part(qemu_coroutine_self()); ++ offset_in_cluster = offset_into_cluster(s, offset); ++ cur_bytes = MIN(bytes, INT_MAX); ++ if (bs->encrypted) { ++ cur_bytes = MIN(cur_bytes, ++ QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size ++ - offset_in_cluster); ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++ ret = qcow2_alloc_host_offset(bs, offset, &cur_bytes, ++ &host_offset, &l2meta); ++ if (ret < 0) { ++ goto out_locked; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, host_offset, ++ cur_bytes, true); ++ if (ret < 0) { ++ goto out_locked; ++ } ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ++ if (!aio && cur_bytes != bytes) { ++ aio = aio_task_pool_new(QCOW2_MAX_WORKERS); ++ } ++ ret = qcow2_add_task(bs, aio, qcow2_co_pwritev_task_entry, 0, ++ host_offset, offset, ++ cur_bytes, qiov, qiov_offset, l2meta); ++ l2meta = NULL; /* l2meta is consumed by qcow2_co_pwritev_task() */ ++ if (ret < 0) { ++ goto fail_nometa; ++ } ++ ++ bytes -= cur_bytes; ++ offset += cur_bytes; ++ qiov_offset += cur_bytes; ++ trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_bytes); ++ } ++ ret = 0; ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++out_locked: ++ qcow2_handle_l2meta(bs, &l2meta, false); ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ++fail_nometa: ++ if (aio) { ++ aio_task_pool_wait_all(aio); ++ if (ret == 0) { ++ ret = aio_task_pool_status(aio); ++ } ++ g_free(aio); ++ } ++ ++ trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); ++ ++ return ret; ++} ++ ++static int GRAPH_RDLOCK qcow2_inactivate(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret, result = 0; ++ Error *local_err = NULL; ++ ++ qcow2_store_persistent_dirty_bitmaps(bs, true, &local_err); ++ if (local_err != NULL) { ++ result = -EINVAL; ++ error_reportf_err(local_err, "Lost persistent bitmaps during " ++ "inactivation of node '%s': ", ++ bdrv_get_device_or_node_name(bs)); ++ } ++ ++ ret = qcow2_cache_flush(bs, s->l2_table_cache); ++ if (ret) { ++ result = ret; ++ error_report("Failed to flush the L2 table cache: %s", ++ strerror(-ret)); ++ } ++ ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret) { ++ result = ret; ++ error_report("Failed to flush the refcount block cache: %s", ++ strerror(-ret)); ++ } ++ ++ if (result == 0) { ++ qcow2_mark_clean(bs); ++ } ++ ++ return result; ++} ++ ++static void coroutine_mixed_fn GRAPH_RDLOCK ++qcow2_do_close(BlockDriverState *bs, bool close_data_file) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ qemu_vfree(s->l1_table); ++ /* else pre-write overlap checks in cache_destroy may crash */ ++ s->l1_table = NULL; ++ ++ if (!(s->flags & BDRV_O_INACTIVE)) { ++ qcow2_inactivate(bs); ++ } ++ ++ cache_clean_timer_del(bs); ++ qcow2_cache_destroy(s->l2_table_cache); ++ qcow2_cache_destroy(s->refcount_block_cache); ++ ++ qcrypto_block_free(s->crypto); ++ s->crypto = NULL; ++ qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); ++ ++ g_free(s->unknown_header_fields); ++ cleanup_unknown_header_ext(bs); ++ ++ g_free(s->image_data_file); ++ g_free(s->image_backing_file); ++ g_free(s->image_backing_format); ++ ++ if (close_data_file && has_data_file(bs)) { ++ GLOBAL_STATE_CODE(); ++ bdrv_graph_rdunlock_main_loop(); ++ bdrv_graph_wrlock(); ++ bdrv_unref_child(bs, s->data_file); ++ bdrv_graph_wrunlock(); ++ s->data_file = NULL; ++ bdrv_graph_rdlock_main_loop(); ++ } ++ ++ qcow2_refcount_close(bs); ++ qcow2_free_snapshots(bs); ++} ++ ++static void GRAPH_UNLOCKED qcow2_close(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ qcow2_do_close(bs, true); ++} ++ ++static void coroutine_fn GRAPH_RDLOCK ++qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp) ++{ ++ ERRP_GUARD(); ++ BDRVQcow2State *s = bs->opaque; ++ BdrvChild *data_file; ++ int flags = s->flags; ++ QCryptoBlock *crypto = NULL; ++ QDict *options; ++ int ret; ++ ++ /* ++ * Backing files are read-only which makes all of their metadata immutable, ++ * that means we don't have to worry about reopening them here. ++ */ ++ ++ crypto = s->crypto; ++ s->crypto = NULL; ++ ++ /* ++ * Do not reopen s->data_file (i.e., have qcow2_do_close() not close it, ++ * and then prevent qcow2_do_open() from opening it), because this function ++ * runs in the I/O path and as such we must not invoke global-state ++ * functions like bdrv_unref_child() and bdrv_open_child(). ++ */ ++ ++ qcow2_do_close(bs, false); ++ ++ data_file = s->data_file; ++ memset(s, 0, sizeof(BDRVQcow2State)); ++ s->data_file = data_file; ++ ++ options = qdict_clone_shallow(bs->options); ++ ++ flags &= ~BDRV_O_INACTIVE; ++ qemu_co_mutex_lock(&s->lock); ++ ret = qcow2_do_open(bs, options, flags, false, errp); ++ qemu_co_mutex_unlock(&s->lock); ++ qobject_unref(options); ++ if (ret < 0) { ++ error_prepend(errp, "Could not reopen qcow2 layer: "); ++ bs->drv = NULL; ++ return; ++ } ++ ++ s->crypto = crypto; ++} ++ ++static size_t header_ext_add(char *buf, uint32_t magic, const void *s, ++ size_t len, size_t buflen) ++{ ++ QCowExtension *ext_backing_fmt = (QCowExtension*) buf; ++ size_t ext_len = sizeof(QCowExtension) + ((len + 7) & ~7); ++ ++ if (buflen < ext_len) { ++ return -ENOSPC; ++ } ++ ++ *ext_backing_fmt = (QCowExtension) { ++ .magic = cpu_to_be32(magic), ++ .len = cpu_to_be32(len), ++ }; ++ ++ if (len) { ++ memcpy(buf + sizeof(QCowExtension), s, len); ++ } ++ ++ return ext_len; ++} ++ ++/* ++ * Updates the qcow2 header, including the variable length parts of it, i.e. ++ * the backing file name and all extensions. qcow2 was not designed to allow ++ * such changes, so if we run out of space (we can only use the first cluster) ++ * this function may fail. ++ * ++ * Returns 0 on success, -errno in error cases. ++ */ ++int qcow2_update_header(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowHeader *header; ++ char *buf; ++ size_t buflen = s->cluster_size; ++ int ret; ++ uint64_t total_size; ++ uint32_t refcount_table_clusters; ++ size_t header_length; ++ Qcow2UnknownHeaderExtension *uext; ++ ++ buf = qemu_blockalign(bs, buflen); ++ ++ /* Header structure */ ++ header = (QCowHeader*) buf; ++ ++ if (buflen < sizeof(*header)) { ++ ret = -ENOSPC; ++ goto fail; ++ } ++ ++ header_length = sizeof(*header) + s->unknown_header_fields_size; ++ total_size = bs->total_sectors * BDRV_SECTOR_SIZE; ++ refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3); ++ ++ ret = validate_compression_type(s, NULL); ++ if (ret) { ++ goto fail; ++ } ++ ++ *header = (QCowHeader) { ++ /* Version 2 fields */ ++ .magic = cpu_to_be32(QCOW_MAGIC), ++ .version = cpu_to_be32(s->qcow_version), ++ .backing_file_offset = 0, ++ .backing_file_size = 0, ++ .cluster_bits = cpu_to_be32(s->cluster_bits), ++ .size = cpu_to_be64(total_size), ++ .crypt_method = cpu_to_be32(s->crypt_method_header), ++ .l1_size = cpu_to_be32(s->l1_size), ++ .l1_table_offset = cpu_to_be64(s->l1_table_offset), ++ .refcount_table_offset = cpu_to_be64(s->refcount_table_offset), ++ .refcount_table_clusters = cpu_to_be32(refcount_table_clusters), ++ .nb_snapshots = cpu_to_be32(s->nb_snapshots), ++ .snapshots_offset = cpu_to_be64(s->snapshots_offset), ++ ++ /* Version 3 fields */ ++ .incompatible_features = cpu_to_be64(s->incompatible_features), ++ .compatible_features = cpu_to_be64(s->compatible_features), ++ .autoclear_features = cpu_to_be64(s->autoclear_features), ++ .refcount_order = cpu_to_be32(s->refcount_order), ++ .header_length = cpu_to_be32(header_length), ++ .compression_type = s->compression_type, ++ }; ++ ++ /* For older versions, write a shorter header */ ++ switch (s->qcow_version) { ++ case 2: ++ ret = offsetof(QCowHeader, incompatible_features); ++ break; ++ case 3: ++ ret = sizeof(*header); ++ break; ++ default: ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ buf += ret; ++ buflen -= ret; ++ memset(buf, 0, buflen); ++ ++ /* Preserve any unknown field in the header */ ++ if (s->unknown_header_fields_size) { ++ if (buflen < s->unknown_header_fields_size) { ++ ret = -ENOSPC; ++ goto fail; ++ } ++ ++ memcpy(buf, s->unknown_header_fields, s->unknown_header_fields_size); ++ buf += s->unknown_header_fields_size; ++ buflen -= s->unknown_header_fields_size; ++ } ++ ++ /* Backing file format header extension */ ++ if (s->image_backing_format) { ++ ret = header_ext_add(buf, QCOW2_EXT_MAGIC_BACKING_FORMAT, ++ s->image_backing_format, ++ strlen(s->image_backing_format), ++ buflen); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ buf += ret; ++ buflen -= ret; ++ } ++ ++ /* External data file header extension */ ++ if (has_data_file(bs) && s->image_data_file) { ++ ret = header_ext_add(buf, QCOW2_EXT_MAGIC_DATA_FILE, ++ s->image_data_file, strlen(s->image_data_file), ++ buflen); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ buf += ret; ++ buflen -= ret; ++ } ++ ++ /* Full disk encryption header pointer extension */ ++ if (s->crypto_header.offset != 0) { ++ s->crypto_header.offset = cpu_to_be64(s->crypto_header.offset); ++ s->crypto_header.length = cpu_to_be64(s->crypto_header.length); ++ ret = header_ext_add(buf, QCOW2_EXT_MAGIC_CRYPTO_HEADER, ++ &s->crypto_header, sizeof(s->crypto_header), ++ buflen); ++ s->crypto_header.offset = be64_to_cpu(s->crypto_header.offset); ++ s->crypto_header.length = be64_to_cpu(s->crypto_header.length); ++ if (ret < 0) { ++ goto fail; ++ } ++ buf += ret; ++ buflen -= ret; ++ } ++ ++ /* ++ * Feature table. A mere 8 feature names occupies 392 bytes, and ++ * when coupled with the v3 minimum header of 104 bytes plus the ++ * 8-byte end-of-extension marker, that would leave only 8 bytes ++ * for a backing file name in an image with 512-byte clusters. ++ * Thus, we choose to omit this header for cluster sizes 4k and ++ * smaller. ++ */ ++ if (s->qcow_version >= 3 && s->cluster_size > 4096) { ++ static const Qcow2Feature features[] = { ++ { ++ .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, ++ .bit = QCOW2_INCOMPAT_DIRTY_BITNR, ++ .name = "dirty bit", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, ++ .bit = QCOW2_INCOMPAT_CORRUPT_BITNR, ++ .name = "corrupt bit", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, ++ .bit = QCOW2_INCOMPAT_DATA_FILE_BITNR, ++ .name = "external data file", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, ++ .bit = QCOW2_INCOMPAT_COMPRESSION_BITNR, ++ .name = "compression type", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, ++ .bit = QCOW2_INCOMPAT_EXTL2_BITNR, ++ .name = "extended L2 entries", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_COMPATIBLE, ++ .bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, ++ .name = "lazy refcounts", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_AUTOCLEAR, ++ .bit = QCOW2_AUTOCLEAR_BITMAPS_BITNR, ++ .name = "bitmaps", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_AUTOCLEAR, ++ .bit = QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR, ++ .name = "raw external data", ++ }, ++ }; ++ ++ ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE, ++ features, sizeof(features), buflen); ++ if (ret < 0) { ++ goto fail; ++ } ++ buf += ret; ++ buflen -= ret; ++ } ++ ++ /* Bitmap extension */ ++ if (s->nb_bitmaps > 0) { ++ Qcow2BitmapHeaderExt bitmaps_header = { ++ .nb_bitmaps = cpu_to_be32(s->nb_bitmaps), ++ .bitmap_directory_size = ++ cpu_to_be64(s->bitmap_directory_size), ++ .bitmap_directory_offset = ++ cpu_to_be64(s->bitmap_directory_offset) ++ }; ++ ret = header_ext_add(buf, QCOW2_EXT_MAGIC_BITMAPS, ++ &bitmaps_header, sizeof(bitmaps_header), ++ buflen); ++ if (ret < 0) { ++ goto fail; ++ } ++ buf += ret; ++ buflen -= ret; ++ } ++ ++ /* Keep unknown header extensions */ ++ QLIST_FOREACH(uext, &s->unknown_header_ext, next) { ++ ret = header_ext_add(buf, uext->magic, uext->data, uext->len, buflen); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ buf += ret; ++ buflen -= ret; ++ } ++ ++ /* End of header extensions */ ++ ret = header_ext_add(buf, QCOW2_EXT_MAGIC_END, NULL, 0, buflen); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ buf += ret; ++ buflen -= ret; ++ ++ /* Backing file name */ ++ if (s->image_backing_file) { ++ size_t backing_file_len = strlen(s->image_backing_file); ++ ++ if (buflen < backing_file_len) { ++ ret = -ENOSPC; ++ goto fail; ++ } ++ ++ /* Using strncpy is ok here, since buf is not NUL-terminated. */ ++ strncpy(buf, s->image_backing_file, buflen); ++ ++ header->backing_file_offset = cpu_to_be64(buf - ((char*) header)); ++ header->backing_file_size = cpu_to_be32(backing_file_len); ++ } ++ ++ /* Write the new header */ ++ ret = bdrv_pwrite(bs->file, 0, s->cluster_size, header, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = 0; ++fail: ++ qemu_vfree(header); ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_change_backing_file(BlockDriverState *bs, const char *backing_file, ++ const char *backing_fmt) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ /* Adding a backing file means that the external data file alone won't be ++ * enough to make sense of the content */ ++ if (backing_file && data_file_is_raw(bs)) { ++ return -EINVAL; ++ } ++ ++ if (backing_file && strlen(backing_file) > 1023) { ++ return -EINVAL; ++ } ++ ++ pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), ++ backing_file ?: ""); ++ pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: ""); ++ pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: ""); ++ ++ g_free(s->image_backing_file); ++ g_free(s->image_backing_format); ++ ++ s->image_backing_file = backing_file ? g_strdup(bs->backing_file) : NULL; ++ s->image_backing_format = backing_fmt ? g_strdup(bs->backing_format) : NULL; ++ ++ return qcow2_update_header(bs); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_set_up_encryption(BlockDriverState *bs, ++ QCryptoBlockCreateOptions *cryptoopts, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCryptoBlock *crypto = NULL; ++ int fmt, ret; ++ ++ switch (cryptoopts->format) { ++ case Q_CRYPTO_BLOCK_FORMAT_LUKS: ++ fmt = QCOW_CRYPT_LUKS; ++ break; ++ case Q_CRYPTO_BLOCK_FORMAT_QCOW: ++ fmt = QCOW_CRYPT_AES; ++ break; ++ default: ++ error_setg(errp, "Crypto format not supported in qcow2"); ++ return -EINVAL; ++ } ++ ++ s->crypt_method_header = fmt; ++ ++ crypto = qcrypto_block_create(cryptoopts, "encrypt.", ++ qcow2_crypto_hdr_init_func, ++ qcow2_crypto_hdr_write_func, ++ bs, 0, errp); ++ if (!crypto) { ++ return -EINVAL; ++ } ++ ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not write encryption header"); ++ goto out; ++ } ++ ++ ret = 0; ++ out: ++ qcrypto_block_free(crypto); ++ return ret; ++} ++ ++/** ++ * Preallocates metadata structures for data clusters between @offset (in the ++ * guest disk) and @new_length (which is thus generally the new guest disk ++ * size). ++ * ++ * Returns: 0 on success, -errno on failure. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++preallocate_co(BlockDriverState *bs, uint64_t offset, uint64_t new_length, ++ PreallocMode mode, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t bytes; ++ uint64_t host_offset = 0; ++ int64_t file_length; ++ unsigned int cur_bytes; ++ int ret; ++ QCowL2Meta *meta = NULL, *m; ++ ++ assert(offset <= new_length); ++ bytes = new_length - offset; ++ ++ while (bytes) { ++ cur_bytes = MIN(bytes, QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size)); ++ ret = qcow2_alloc_host_offset(bs, offset, &cur_bytes, ++ &host_offset, &meta); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Allocating clusters failed"); ++ goto out; ++ } ++ ++ for (m = meta; m != NULL; m = m->next) { ++ m->prealloc = true; ++ } ++ ++ ret = qcow2_handle_l2meta(bs, &meta, true); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Mapping clusters failed"); ++ goto out; ++ } ++ ++ /* TODO Preallocate data if requested */ ++ ++ bytes -= cur_bytes; ++ offset += cur_bytes; ++ } ++ ++ /* ++ * It is expected that the image file is large enough to actually contain ++ * all of the allocated clusters (otherwise we get failing reads after ++ * EOF). Extend the image to the last allocated sector. ++ */ ++ file_length = bdrv_co_getlength(s->data_file->bs); ++ if (file_length < 0) { ++ error_setg_errno(errp, -file_length, "Could not get file size"); ++ ret = file_length; ++ goto out; ++ } ++ ++ if (host_offset + cur_bytes > file_length) { ++ if (mode == PREALLOC_MODE_METADATA) { ++ mode = PREALLOC_MODE_OFF; ++ } ++ ret = bdrv_co_truncate(s->data_file, host_offset + cur_bytes, false, ++ mode, 0, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ ++ ret = 0; ++ ++out: ++ qcow2_handle_l2meta(bs, &meta, false); ++ return ret; ++} ++ ++/* qcow2_refcount_metadata_size: ++ * @clusters: number of clusters to refcount (including data and L1/L2 tables) ++ * @cluster_size: size of a cluster, in bytes ++ * @refcount_order: refcount bits power-of-2 exponent ++ * @generous_increase: allow for the refcount table to be 1.5x as large as it ++ * needs to be ++ * ++ * Returns: Number of bytes required for refcount blocks and table metadata. ++ */ ++int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size, ++ int refcount_order, bool generous_increase, ++ uint64_t *refblock_count) ++{ ++ /* ++ * Every host cluster is reference-counted, including metadata (even ++ * refcount metadata is recursively included). ++ * ++ * An accurate formula for the size of refcount metadata size is difficult ++ * to derive. An easier method of calculation is finding the fixed point ++ * where no further refcount blocks or table clusters are required to ++ * reference count every cluster. ++ */ ++ int64_t blocks_per_table_cluster = cluster_size / REFTABLE_ENTRY_SIZE; ++ int64_t refcounts_per_block = cluster_size * 8 / (1 << refcount_order); ++ int64_t table = 0; /* number of refcount table clusters */ ++ int64_t blocks = 0; /* number of refcount block clusters */ ++ int64_t last; ++ int64_t n = 0; ++ ++ do { ++ last = n; ++ blocks = DIV_ROUND_UP(clusters + table + blocks, refcounts_per_block); ++ table = DIV_ROUND_UP(blocks, blocks_per_table_cluster); ++ n = clusters + blocks + table; ++ ++ if (n == last && generous_increase) { ++ clusters += DIV_ROUND_UP(table, 2); ++ n = 0; /* force another loop */ ++ generous_increase = false; ++ } ++ } while (n != last); ++ ++ if (refblock_count) { ++ *refblock_count = blocks; ++ } ++ ++ return (blocks + table) * cluster_size; ++} ++ ++/** ++ * qcow2_calc_prealloc_size: ++ * @total_size: virtual disk size in bytes ++ * @cluster_size: cluster size in bytes ++ * @refcount_order: refcount bits power-of-2 exponent ++ * @extended_l2: true if the image has extended L2 entries ++ * ++ * Returns: Total number of bytes required for the fully allocated image ++ * (including metadata). ++ */ ++static int64_t qcow2_calc_prealloc_size(int64_t total_size, ++ size_t cluster_size, ++ int refcount_order, ++ bool extended_l2) ++{ ++ int64_t meta_size = 0; ++ uint64_t nl1e, nl2e; ++ int64_t aligned_total_size = ROUND_UP(total_size, cluster_size); ++ size_t l2e_size = extended_l2 ? L2E_SIZE_EXTENDED : L2E_SIZE_NORMAL; ++ ++ /* header: 1 cluster */ ++ meta_size += cluster_size; ++ ++ /* total size of L2 tables */ ++ nl2e = aligned_total_size / cluster_size; ++ nl2e = ROUND_UP(nl2e, cluster_size / l2e_size); ++ meta_size += nl2e * l2e_size; ++ ++ /* total size of L1 tables */ ++ nl1e = nl2e * l2e_size / cluster_size; ++ nl1e = ROUND_UP(nl1e, cluster_size / L1E_SIZE); ++ meta_size += nl1e * L1E_SIZE; ++ ++ /* total size of refcount table and blocks */ ++ meta_size += qcow2_refcount_metadata_size( ++ (meta_size + aligned_total_size) / cluster_size, ++ cluster_size, refcount_order, false, NULL); ++ ++ return meta_size + aligned_total_size; ++} ++ ++static bool validate_cluster_size(size_t cluster_size, bool extended_l2, ++ Error **errp) ++{ ++ int cluster_bits = ctz32(cluster_size); ++ if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS || ++ (1 << cluster_bits) != cluster_size) ++ { ++ error_setg(errp, "Cluster size must be a power of two between %d and " ++ "%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10)); ++ return false; ++ } ++ ++ if (extended_l2) { ++ unsigned min_cluster_size = ++ (1 << MIN_CLUSTER_BITS) * QCOW_EXTL2_SUBCLUSTERS_PER_CLUSTER; ++ if (cluster_size < min_cluster_size) { ++ error_setg(errp, "Extended L2 entries are only supported with " ++ "cluster sizes of at least %u bytes", min_cluster_size); ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, bool extended_l2, ++ Error **errp) ++{ ++ size_t cluster_size; ++ ++ cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, ++ DEFAULT_CLUSTER_SIZE); ++ if (!validate_cluster_size(cluster_size, extended_l2, errp)) { ++ return 0; ++ } ++ return cluster_size; ++} ++ ++static int qcow2_opt_get_version_del(QemuOpts *opts, Error **errp) ++{ ++ char *buf; ++ int ret; ++ ++ buf = qemu_opt_get_del(opts, BLOCK_OPT_COMPAT_LEVEL); ++ if (!buf) { ++ ret = 3; /* default */ ++ } else if (!strcmp(buf, "0.10")) { ++ ret = 2; ++ } else if (!strcmp(buf, "1.1")) { ++ ret = 3; ++ } else { ++ error_setg(errp, "Invalid compatibility level: '%s'", buf); ++ ret = -EINVAL; ++ } ++ g_free(buf); ++ return ret; ++} ++ ++static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version, ++ Error **errp) ++{ ++ uint64_t refcount_bits; ++ ++ refcount_bits = qemu_opt_get_number_del(opts, BLOCK_OPT_REFCOUNT_BITS, 16); ++ if (refcount_bits > 64 || !is_power_of_2(refcount_bits)) { ++ error_setg(errp, "Refcount width must be a power of two and may not " ++ "exceed 64 bits"); ++ return 0; ++ } ++ ++ if (version < 3 && refcount_bits != 16) { ++ error_setg(errp, "Different refcount widths than 16 bits require " ++ "compatibility level 1.1 or above (use compat=1.1 or " ++ "greater)"); ++ return 0; ++ } ++ ++ return refcount_bits; ++} ++ ++static int coroutine_fn GRAPH_UNLOCKED ++qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) ++{ ++ ERRP_GUARD(); ++ BlockdevCreateOptionsQcow2 *qcow2_opts; ++ QDict *options; ++ ++ /* ++ * Open the image file and write a minimal qcow2 header. ++ * ++ * We keep things simple and start with a zero-sized image. We also ++ * do without refcount blocks or a L1 table for now. We'll fix the ++ * inconsistency later. ++ * ++ * We do need a refcount table because growing the refcount table means ++ * allocating two new refcount blocks - the second of which would be at ++ * 2 GB for 64k clusters, and we don't want to have a 2 GB initial file ++ * size for any qcow2 image. ++ */ ++ BlockBackend *blk = NULL; ++ BlockDriverState *bs = NULL; ++ BlockDriverState *data_bs = NULL; ++ QCowHeader *header; ++ size_t cluster_size; ++ int version; ++ int refcount_order; ++ uint64_t *refcount_table; ++ int ret; ++ uint8_t compression_type = QCOW2_COMPRESSION_TYPE_ZLIB; ++ ++ assert(create_options->driver == BLOCKDEV_DRIVER_QCOW2); ++ qcow2_opts = &create_options->u.qcow2; ++ ++ bs = bdrv_co_open_blockdev_ref(qcow2_opts->file, errp); ++ if (bs == NULL) { ++ return -EIO; ++ } ++ ++ /* Validate options and set default values */ ++ if (!QEMU_IS_ALIGNED(qcow2_opts->size, BDRV_SECTOR_SIZE)) { ++ error_setg(errp, "Image size must be a multiple of %u bytes", ++ (unsigned) BDRV_SECTOR_SIZE); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (qcow2_opts->has_version) { ++ switch (qcow2_opts->version) { ++ case BLOCKDEV_QCOW2_VERSION_V2: ++ version = 2; ++ break; ++ case BLOCKDEV_QCOW2_VERSION_V3: ++ version = 3; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } else { ++ version = 3; ++ } ++ ++ if (qcow2_opts->has_cluster_size) { ++ cluster_size = qcow2_opts->cluster_size; ++ } else { ++ cluster_size = DEFAULT_CLUSTER_SIZE; ++ } ++ ++ if (!qcow2_opts->has_extended_l2) { ++ qcow2_opts->extended_l2 = false; ++ } ++ if (qcow2_opts->extended_l2) { ++ if (version < 3) { ++ error_setg(errp, "Extended L2 entries are only supported with " ++ "compatibility level 1.1 and above (use version=v3 or " ++ "greater)"); ++ ret = -EINVAL; ++ goto out; ++ } ++ } ++ ++ if (!validate_cluster_size(cluster_size, qcow2_opts->extended_l2, errp)) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (!qcow2_opts->has_preallocation) { ++ qcow2_opts->preallocation = PREALLOC_MODE_OFF; ++ } ++ if (qcow2_opts->backing_file && ++ qcow2_opts->preallocation != PREALLOC_MODE_OFF && ++ !qcow2_opts->extended_l2) ++ { ++ error_setg(errp, "Backing file and preallocation can only be used at " ++ "the same time if extended_l2 is on"); ++ ret = -EINVAL; ++ goto out; ++ } ++ if (qcow2_opts->has_backing_fmt && !qcow2_opts->backing_file) { ++ error_setg(errp, "Backing format cannot be used without backing file"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (!qcow2_opts->has_lazy_refcounts) { ++ qcow2_opts->lazy_refcounts = false; ++ } ++ if (version < 3 && qcow2_opts->lazy_refcounts) { ++ error_setg(errp, "Lazy refcounts only supported with compatibility " ++ "level 1.1 and above (use version=v3 or greater)"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (!qcow2_opts->has_refcount_bits) { ++ qcow2_opts->refcount_bits = 16; ++ } ++ if (qcow2_opts->refcount_bits > 64 || ++ !is_power_of_2(qcow2_opts->refcount_bits)) ++ { ++ error_setg(errp, "Refcount width must be a power of two and may not " ++ "exceed 64 bits"); ++ ret = -EINVAL; ++ goto out; ++ } ++ if (version < 3 && qcow2_opts->refcount_bits != 16) { ++ error_setg(errp, "Different refcount widths than 16 bits require " ++ "compatibility level 1.1 or above (use version=v3 or " ++ "greater)"); ++ ret = -EINVAL; ++ goto out; ++ } ++ refcount_order = ctz32(qcow2_opts->refcount_bits); ++ ++ if (qcow2_opts->data_file_raw && !qcow2_opts->data_file) { ++ error_setg(errp, "data-file-raw requires data-file"); ++ ret = -EINVAL; ++ goto out; ++ } ++ if (qcow2_opts->data_file_raw && qcow2_opts->backing_file) { ++ error_setg(errp, "Backing file and data-file-raw cannot be used at " ++ "the same time"); ++ ret = -EINVAL; ++ goto out; ++ } ++ if (qcow2_opts->data_file_raw && ++ qcow2_opts->preallocation == PREALLOC_MODE_OFF) ++ { ++ /* ++ * data-file-raw means that "the external data file can be ++ * read as a consistent standalone raw image without looking ++ * at the qcow2 metadata." It does not say that the metadata ++ * must be ignored, though (and the qcow2 driver in fact does ++ * not ignore it), so the L1/L2 tables must be present and ++ * give a 1:1 mapping, so you get the same result regardless ++ * of whether you look at the metadata or whether you ignore ++ * it. ++ */ ++ qcow2_opts->preallocation = PREALLOC_MODE_METADATA; ++ ++ /* ++ * Cannot use preallocation with backing files, but giving a ++ * backing file when specifying data_file_raw is an error ++ * anyway. ++ */ ++ assert(!qcow2_opts->backing_file); ++ } ++ ++ if (qcow2_opts->data_file) { ++ if (version < 3) { ++ error_setg(errp, "External data files are only supported with " ++ "compatibility level 1.1 and above (use version=v3 or " ++ "greater)"); ++ ret = -EINVAL; ++ goto out; ++ } ++ data_bs = bdrv_co_open_blockdev_ref(qcow2_opts->data_file, errp); ++ if (data_bs == NULL) { ++ ret = -EIO; ++ goto out; ++ } ++ } ++ ++ if (qcow2_opts->has_compression_type && ++ qcow2_opts->compression_type != QCOW2_COMPRESSION_TYPE_ZLIB) { ++ ++ ret = -EINVAL; ++ ++ if (version < 3) { ++ error_setg(errp, "Non-zlib compression type is only supported with " ++ "compatibility level 1.1 and above (use version=v3 or " ++ "greater)"); ++ goto out; ++ } ++ ++ switch (qcow2_opts->compression_type) { ++#ifdef CONFIG_ZSTD ++ case QCOW2_COMPRESSION_TYPE_ZSTD: ++ break; ++#endif ++ default: ++ error_setg(errp, "Unknown compression type"); ++ goto out; ++ } ++ ++ compression_type = qcow2_opts->compression_type; ++ } ++ ++ /* Create BlockBackend to write to the image */ ++ blk = blk_co_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, ++ errp); ++ if (!blk) { ++ ret = -EPERM; ++ goto out; ++ } ++ blk_set_allow_write_beyond_eof(blk, true); ++ ++ /* Write the header */ ++ QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header)); ++ header = g_malloc0(cluster_size); ++ *header = (QCowHeader) { ++ .magic = cpu_to_be32(QCOW_MAGIC), ++ .version = cpu_to_be32(version), ++ .cluster_bits = cpu_to_be32(ctz32(cluster_size)), ++ .size = cpu_to_be64(0), ++ .l1_table_offset = cpu_to_be64(0), ++ .l1_size = cpu_to_be32(0), ++ .refcount_table_offset = cpu_to_be64(cluster_size), ++ .refcount_table_clusters = cpu_to_be32(1), ++ .refcount_order = cpu_to_be32(refcount_order), ++ /* don't deal with endianness since compression_type is 1 byte long */ ++ .compression_type = compression_type, ++ .header_length = cpu_to_be32(sizeof(*header)), ++ }; ++ ++ /* We'll update this to correct value later */ ++ header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); ++ ++ if (qcow2_opts->lazy_refcounts) { ++ header->compatible_features |= ++ cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS); ++ } ++ if (data_bs) { ++ header->incompatible_features |= ++ cpu_to_be64(QCOW2_INCOMPAT_DATA_FILE); ++ } ++ if (qcow2_opts->data_file_raw) { ++ header->autoclear_features |= ++ cpu_to_be64(QCOW2_AUTOCLEAR_DATA_FILE_RAW); ++ } ++ if (compression_type != QCOW2_COMPRESSION_TYPE_ZLIB) { ++ header->incompatible_features |= ++ cpu_to_be64(QCOW2_INCOMPAT_COMPRESSION); ++ } ++ ++ if (qcow2_opts->extended_l2) { ++ header->incompatible_features |= ++ cpu_to_be64(QCOW2_INCOMPAT_EXTL2); ++ } ++ ++ ret = blk_co_pwrite(blk, 0, cluster_size, header, 0); ++ g_free(header); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not write qcow2 header"); ++ goto out; ++ } ++ ++ /* Write a refcount table with one refcount block */ ++ refcount_table = g_malloc0(2 * cluster_size); ++ refcount_table[0] = cpu_to_be64(2 * cluster_size); ++ ret = blk_co_pwrite(blk, cluster_size, 2 * cluster_size, refcount_table, 0); ++ g_free(refcount_table); ++ ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not write refcount table"); ++ goto out; ++ } ++ ++ blk_co_unref(blk); ++ blk = NULL; ++ ++ /* ++ * And now open the image and make it consistent first (i.e. increase the ++ * refcount of the cluster that is occupied by the header and the refcount ++ * table) ++ */ ++ options = qdict_new(); ++ qdict_put_str(options, "driver", "qcow2"); ++ qdict_put_str(options, "file", bs->node_name); ++ if (data_bs) { ++ qdict_put_str(options, "data-file", data_bs->node_name); ++ } ++ blk = blk_co_new_open(NULL, NULL, options, ++ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH, ++ errp); ++ if (blk == NULL) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ bdrv_graph_co_rdlock(); ++ ret = qcow2_alloc_clusters(blk_bs(blk), 3 * cluster_size); ++ if (ret < 0) { ++ bdrv_graph_co_rdunlock(); ++ error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 " ++ "header and refcount table"); ++ goto out; ++ ++ } else if (ret != 0) { ++ error_report("Huh, first cluster in empty image is already in use?"); ++ abort(); ++ } ++ ++ /* Set the external data file if necessary */ ++ if (data_bs) { ++ BDRVQcow2State *s = blk_bs(blk)->opaque; ++ s->image_data_file = g_strdup(data_bs->filename); ++ } ++ ++ /* Create a full header (including things like feature table) */ ++ ret = qcow2_update_header(blk_bs(blk)); ++ bdrv_graph_co_rdunlock(); ++ ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not update qcow2 header"); ++ goto out; ++ } ++ ++ /* Okay, now that we have a valid image, let's give it the right size */ ++ ret = blk_co_truncate(blk, qcow2_opts->size, false, ++ qcow2_opts->preallocation, 0, errp); ++ if (ret < 0) { ++ error_prepend(errp, "Could not resize image: "); ++ goto out; ++ } ++ ++ /* Want a backing file? There you go. */ ++ if (qcow2_opts->backing_file) { ++ const char *backing_format = NULL; ++ ++ if (qcow2_opts->has_backing_fmt) { ++ backing_format = BlockdevDriver_str(qcow2_opts->backing_fmt); ++ } ++ ++ bdrv_graph_co_rdlock(); ++ ret = bdrv_co_change_backing_file(blk_bs(blk), qcow2_opts->backing_file, ++ backing_format, false); ++ bdrv_graph_co_rdunlock(); ++ ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not assign backing file '%s' " ++ "with format '%s'", qcow2_opts->backing_file, ++ backing_format); ++ goto out; ++ } ++ } ++ ++ /* Want encryption? There you go. */ ++ if (qcow2_opts->encrypt) { ++ bdrv_graph_co_rdlock(); ++ ret = qcow2_set_up_encryption(blk_bs(blk), qcow2_opts->encrypt, errp); ++ bdrv_graph_co_rdunlock(); ++ ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ ++ blk_co_unref(blk); ++ blk = NULL; ++ ++ /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning. ++ * Using BDRV_O_NO_IO, since encryption is now setup we don't want to ++ * have to setup decryption context. We're not doing any I/O on the top ++ * level BlockDriverState, only lower layers, where BDRV_O_NO_IO does ++ * not have effect. ++ */ ++ options = qdict_new(); ++ qdict_put_str(options, "driver", "qcow2"); ++ qdict_put_str(options, "file", bs->node_name); ++ if (data_bs) { ++ qdict_put_str(options, "data-file", data_bs->node_name); ++ } ++ blk = blk_co_new_open(NULL, NULL, options, ++ BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO, ++ errp); ++ if (blk == NULL) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ ret = 0; ++out: ++ blk_co_unref(blk); ++ bdrv_co_unref(bs); ++ bdrv_co_unref(data_bs); ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_UNLOCKED ++qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts, ++ Error **errp) ++{ ++ BlockdevCreateOptions *create_options = NULL; ++ QDict *qdict; ++ Visitor *v; ++ BlockDriverState *bs = NULL; ++ BlockDriverState *data_bs = NULL; ++ const char *val; ++ int ret; ++ ++ /* Only the keyval visitor supports the dotted syntax needed for ++ * encryption, so go through a QDict before getting a QAPI type. Ignore ++ * options meant for the protocol layer so that the visitor doesn't ++ * complain. */ ++ qdict = qemu_opts_to_qdict_filtered(opts, NULL, bdrv_qcow2.create_opts, ++ true); ++ ++ /* Handle encryption options */ ++ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT); ++ if (val && !strcmp(val, "on")) { ++ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow"); ++ } else if (val && !strcmp(val, "off")) { ++ qdict_del(qdict, BLOCK_OPT_ENCRYPT); ++ } ++ ++ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT); ++ if (val && !strcmp(val, "aes")) { ++ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow"); ++ } ++ ++ /* Convert compat=0.10/1.1 into compat=v2/v3, to be renamed into ++ * version=v2/v3 below. */ ++ val = qdict_get_try_str(qdict, BLOCK_OPT_COMPAT_LEVEL); ++ if (val && !strcmp(val, "0.10")) { ++ qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v2"); ++ } else if (val && !strcmp(val, "1.1")) { ++ qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v3"); ++ } ++ ++ /* Change legacy command line options into QMP ones */ ++ static const QDictRenames opt_renames[] = { ++ { BLOCK_OPT_BACKING_FILE, "backing-file" }, ++ { BLOCK_OPT_BACKING_FMT, "backing-fmt" }, ++ { BLOCK_OPT_CLUSTER_SIZE, "cluster-size" }, ++ { BLOCK_OPT_LAZY_REFCOUNTS, "lazy-refcounts" }, ++ { BLOCK_OPT_EXTL2, "extended-l2" }, ++ { BLOCK_OPT_REFCOUNT_BITS, "refcount-bits" }, ++ { BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT }, ++ { BLOCK_OPT_COMPAT_LEVEL, "version" }, ++ { BLOCK_OPT_DATA_FILE_RAW, "data-file-raw" }, ++ { BLOCK_OPT_COMPRESSION_TYPE, "compression-type" }, ++ { NULL, NULL }, ++ }; ++ ++ if (!qdict_rename_keys(qdict, opt_renames, errp)) { ++ ret = -EINVAL; ++ goto finish; ++ } ++ ++ /* Create and open the file (protocol layer) */ ++ ret = bdrv_co_create_file(filename, opts, errp); ++ if (ret < 0) { ++ goto finish; ++ } ++ ++ bs = bdrv_co_open(filename, NULL, NULL, ++ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); ++ if (bs == NULL) { ++ ret = -EIO; ++ goto finish; ++ } ++ ++ /* Create and open an external data file (protocol layer) */ ++ val = qdict_get_try_str(qdict, BLOCK_OPT_DATA_FILE); ++ if (val) { ++ ret = bdrv_co_create_file(val, opts, errp); ++ if (ret < 0) { ++ goto finish; ++ } ++ ++ data_bs = bdrv_co_open(val, NULL, NULL, ++ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, ++ errp); ++ if (data_bs == NULL) { ++ ret = -EIO; ++ goto finish; ++ } ++ ++ qdict_del(qdict, BLOCK_OPT_DATA_FILE); ++ qdict_put_str(qdict, "data-file", data_bs->node_name); ++ } ++ ++ /* Set 'driver' and 'node' options */ ++ qdict_put_str(qdict, "driver", "qcow2"); ++ qdict_put_str(qdict, "file", bs->node_name); ++ ++ /* Now get the QAPI type BlockdevCreateOptions */ ++ v = qobject_input_visitor_new_flat_confused(qdict, errp); ++ if (!v) { ++ ret = -EINVAL; ++ goto finish; ++ } ++ ++ visit_type_BlockdevCreateOptions(v, NULL, &create_options, errp); ++ visit_free(v); ++ if (!create_options) { ++ ret = -EINVAL; ++ goto finish; ++ } ++ ++ /* Silently round up size */ ++ create_options->u.qcow2.size = ROUND_UP(create_options->u.qcow2.size, ++ BDRV_SECTOR_SIZE); ++ ++ /* Create the qcow2 image (format layer) */ ++ ret = qcow2_co_create(create_options, errp); ++finish: ++ if (ret < 0) { ++ bdrv_graph_co_rdlock(); ++ bdrv_co_delete_file_noerr(bs); ++ bdrv_co_delete_file_noerr(data_bs); ++ bdrv_graph_co_rdunlock(); ++ } else { ++ ret = 0; ++ } ++ ++ qobject_unref(qdict); ++ bdrv_co_unref(bs); ++ bdrv_co_unref(data_bs); ++ qapi_free_BlockdevCreateOptions(create_options); ++ return ret; ++} ++ ++ ++static bool coroutine_fn GRAPH_RDLOCK ++is_zero(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ int64_t nr; ++ int res; ++ ++ /* Clamp to image length, before checking status of underlying sectors */ ++ if (offset + bytes > bs->total_sectors * BDRV_SECTOR_SIZE) { ++ bytes = bs->total_sectors * BDRV_SECTOR_SIZE - offset; ++ } ++ ++ if (!bytes) { ++ return true; ++ } ++ ++ /* ++ * bdrv_block_status_above doesn't merge different types of zeros, for ++ * example, zeros which come from the region which is unallocated in ++ * the whole backing chain, and zeros which come because of a short ++ * backing file. So, we need a loop. ++ */ ++ do { ++ res = bdrv_co_block_status_above(bs, NULL, offset, bytes, &nr, NULL, NULL); ++ offset += nr; ++ bytes -= nr; ++ } while (res >= 0 && (res & BDRV_BLOCK_ZERO) && nr && bytes); ++ ++ return res >= 0 && (res & BDRV_BLOCK_ZERO) && bytes == 0; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ ++ uint32_t head = offset_into_subcluster(s, offset); ++ uint32_t tail = ROUND_UP(offset + bytes, s->subcluster_size) - ++ (offset + bytes); ++ ++ trace_qcow2_pwrite_zeroes_start_req(qemu_coroutine_self(), offset, bytes); ++ if (offset + bytes == bs->total_sectors * BDRV_SECTOR_SIZE) { ++ tail = 0; ++ } ++ ++ if (head || tail) { ++ uint64_t off; ++ unsigned int nr; ++ QCow2SubclusterType type; ++ ++ assert(head + bytes + tail <= s->subcluster_size); ++ ++ /* check whether remainder of cluster already reads as zero */ ++ if (!(is_zero(bs, offset - head, head) && ++ is_zero(bs, offset + bytes, tail))) { ++ return -ENOTSUP; ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ /* We can have new write after previous check */ ++ offset -= head; ++ bytes = s->subcluster_size; ++ nr = s->subcluster_size; ++ ret = qcow2_get_host_offset(bs, offset, &nr, &off, &type); ++ if (ret < 0 || ++ (type != QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN && ++ type != QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC && ++ type != QCOW2_SUBCLUSTER_ZERO_PLAIN && ++ type != QCOW2_SUBCLUSTER_ZERO_ALLOC)) { ++ qemu_co_mutex_unlock(&s->lock); ++ return ret < 0 ? ret : -ENOTSUP; ++ } ++ } else { ++ qemu_co_mutex_lock(&s->lock); ++ } ++ ++ trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, bytes); ++ ++ /* Whatever is left can use real zero subclusters */ ++ ret = qcow2_subcluster_zeroize(bs, offset, bytes, flags); ++ qemu_co_mutex_unlock(&s->lock); ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ ++ /* If the image does not support QCOW_OFLAG_ZERO then discarding ++ * clusters could expose stale data from the backing file. */ ++ if (s->qcow_version < 3 && bs->backing) { ++ return -ENOTSUP; ++ } ++ ++ if (!QEMU_IS_ALIGNED(offset | bytes, s->cluster_size)) { ++ assert(bytes < s->cluster_size); ++ /* Ignore partial clusters, except for the special case of the ++ * complete partial cluster at the end of an unaligned file */ ++ if (!QEMU_IS_ALIGNED(offset, s->cluster_size) || ++ offset + bytes != bs->total_sectors * BDRV_SECTOR_SIZE) { ++ return -ENOTSUP; ++ } ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ret = qcow2_cluster_discard(bs, offset, bytes, QCOW2_DISCARD_REQUEST, ++ false); ++ qemu_co_mutex_unlock(&s->lock); ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_copy_range_from(BlockDriverState *bs, ++ BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ unsigned int cur_bytes; /* number of bytes in current iteration */ ++ BdrvChild *child = NULL; ++ BdrvRequestFlags cur_write_flags; ++ ++ assert(!bs->encrypted); ++ qemu_co_mutex_lock(&s->lock); ++ ++ while (bytes != 0) { ++ uint64_t copy_offset = 0; ++ QCow2SubclusterType type; ++ /* prepare next request */ ++ cur_bytes = MIN(bytes, INT_MAX); ++ cur_write_flags = write_flags; ++ ++ ret = qcow2_get_host_offset(bs, src_offset, &cur_bytes, ++ ©_offset, &type); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ switch (type) { ++ case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ if (bs->backing && bs->backing->bs) { ++ int64_t backing_length = bdrv_co_getlength(bs->backing->bs); ++ if (src_offset >= backing_length) { ++ cur_write_flags |= BDRV_REQ_ZERO_WRITE; ++ } else { ++ child = bs->backing; ++ cur_bytes = MIN(cur_bytes, backing_length - src_offset); ++ copy_offset = src_offset; ++ } ++ } else { ++ cur_write_flags |= BDRV_REQ_ZERO_WRITE; ++ } ++ break; ++ ++ case QCOW2_SUBCLUSTER_ZERO_PLAIN: ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ cur_write_flags |= BDRV_REQ_ZERO_WRITE; ++ break; ++ ++ case QCOW2_SUBCLUSTER_COMPRESSED: ++ ret = -ENOTSUP; ++ goto out; ++ ++ case QCOW2_SUBCLUSTER_NORMAL: ++ child = s->data_file; ++ break; ++ ++ default: ++ abort(); ++ } ++ qemu_co_mutex_unlock(&s->lock); ++ ret = bdrv_co_copy_range_from(child, ++ copy_offset, ++ dst, dst_offset, ++ cur_bytes, read_flags, cur_write_flags); ++ qemu_co_mutex_lock(&s->lock); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ bytes -= cur_bytes; ++ src_offset += cur_bytes; ++ dst_offset += cur_bytes; ++ } ++ ret = 0; ++ ++out: ++ qemu_co_mutex_unlock(&s->lock); ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_copy_range_to(BlockDriverState *bs, ++ BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ unsigned int cur_bytes; /* number of sectors in current iteration */ ++ uint64_t host_offset; ++ QCowL2Meta *l2meta = NULL; ++ ++ assert(!bs->encrypted); ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++ while (bytes != 0) { ++ ++ l2meta = NULL; ++ ++ cur_bytes = MIN(bytes, INT_MAX); ++ ++ /* TODO: ++ * If src->bs == dst->bs, we could simply copy by incrementing ++ * the refcnt, without copying user data. ++ * Or if src->bs == dst->bs->backing->bs, we could copy by discarding. */ ++ ret = qcow2_alloc_host_offset(bs, dst_offset, &cur_bytes, ++ &host_offset, &l2meta); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, host_offset, cur_bytes, ++ true); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ret = bdrv_co_copy_range_to(src, src_offset, s->data_file, host_offset, ++ cur_bytes, read_flags, write_flags); ++ qemu_co_mutex_lock(&s->lock); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_handle_l2meta(bs, &l2meta, true); ++ if (ret) { ++ goto fail; ++ } ++ ++ bytes -= cur_bytes; ++ src_offset += cur_bytes; ++ dst_offset += cur_bytes; ++ } ++ ret = 0; ++ ++fail: ++ qcow2_handle_l2meta(bs, &l2meta, false); ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ++ trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) ++{ ++ ERRP_GUARD(); ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t old_length; ++ int64_t new_l1_size; ++ int ret; ++ QDict *options; ++ ++ if (prealloc != PREALLOC_MODE_OFF && prealloc != PREALLOC_MODE_METADATA && ++ prealloc != PREALLOC_MODE_FALLOC && prealloc != PREALLOC_MODE_FULL) ++ { ++ error_setg(errp, "Unsupported preallocation mode '%s'", ++ PreallocMode_str(prealloc)); ++ return -ENOTSUP; ++ } ++ ++ if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { ++ error_setg(errp, "The new size must be a multiple of %u", ++ (unsigned) BDRV_SECTOR_SIZE); ++ return -EINVAL; ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++ /* ++ * Even though we store snapshot size for all images, it was not ++ * required until v3, so it is not safe to proceed for v2. ++ */ ++ if (s->nb_snapshots && s->qcow_version < 3) { ++ error_setg(errp, "Can't resize a v2 image which has snapshots"); ++ ret = -ENOTSUP; ++ goto fail; ++ } ++ ++ /* See qcow2-bitmap.c for which bitmap scenarios prevent a resize. */ ++ if (qcow2_truncate_bitmaps_check(bs, errp)) { ++ ret = -ENOTSUP; ++ goto fail; ++ } ++ ++ old_length = bs->total_sectors * BDRV_SECTOR_SIZE; ++ new_l1_size = size_to_l1(s, offset); ++ ++ if (offset < old_length) { ++ int64_t last_cluster, old_file_size; ++ if (prealloc != PREALLOC_MODE_OFF) { ++ error_setg(errp, ++ "Preallocation can't be used for shrinking an image"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ ret = qcow2_cluster_discard(bs, ROUND_UP(offset, s->cluster_size), ++ old_length - ROUND_UP(offset, ++ s->cluster_size), ++ QCOW2_DISCARD_ALWAYS, true); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to discard cropped clusters"); ++ goto fail; ++ } ++ ++ ret = qcow2_shrink_l1_table(bs, new_l1_size); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to reduce the number of L2 tables"); ++ goto fail; ++ } ++ ++ ret = qcow2_shrink_reftable(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to discard unused refblocks"); ++ goto fail; ++ } ++ ++ old_file_size = bdrv_co_getlength(bs->file->bs); ++ if (old_file_size < 0) { ++ error_setg_errno(errp, -old_file_size, ++ "Failed to inquire current file length"); ++ ret = old_file_size; ++ goto fail; ++ } ++ last_cluster = qcow2_get_last_cluster(bs, old_file_size); ++ if (last_cluster < 0) { ++ error_setg_errno(errp, -last_cluster, ++ "Failed to find the last cluster"); ++ ret = last_cluster; ++ goto fail; ++ } ++ if ((last_cluster + 1) * s->cluster_size < old_file_size) { ++ Error *local_err = NULL; ++ ++ /* ++ * Do not pass @exact here: It will not help the user if ++ * we get an error here just because they wanted to shrink ++ * their qcow2 image (on a block device) with qemu-img. ++ * (And on the qcow2 layer, the @exact requirement is ++ * always fulfilled, so there is no need to pass it on.) ++ */ ++ bdrv_co_truncate(bs->file, (last_cluster + 1) * s->cluster_size, ++ false, PREALLOC_MODE_OFF, 0, &local_err); ++ if (local_err) { ++ warn_reportf_err(local_err, ++ "Failed to truncate the tail of the image: "); ++ } ++ } ++ } else { ++ ret = qcow2_grow_l1_table(bs, new_l1_size, true); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to grow the L1 table"); ++ goto fail; ++ } ++ ++ if (data_file_is_raw(bs) && prealloc == PREALLOC_MODE_OFF) { ++ /* ++ * When creating a qcow2 image with data-file-raw, we enforce ++ * at least prealloc=metadata, so that the L1/L2 tables are ++ * fully allocated and reading from the data file will return ++ * the same data as reading from the qcow2 image. When the ++ * image is grown, we must consequently preallocate the ++ * metadata structures to cover the added area. ++ */ ++ prealloc = PREALLOC_MODE_METADATA; ++ } ++ } ++ ++ switch (prealloc) { ++ case PREALLOC_MODE_OFF: ++ if (has_data_file(bs)) { ++ /* ++ * If the caller wants an exact resize, the external data ++ * file should be resized to the exact target size, too, ++ * so we pass @exact here. ++ */ ++ ret = bdrv_co_truncate(s->data_file, offset, exact, prealloc, 0, ++ errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ break; ++ ++ case PREALLOC_MODE_METADATA: ++ ret = preallocate_co(bs, old_length, offset, prealloc, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ break; ++ ++ case PREALLOC_MODE_FALLOC: ++ case PREALLOC_MODE_FULL: ++ { ++ int64_t allocation_start, host_offset, guest_offset; ++ int64_t clusters_allocated; ++ int64_t old_file_size, last_cluster, new_file_size; ++ uint64_t nb_new_data_clusters, nb_new_l2_tables; ++ bool subclusters_need_allocation = false; ++ ++ /* With a data file, preallocation means just allocating the metadata ++ * and forwarding the truncate request to the data file */ ++ if (has_data_file(bs)) { ++ ret = preallocate_co(bs, old_length, offset, prealloc, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ break; ++ } ++ ++ old_file_size = bdrv_co_getlength(bs->file->bs); ++ if (old_file_size < 0) { ++ error_setg_errno(errp, -old_file_size, ++ "Failed to inquire current file length"); ++ ret = old_file_size; ++ goto fail; ++ } ++ ++ last_cluster = qcow2_get_last_cluster(bs, old_file_size); ++ if (last_cluster >= 0) { ++ old_file_size = (last_cluster + 1) * s->cluster_size; ++ } else { ++ old_file_size = ROUND_UP(old_file_size, s->cluster_size); ++ } ++ ++ nb_new_data_clusters = (ROUND_UP(offset, s->cluster_size) - ++ start_of_cluster(s, old_length)) >> s->cluster_bits; ++ ++ /* This is an overestimation; we will not actually allocate space for ++ * these in the file but just make sure the new refcount structures are ++ * able to cover them so we will not have to allocate new refblocks ++ * while entering the data blocks in the potentially new L2 tables. ++ * (We do not actually care where the L2 tables are placed. Maybe they ++ * are already allocated or they can be placed somewhere before ++ * @old_file_size. It does not matter because they will be fully ++ * allocated automatically, so they do not need to be covered by the ++ * preallocation. All that matters is that we will not have to allocate ++ * new refcount structures for them.) */ ++ nb_new_l2_tables = DIV_ROUND_UP(nb_new_data_clusters, ++ s->cluster_size / l2_entry_size(s)); ++ /* The cluster range may not be aligned to L2 boundaries, so add one L2 ++ * table for a potential head/tail */ ++ nb_new_l2_tables++; ++ ++ allocation_start = qcow2_refcount_area(bs, old_file_size, ++ nb_new_data_clusters + ++ nb_new_l2_tables, ++ true, 0, 0); ++ if (allocation_start < 0) { ++ error_setg_errno(errp, -allocation_start, ++ "Failed to resize refcount structures"); ++ ret = allocation_start; ++ goto fail; ++ } ++ ++ clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start, ++ nb_new_data_clusters); ++ if (clusters_allocated < 0) { ++ error_setg_errno(errp, -clusters_allocated, ++ "Failed to allocate data clusters"); ++ ret = clusters_allocated; ++ goto fail; ++ } ++ ++ assert(clusters_allocated == nb_new_data_clusters); ++ ++ /* Allocate the data area */ ++ new_file_size = allocation_start + ++ nb_new_data_clusters * s->cluster_size; ++ /* ++ * Image file grows, so @exact does not matter. ++ * ++ * If we need to zero out the new area, try first whether the protocol ++ * driver can already take care of this. ++ */ ++ if (flags & BDRV_REQ_ZERO_WRITE) { ++ ret = bdrv_co_truncate(bs->file, new_file_size, false, prealloc, ++ BDRV_REQ_ZERO_WRITE, NULL); ++ if (ret >= 0) { ++ flags &= ~BDRV_REQ_ZERO_WRITE; ++ /* Ensure that we read zeroes and not backing file data */ ++ subclusters_need_allocation = true; ++ } ++ } else { ++ ret = -1; ++ } ++ if (ret < 0) { ++ ret = bdrv_co_truncate(bs->file, new_file_size, false, prealloc, 0, ++ errp); ++ } ++ if (ret < 0) { ++ error_prepend(errp, "Failed to resize underlying file: "); ++ qcow2_free_clusters(bs, allocation_start, ++ nb_new_data_clusters * s->cluster_size, ++ QCOW2_DISCARD_OTHER); ++ goto fail; ++ } ++ ++ /* Create the necessary L2 entries */ ++ host_offset = allocation_start; ++ guest_offset = old_length; ++ while (nb_new_data_clusters) { ++ int64_t nb_clusters = MIN( ++ nb_new_data_clusters, ++ s->l2_slice_size - offset_to_l2_slice_index(s, guest_offset)); ++ unsigned cow_start_length = offset_into_cluster(s, guest_offset); ++ QCowL2Meta allocation; ++ guest_offset = start_of_cluster(s, guest_offset); ++ allocation = (QCowL2Meta) { ++ .offset = guest_offset, ++ .alloc_offset = host_offset, ++ .nb_clusters = nb_clusters, ++ .cow_start = { ++ .offset = 0, ++ .nb_bytes = cow_start_length, ++ }, ++ .cow_end = { ++ .offset = nb_clusters << s->cluster_bits, ++ .nb_bytes = 0, ++ }, ++ .prealloc = !subclusters_need_allocation, ++ }; ++ qemu_co_queue_init(&allocation.dependent_requests); ++ ++ ret = qcow2_alloc_cluster_link_l2(bs, &allocation); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to update L2 tables"); ++ qcow2_free_clusters(bs, host_offset, ++ nb_new_data_clusters * s->cluster_size, ++ QCOW2_DISCARD_OTHER); ++ goto fail; ++ } ++ ++ guest_offset += nb_clusters * s->cluster_size; ++ host_offset += nb_clusters * s->cluster_size; ++ nb_new_data_clusters -= nb_clusters; ++ } ++ break; ++ } ++ ++ default: ++ g_assert_not_reached(); ++ } ++ ++ if ((flags & BDRV_REQ_ZERO_WRITE) && offset > old_length) { ++ uint64_t zero_start = QEMU_ALIGN_UP(old_length, s->subcluster_size); ++ ++ /* ++ * Use zero clusters as much as we can. qcow2_subcluster_zeroize() ++ * requires a subcluster-aligned start. The end may be unaligned if ++ * it is at the end of the image (which it is here). ++ */ ++ if (offset > zero_start) { ++ ret = qcow2_subcluster_zeroize(bs, zero_start, offset - zero_start, ++ 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to zero out new clusters"); ++ goto fail; ++ } ++ } ++ ++ /* Write explicit zeros for the unaligned head */ ++ if (zero_start > old_length) { ++ uint64_t len = MIN(zero_start, offset) - old_length; ++ uint8_t *buf = qemu_blockalign0(bs, len); ++ QEMUIOVector qiov; ++ qemu_iovec_init_buf(&qiov, buf, len); ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ret = qcow2_co_pwritev_part(bs, old_length, len, &qiov, 0, 0); ++ qemu_co_mutex_lock(&s->lock); ++ ++ qemu_vfree(buf); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to zero out the new area"); ++ goto fail; ++ } ++ } ++ } ++ ++ if (prealloc != PREALLOC_MODE_OFF) { ++ /* Flush metadata before actually changing the image size */ ++ ret = qcow2_write_caches(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to flush the preallocated area to disk"); ++ goto fail; ++ } ++ } ++ ++ bs->total_sectors = offset / BDRV_SECTOR_SIZE; ++ ++ /* write updated header.size */ ++ offset = cpu_to_be64(offset); ++ ret = bdrv_co_pwrite_sync(bs->file, offsetof(QCowHeader, size), ++ sizeof(offset), &offset, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to update the image size"); ++ goto fail; ++ } ++ ++ s->l1_vm_state_index = new_l1_size; ++ ++ /* Update cache sizes */ ++ options = qdict_clone_shallow(bs->options); ++ ret = qcow2_update_options(bs, options, s->flags, errp); ++ qobject_unref(options); ++ if (ret < 0) { ++ goto fail; ++ } ++ ret = 0; ++fail: ++ qemu_co_mutex_unlock(&s->lock); ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_pwritev_compressed_task(BlockDriverState *bs, ++ uint64_t offset, uint64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ssize_t out_len; ++ uint8_t *buf, *out_buf; ++ uint64_t cluster_offset; ++ ++ assert(bytes == s->cluster_size || (bytes < s->cluster_size && ++ (offset + bytes == bs->total_sectors << BDRV_SECTOR_BITS))); ++ ++ buf = qemu_blockalign(bs, s->cluster_size); ++ if (bytes < s->cluster_size) { ++ /* Zero-pad last write if image size is not cluster aligned */ ++ memset(buf + bytes, 0, s->cluster_size - bytes); ++ } ++ qemu_iovec_to_buf(qiov, qiov_offset, buf, bytes); ++ ++ out_buf = g_malloc(s->cluster_size); ++ ++ out_len = qcow2_co_compress(bs, out_buf, s->cluster_size - 1, ++ buf, s->cluster_size); ++ if (out_len == -ENOMEM) { ++ /* could not compress: write normal cluster */ ++ ret = qcow2_co_pwritev_part(bs, offset, bytes, qiov, qiov_offset, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ goto success; ++ } else if (out_len < 0) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ret = qcow2_alloc_compressed_cluster_offset(bs, offset, out_len, ++ &cluster_offset); ++ if (ret < 0) { ++ qemu_co_mutex_unlock(&s->lock); ++ goto fail; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len, true); ++ qemu_co_mutex_unlock(&s->lock); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ BLKDBG_CO_EVENT(s->data_file, BLKDBG_WRITE_COMPRESSED); ++ ret = bdrv_co_pwrite(s->data_file, cluster_offset, out_len, out_buf, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++success: ++ ret = 0; ++fail: ++ qemu_vfree(buf); ++ g_free(out_buf); ++ return ret; ++} ++ ++/* ++ * This function can count as GRAPH_RDLOCK because ++ * qcow2_co_pwritev_compressed_part() holds the graph lock and keeps it until ++ * this coroutine has terminated. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_pwritev_compressed_task_entry(AioTask *task) ++{ ++ Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); ++ ++ assert(!t->subcluster_type && !t->l2meta); ++ ++ return qcow2_co_pwritev_compressed_task(t->bs, t->offset, t->bytes, t->qiov, ++ t->qiov_offset); ++} ++ ++/* ++ * XXX: put compressed sectors first, then all the cluster aligned ++ * tables to avoid losing bytes in alignment ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_pwritev_compressed_part(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ AioTaskPool *aio = NULL; ++ int ret = 0; ++ ++ if (has_data_file(bs)) { ++ return -ENOTSUP; ++ } ++ ++ if (bytes == 0) { ++ /* ++ * align end of file to a sector boundary to ease reading with ++ * sector based I/Os ++ */ ++ int64_t len = bdrv_co_getlength(bs->file->bs); ++ if (len < 0) { ++ return len; ++ } ++ return bdrv_co_truncate(bs->file, len, false, PREALLOC_MODE_OFF, 0, ++ NULL); ++ } ++ ++ if (offset_into_cluster(s, offset)) { ++ return -EINVAL; ++ } ++ ++ if (offset_into_cluster(s, bytes) && ++ (offset + bytes) != (bs->total_sectors << BDRV_SECTOR_BITS)) { ++ return -EINVAL; ++ } ++ ++ while (bytes && aio_task_pool_status(aio) == 0) { ++ uint64_t chunk_size = MIN(bytes, s->cluster_size); ++ ++ if (!aio && chunk_size != bytes) { ++ aio = aio_task_pool_new(QCOW2_MAX_WORKERS); ++ } ++ ++ ret = qcow2_add_task(bs, aio, qcow2_co_pwritev_compressed_task_entry, ++ 0, 0, offset, chunk_size, qiov, qiov_offset, NULL); ++ if (ret < 0) { ++ break; ++ } ++ qiov_offset += chunk_size; ++ offset += chunk_size; ++ bytes -= chunk_size; ++ } ++ ++ if (aio) { ++ aio_task_pool_wait_all(aio); ++ if (ret == 0) { ++ ret = aio_task_pool_status(aio); ++ } ++ g_free(aio); ++ } ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_preadv_compressed(BlockDriverState *bs, ++ uint64_t l2_entry, ++ uint64_t offset, ++ uint64_t bytes, ++ QEMUIOVector *qiov, ++ size_t qiov_offset) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret = 0, csize; ++ uint64_t coffset; ++ uint8_t *buf, *out_buf; ++ int offset_in_cluster = offset_into_cluster(s, offset); ++ ++ qcow2_parse_compressed_l2_entry(bs, l2_entry, &coffset, &csize); ++ ++ buf = g_try_malloc(csize); ++ if (!buf) { ++ return -ENOMEM; ++ } ++ ++ out_buf = qemu_blockalign(bs, s->cluster_size); ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_COMPRESSED); ++ ret = bdrv_co_pread(bs->file, coffset, csize, buf, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if (qcow2_co_decompress(bs, out_buf, s->cluster_size, buf, csize) < 0) { ++ ret = -EIO; ++ goto fail; ++ } ++ ++ qemu_iovec_from_buf(qiov, qiov_offset, out_buf + offset_in_cluster, bytes); ++ ++fail: ++ qemu_vfree(out_buf); ++ g_free(buf); ++ ++ return ret; ++} ++ ++static int GRAPH_RDLOCK make_completely_empty(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Error *local_err = NULL; ++ int ret, l1_clusters; ++ int64_t offset; ++ uint64_t *new_reftable = NULL; ++ uint64_t rt_entry, l1_size2; ++ struct { ++ uint64_t l1_offset; ++ uint64_t reftable_offset; ++ uint32_t reftable_clusters; ++ } QEMU_PACKED l1_ofs_rt_ofs_cls; ++ ++ ret = qcow2_cache_empty(bs, s->l2_table_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_cache_empty(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Refcounts will be broken utterly */ ++ ret = qcow2_mark_dirty(bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); ++ ++ l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / L1E_SIZE); ++ l1_size2 = (uint64_t)s->l1_size * L1E_SIZE; ++ ++ /* After this call, neither the in-memory nor the on-disk refcount ++ * information accurately describe the actual references */ ++ ++ ret = bdrv_pwrite_zeroes(bs->file, s->l1_table_offset, ++ l1_clusters * s->cluster_size, 0); ++ if (ret < 0) { ++ goto fail_broken_refcounts; ++ } ++ memset(s->l1_table, 0, l1_size2); ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_EMPTY_IMAGE_PREPARE); ++ ++ /* Overwrite enough clusters at the beginning of the sectors to place ++ * the refcount table, a refcount block and the L1 table in; this may ++ * overwrite parts of the existing refcount and L1 table, which is not ++ * an issue because the dirty flag is set, complete data loss is in fact ++ * desired and partial data loss is consequently fine as well */ ++ ret = bdrv_pwrite_zeroes(bs->file, s->cluster_size, ++ (2 + l1_clusters) * s->cluster_size, 0); ++ /* This call (even if it failed overall) may have overwritten on-disk ++ * refcount structures; in that case, the in-memory refcount information ++ * will probably differ from the on-disk information which makes the BDS ++ * unusable */ ++ if (ret < 0) { ++ goto fail_broken_refcounts; ++ } ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); ++ BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_UPDATE); ++ ++ /* "Create" an empty reftable (one cluster) directly after the image ++ * header and an empty L1 table three clusters after the image header; ++ * the cluster between those two will be used as the first refblock */ ++ l1_ofs_rt_ofs_cls.l1_offset = cpu_to_be64(3 * s->cluster_size); ++ l1_ofs_rt_ofs_cls.reftable_offset = cpu_to_be64(s->cluster_size); ++ l1_ofs_rt_ofs_cls.reftable_clusters = cpu_to_be32(1); ++ ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_table_offset), ++ sizeof(l1_ofs_rt_ofs_cls), &l1_ofs_rt_ofs_cls, 0); ++ if (ret < 0) { ++ goto fail_broken_refcounts; ++ } ++ ++ s->l1_table_offset = 3 * s->cluster_size; ++ ++ new_reftable = g_try_new0(uint64_t, s->cluster_size / REFTABLE_ENTRY_SIZE); ++ if (!new_reftable) { ++ ret = -ENOMEM; ++ goto fail_broken_refcounts; ++ } ++ ++ s->refcount_table_offset = s->cluster_size; ++ s->refcount_table_size = s->cluster_size / REFTABLE_ENTRY_SIZE; ++ s->max_refcount_table_index = 0; ++ ++ g_free(s->refcount_table); ++ s->refcount_table = new_reftable; ++ new_reftable = NULL; ++ ++ /* Now the in-memory refcount information again corresponds to the on-disk ++ * information (reftable is empty and no refblocks (the refblock cache is ++ * empty)); however, this means some clusters (e.g. the image header) are ++ * referenced, but not refcounted, but the normal qcow2 code assumes that ++ * the in-memory information is always correct */ ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC); ++ ++ /* Enter the first refblock into the reftable */ ++ rt_entry = cpu_to_be64(2 * s->cluster_size); ++ ret = bdrv_pwrite_sync(bs->file, s->cluster_size, sizeof(rt_entry), ++ &rt_entry, 0); ++ if (ret < 0) { ++ goto fail_broken_refcounts; ++ } ++ s->refcount_table[0] = 2 * s->cluster_size; ++ ++ s->free_cluster_index = 0; ++ assert(3 + l1_clusters <= s->refcount_block_size); ++ offset = qcow2_alloc_clusters(bs, 3 * s->cluster_size + l1_size2); ++ if (offset < 0) { ++ ret = offset; ++ goto fail_broken_refcounts; ++ } else if (offset > 0) { ++ error_report("First cluster in emptied image is in use"); ++ abort(); ++ } ++ ++ /* Now finally the in-memory information corresponds to the on-disk ++ * structures and is correct */ ++ ret = qcow2_mark_clean(bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size, false, ++ PREALLOC_MODE_OFF, 0, &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto fail; ++ } ++ ++ return 0; ++ ++fail_broken_refcounts: ++ /* The BDS is unusable at this point. If we wanted to make it usable, we ++ * would have to call qcow2_refcount_close(), qcow2_refcount_init(), ++ * qcow2_check_refcounts(), qcow2_refcount_close() and qcow2_refcount_init() ++ * again. However, because the functions which could have caused this error ++ * path to be taken are used by those functions as well, it's very likely ++ * that that sequence will fail as well. Therefore, just eject the BDS. */ ++ bs->drv = NULL; ++ ++fail: ++ g_free(new_reftable); ++ return ret; ++} ++ ++static int GRAPH_RDLOCK qcow2_make_empty(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t offset, end_offset; ++ int step = QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size); ++ int l1_clusters, ret = 0; ++ ++ l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / L1E_SIZE); ++ ++ if (s->qcow_version >= 3 && !s->snapshots && !s->nb_bitmaps && ++ 3 + l1_clusters <= s->refcount_block_size && ++ s->crypt_method_header != QCOW_CRYPT_LUKS && ++ !has_data_file(bs)) { ++ /* The following function only works for qcow2 v3 images (it ++ * requires the dirty flag) and only as long as there are no ++ * features that reserve extra clusters (such as snapshots, ++ * LUKS header, or persistent bitmaps), because it completely ++ * empties the image. Furthermore, the L1 table and three ++ * additional clusters (image header, refcount table, one ++ * refcount block) have to fit inside one refcount block. It ++ * only resets the image file, i.e. does not work with an ++ * external data file. */ ++ return make_completely_empty(bs); ++ } ++ ++ /* This fallback code simply discards every active cluster; this is slow, ++ * but works in all cases */ ++ end_offset = bs->total_sectors * BDRV_SECTOR_SIZE; ++ for (offset = 0; offset < end_offset; offset += step) { ++ /* As this function is generally used after committing an external ++ * snapshot, QCOW2_DISCARD_SNAPSHOT seems appropriate. Also, the ++ * default action for this kind of discard is to pass the discard, ++ * which will ideally result in an actually smaller image file, as ++ * is probably desired. */ ++ ret = qcow2_cluster_discard(bs, offset, MIN(step, end_offset - offset), ++ QCOW2_DISCARD_SNAPSHOT, true); ++ if (ret < 0) { ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++static coroutine_fn GRAPH_RDLOCK int qcow2_co_flush_to_os(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ qemu_co_mutex_lock(&s->lock); ++ ret = qcow2_write_caches(bs); ++ qemu_co_mutex_unlock(&s->lock); ++ ++ return ret; ++} ++ ++static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, ++ Error **errp) ++{ ++ Error *local_err = NULL; ++ BlockMeasureInfo *info; ++ uint64_t required = 0; /* bytes that contribute to required size */ ++ uint64_t virtual_size; /* disk size as seen by guest */ ++ uint64_t refcount_bits; ++ uint64_t l2_tables; ++ uint64_t luks_payload_size = 0; ++ size_t cluster_size; ++ int version; ++ char *optstr; ++ PreallocMode prealloc; ++ bool has_backing_file; ++ bool has_luks; ++ bool extended_l2; ++ size_t l2e_size; ++ ++ /* Parse image creation options */ ++ extended_l2 = qemu_opt_get_bool_del(opts, BLOCK_OPT_EXTL2, false); ++ ++ cluster_size = qcow2_opt_get_cluster_size_del(opts, extended_l2, ++ &local_err); ++ if (local_err) { ++ goto err; ++ } ++ ++ version = qcow2_opt_get_version_del(opts, &local_err); ++ if (local_err) { ++ goto err; ++ } ++ ++ refcount_bits = qcow2_opt_get_refcount_bits_del(opts, version, &local_err); ++ if (local_err) { ++ goto err; ++ } ++ ++ optstr = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); ++ prealloc = qapi_enum_parse(&PreallocMode_lookup, optstr, ++ PREALLOC_MODE_OFF, &local_err); ++ g_free(optstr); ++ if (local_err) { ++ goto err; ++ } ++ ++ optstr = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); ++ has_backing_file = !!optstr; ++ g_free(optstr); ++ ++ optstr = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT); ++ has_luks = optstr && strcmp(optstr, "luks") == 0; ++ g_free(optstr); ++ ++ if (has_luks) { ++ g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL; ++ QDict *cryptoopts = qcow2_extract_crypto_opts(opts, "luks", errp); ++ size_t headerlen; ++ ++ create_opts = block_crypto_create_opts_init(cryptoopts, errp); ++ qobject_unref(cryptoopts); ++ if (!create_opts) { ++ goto err; ++ } ++ ++ if (!qcrypto_block_calculate_payload_offset(create_opts, ++ "encrypt.", ++ &headerlen, ++ &local_err)) { ++ goto err; ++ } ++ ++ luks_payload_size = ROUND_UP(headerlen, cluster_size); ++ } ++ ++ virtual_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); ++ virtual_size = ROUND_UP(virtual_size, cluster_size); ++ ++ /* Check that virtual disk size is valid */ ++ l2e_size = extended_l2 ? L2E_SIZE_EXTENDED : L2E_SIZE_NORMAL; ++ l2_tables = DIV_ROUND_UP(virtual_size / cluster_size, ++ cluster_size / l2e_size); ++ if (l2_tables * L1E_SIZE > QCOW_MAX_L1_SIZE) { ++ error_setg(&local_err, "The image size is too large " ++ "(try using a larger cluster size)"); ++ goto err; ++ } ++ ++ /* Account for input image */ ++ if (in_bs) { ++ int64_t ssize = bdrv_getlength(in_bs); ++ if (ssize < 0) { ++ error_setg_errno(&local_err, -ssize, ++ "Unable to get image virtual_size"); ++ goto err; ++ } ++ ++ virtual_size = ROUND_UP(ssize, cluster_size); ++ ++ if (has_backing_file) { ++ /* We don't how much of the backing chain is shared by the input ++ * image and the new image file. In the worst case the new image's ++ * backing file has nothing in common with the input image. Be ++ * conservative and assume all clusters need to be written. ++ */ ++ required = virtual_size; ++ } else { ++ int64_t offset; ++ int64_t pnum = 0; ++ ++ for (offset = 0; offset < ssize; offset += pnum) { ++ int ret; ++ ++ ret = bdrv_block_status_above(in_bs, NULL, offset, ++ ssize - offset, &pnum, NULL, ++ NULL); ++ if (ret < 0) { ++ error_setg_errno(&local_err, -ret, ++ "Unable to get block status"); ++ goto err; ++ } ++ ++ if (ret & BDRV_BLOCK_ZERO) { ++ /* Skip zero regions (safe with no backing file) */ ++ } else if ((ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED)) == ++ (BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED)) { ++ /* Extend pnum to end of cluster for next iteration */ ++ pnum = ROUND_UP(offset + pnum, cluster_size) - offset; ++ ++ /* Count clusters we've seen */ ++ required += offset % cluster_size + pnum; ++ } ++ } ++ } ++ } ++ ++ /* Take into account preallocation. Nothing special is needed for ++ * PREALLOC_MODE_METADATA since metadata is always counted. ++ */ ++ if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) { ++ required = virtual_size; ++ } ++ ++ info = g_new0(BlockMeasureInfo, 1); ++ info->fully_allocated = luks_payload_size + ++ qcow2_calc_prealloc_size(virtual_size, cluster_size, ++ ctz32(refcount_bits), extended_l2); ++ ++ /* ++ * Remove data clusters that are not required. This overestimates the ++ * required size because metadata needed for the fully allocated file is ++ * still counted. Show bitmaps only if both source and destination ++ * would support them. ++ */ ++ info->required = info->fully_allocated - virtual_size + required; ++ info->has_bitmaps = version >= 3 && in_bs && ++ bdrv_supports_persistent_dirty_bitmap(in_bs); ++ if (info->has_bitmaps) { ++ info->bitmaps = qcow2_get_persistent_dirty_bitmap_size(in_bs, ++ cluster_size); ++ } ++ return info; ++ ++err: ++ error_propagate(errp, local_err); ++ return NULL; ++} ++ ++static int coroutine_fn ++qcow2_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ bdi->cluster_size = s->cluster_size; ++ bdi->subcluster_size = s->subcluster_size; ++ bdi->vm_state_offset = qcow2_vm_state_offset(s); ++ bdi->is_dirty = s->incompatible_features & QCOW2_INCOMPAT_DIRTY; ++ return 0; ++} ++ ++static ImageInfoSpecific * GRAPH_RDLOCK ++qcow2_get_specific_info(BlockDriverState *bs, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ImageInfoSpecific *spec_info; ++ QCryptoBlockInfo *encrypt_info = NULL; ++ ++ if (s->crypto != NULL) { ++ encrypt_info = qcrypto_block_get_info(s->crypto, errp); ++ if (!encrypt_info) { ++ return NULL; ++ } ++ } ++ ++ spec_info = g_new(ImageInfoSpecific, 1); ++ *spec_info = (ImageInfoSpecific){ ++ .type = IMAGE_INFO_SPECIFIC_KIND_QCOW2, ++ .u.qcow2.data = g_new0(ImageInfoSpecificQCow2, 1), ++ }; ++ if (s->qcow_version == 2) { ++ *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){ ++ .compat = g_strdup("0.10"), ++ .refcount_bits = s->refcount_bits, ++ }; ++ } else if (s->qcow_version == 3) { ++ Qcow2BitmapInfoList *bitmaps; ++ if (!qcow2_get_bitmap_info_list(bs, &bitmaps, errp)) { ++ qapi_free_ImageInfoSpecific(spec_info); ++ qapi_free_QCryptoBlockInfo(encrypt_info); ++ return NULL; ++ } ++ *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){ ++ .compat = g_strdup("1.1"), ++ .lazy_refcounts = s->compatible_features & ++ QCOW2_COMPAT_LAZY_REFCOUNTS, ++ .has_lazy_refcounts = true, ++ .corrupt = s->incompatible_features & ++ QCOW2_INCOMPAT_CORRUPT, ++ .has_corrupt = true, ++ .has_extended_l2 = true, ++ .extended_l2 = has_subclusters(s), ++ .refcount_bits = s->refcount_bits, ++ .has_bitmaps = !!bitmaps, ++ .bitmaps = bitmaps, ++ .data_file = g_strdup(s->image_data_file), ++ .has_data_file_raw = has_data_file(bs), ++ .data_file_raw = data_file_is_raw(bs), ++ .compression_type = s->compression_type, ++ }; ++ } else { ++ /* if this assertion fails, this probably means a new version was ++ * added without having it covered here */ ++ assert(false); ++ } ++ ++ if (encrypt_info) { ++ ImageInfoSpecificQCow2Encryption *qencrypt = ++ g_new(ImageInfoSpecificQCow2Encryption, 1); ++ switch (encrypt_info->format) { ++ case Q_CRYPTO_BLOCK_FORMAT_QCOW: ++ qencrypt->format = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES; ++ break; ++ case Q_CRYPTO_BLOCK_FORMAT_LUKS: ++ qencrypt->format = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS; ++ qencrypt->u.luks = encrypt_info->u.luks; ++ break; ++ default: ++ abort(); ++ } ++ /* Since we did shallow copy above, erase any pointers ++ * in the original info */ ++ memset(&encrypt_info->u, 0, sizeof(encrypt_info->u)); ++ qapi_free_QCryptoBlockInfo(encrypt_info); ++ ++ spec_info->u.qcow2.data->encrypt = qencrypt; ++ } ++ ++ return spec_info; ++} ++ ++static int coroutine_mixed_fn GRAPH_RDLOCK ++qcow2_has_zero_init(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ bool preallocated; ++ ++ if (qemu_in_coroutine()) { ++ qemu_co_mutex_lock(&s->lock); ++ } ++ /* ++ * Check preallocation status: Preallocated images have all L2 ++ * tables allocated, nonpreallocated images have none. It is ++ * therefore enough to check the first one. ++ */ ++ preallocated = s->l1_size > 0 && s->l1_table[0] != 0; ++ if (qemu_in_coroutine()) { ++ qemu_co_mutex_unlock(&s->lock); ++ } ++ ++ if (!preallocated) { ++ return 1; ++ } else if (bs->encrypted) { ++ return 0; ++ } else { ++ return bdrv_has_zero_init(s->data_file->bs); ++ } ++} ++ ++/* ++ * Check the request to vmstate. On success return ++ * qcow2_vm_state_offset(bs) + @pos ++ */ ++static int64_t qcow2_check_vmstate_request(BlockDriverState *bs, ++ QEMUIOVector *qiov, int64_t pos) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t vmstate_offset = qcow2_vm_state_offset(s); ++ int ret; ++ ++ /* Incoming requests must be OK */ ++ bdrv_check_qiov_request(pos, qiov->size, qiov, 0, &error_abort); ++ ++ if (INT64_MAX - pos < vmstate_offset) { ++ return -EIO; ++ } ++ ++ pos += vmstate_offset; ++ ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return pos; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ++{ ++ int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos); ++ if (offset < 0) { ++ return offset; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_VMSTATE_SAVE); ++ return bs->drv->bdrv_co_pwritev_part(bs, offset, qiov->size, qiov, 0, 0); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ++{ ++ int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos); ++ if (offset < 0) { ++ return offset; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_VMSTATE_LOAD); ++ return bs->drv->bdrv_co_preadv_part(bs, offset, qiov->size, qiov, 0, 0); ++} ++ ++static int GRAPH_RDLOCK qcow2_has_compressed_clusters(BlockDriverState *bs) ++{ ++ int64_t offset = 0; ++ int64_t bytes = bdrv_getlength(bs); ++ ++ if (bytes < 0) { ++ return bytes; ++ } ++ ++ while (bytes != 0) { ++ int ret; ++ QCow2SubclusterType type; ++ unsigned int cur_bytes = MIN(INT_MAX, bytes); ++ uint64_t host_offset; ++ ++ ret = qcow2_get_host_offset(bs, offset, &cur_bytes, &host_offset, ++ &type); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (type == QCOW2_SUBCLUSTER_COMPRESSED) { ++ return 1; ++ } ++ ++ offset += cur_bytes; ++ bytes -= cur_bytes; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Downgrades an image's version. To achieve this, any incompatible features ++ * have to be removed. ++ */ ++static int GRAPH_RDLOCK ++qcow2_downgrade(BlockDriverState *bs, int target_version, ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int current_version = s->qcow_version; ++ int ret; ++ int i; ++ ++ /* This is qcow2_downgrade(), not qcow2_upgrade() */ ++ assert(target_version < current_version); ++ ++ /* There are no other versions (now) that you can downgrade to */ ++ assert(target_version == 2); ++ ++ if (s->refcount_order != 4) { ++ error_setg(errp, "compat=0.10 requires refcount_bits=16"); ++ return -ENOTSUP; ++ } ++ ++ if (has_data_file(bs)) { ++ error_setg(errp, "Cannot downgrade an image with a data file"); ++ return -ENOTSUP; ++ } ++ ++ /* ++ * If any internal snapshot has a different size than the current ++ * image size, or VM state size that exceeds 32 bits, downgrading ++ * is unsafe. Even though we would still use v3-compliant output ++ * to preserve that data, other v2 programs might not realize ++ * those optional fields are important. ++ */ ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (s->snapshots[i].vm_state_size > UINT32_MAX || ++ s->snapshots[i].disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) { ++ error_setg(errp, "Internal snapshots prevent downgrade of image"); ++ return -ENOTSUP; ++ } ++ } ++ ++ /* clear incompatible features */ ++ if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { ++ ret = qcow2_mark_clean(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to make the image clean"); ++ return ret; ++ } ++ } ++ ++ /* with QCOW2_INCOMPAT_CORRUPT, it is pretty much impossible to get here in ++ * the first place; if that happens nonetheless, returning -ENOTSUP is the ++ * best thing to do anyway */ ++ ++ if (s->incompatible_features & ~QCOW2_INCOMPAT_COMPRESSION) { ++ error_setg(errp, "Cannot downgrade an image with incompatible features " ++ "0x%" PRIx64 " set", ++ s->incompatible_features & ~QCOW2_INCOMPAT_COMPRESSION); ++ return -ENOTSUP; ++ } ++ ++ /* since we can ignore compatible features, we can set them to 0 as well */ ++ s->compatible_features = 0; ++ /* if lazy refcounts have been used, they have already been fixed through ++ * clearing the dirty flag */ ++ ++ /* clearing autoclear features is trivial */ ++ s->autoclear_features = 0; ++ ++ ret = qcow2_expand_zero_clusters(bs, status_cb, cb_opaque); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to turn zero into data clusters"); ++ return ret; ++ } ++ ++ if (s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION) { ++ ret = qcow2_has_compressed_clusters(bs); ++ if (ret < 0) { ++ error_setg(errp, "Failed to check block status"); ++ return -EINVAL; ++ } ++ if (ret) { ++ error_setg(errp, "Cannot downgrade an image with zstd compression " ++ "type and existing compressed clusters"); ++ return -ENOTSUP; ++ } ++ /* ++ * No compressed clusters for now, so just chose default zlib ++ * compression. ++ */ ++ s->incompatible_features &= ~QCOW2_INCOMPAT_COMPRESSION; ++ s->compression_type = QCOW2_COMPRESSION_TYPE_ZLIB; ++ } ++ ++ assert(s->incompatible_features == 0); ++ ++ s->qcow_version = target_version; ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ s->qcow_version = current_version; ++ error_setg_errno(errp, -ret, "Failed to update the image header"); ++ return ret; ++ } ++ return 0; ++} ++ ++/* ++ * Upgrades an image's version. While newer versions encompass all ++ * features of older versions, some things may have to be presented ++ * differently. ++ */ ++static int GRAPH_RDLOCK ++qcow2_upgrade(BlockDriverState *bs, int target_version, ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ bool need_snapshot_update; ++ int current_version = s->qcow_version; ++ int i; ++ int ret; ++ ++ /* This is qcow2_upgrade(), not qcow2_downgrade() */ ++ assert(target_version > current_version); ++ ++ /* There are no other versions (yet) that you can upgrade to */ ++ assert(target_version == 3); ++ ++ status_cb(bs, 0, 2, cb_opaque); ++ ++ /* ++ * In v2, snapshots do not need to have extra data. v3 requires ++ * the 64-bit VM state size and the virtual disk size to be ++ * present. ++ * qcow2_write_snapshots() will always write the list in the ++ * v3-compliant format. ++ */ ++ need_snapshot_update = false; ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (s->snapshots[i].extra_data_size < ++ sizeof_field(QCowSnapshotExtraData, vm_state_size_large) + ++ sizeof_field(QCowSnapshotExtraData, disk_size)) ++ { ++ need_snapshot_update = true; ++ break; ++ } ++ } ++ if (need_snapshot_update) { ++ ret = qcow2_write_snapshots(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to update the snapshot table"); ++ return ret; ++ } ++ } ++ status_cb(bs, 1, 2, cb_opaque); ++ ++ s->qcow_version = target_version; ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ s->qcow_version = current_version; ++ error_setg_errno(errp, -ret, "Failed to update the image header"); ++ return ret; ++ } ++ status_cb(bs, 2, 2, cb_opaque); ++ ++ return 0; ++} ++ ++typedef enum Qcow2AmendOperation { ++ /* This is the value Qcow2AmendHelperCBInfo::last_operation will be ++ * statically initialized to so that the helper CB can discern the first ++ * invocation from an operation change */ ++ QCOW2_NO_OPERATION = 0, ++ ++ QCOW2_UPGRADING, ++ QCOW2_UPDATING_ENCRYPTION, ++ QCOW2_CHANGING_REFCOUNT_ORDER, ++ QCOW2_DOWNGRADING, ++} Qcow2AmendOperation; ++ ++typedef struct Qcow2AmendHelperCBInfo { ++ /* The code coordinating the amend operations should only modify ++ * these four fields; the rest will be managed by the CB */ ++ BlockDriverAmendStatusCB *original_status_cb; ++ void *original_cb_opaque; ++ ++ Qcow2AmendOperation current_operation; ++ ++ /* Total number of operations to perform (only set once) */ ++ int total_operations; ++ ++ /* The following fields are managed by the CB */ ++ ++ /* Number of operations completed */ ++ int operations_completed; ++ ++ /* Cumulative offset of all completed operations */ ++ int64_t offset_completed; ++ ++ Qcow2AmendOperation last_operation; ++ int64_t last_work_size; ++} Qcow2AmendHelperCBInfo; ++ ++static void qcow2_amend_helper_cb(BlockDriverState *bs, ++ int64_t operation_offset, ++ int64_t operation_work_size, void *opaque) ++{ ++ Qcow2AmendHelperCBInfo *info = opaque; ++ int64_t current_work_size; ++ int64_t projected_work_size; ++ ++ if (info->current_operation != info->last_operation) { ++ if (info->last_operation != QCOW2_NO_OPERATION) { ++ info->offset_completed += info->last_work_size; ++ info->operations_completed++; ++ } ++ ++ info->last_operation = info->current_operation; ++ } ++ ++ assert(info->total_operations > 0); ++ assert(info->operations_completed < info->total_operations); ++ ++ info->last_work_size = operation_work_size; ++ ++ current_work_size = info->offset_completed + operation_work_size; ++ ++ /* current_work_size is the total work size for (operations_completed + 1) ++ * operations (which includes this one), so multiply it by the number of ++ * operations not covered and divide it by the number of operations ++ * covered to get a projection for the operations not covered */ ++ projected_work_size = current_work_size * (info->total_operations - ++ info->operations_completed - 1) ++ / (info->operations_completed + 1); ++ ++ info->original_status_cb(bs, info->offset_completed + operation_offset, ++ current_work_size + projected_work_size, ++ info->original_cb_opaque); ++} ++ ++static int GRAPH_RDLOCK ++qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ bool force, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int old_version = s->qcow_version, new_version = old_version; ++ uint64_t new_size = 0; ++ const char *backing_file = NULL, *backing_format = NULL, *data_file = NULL; ++ bool lazy_refcounts = s->use_lazy_refcounts; ++ bool data_file_raw = data_file_is_raw(bs); ++ const char *compat = NULL; ++ int refcount_bits = s->refcount_bits; ++ int ret; ++ QemuOptDesc *desc = opts->list->desc; ++ Qcow2AmendHelperCBInfo helper_cb_info; ++ bool encryption_update = false; ++ ++ while (desc && desc->name) { ++ if (!qemu_opt_find(opts, desc->name)) { ++ /* only change explicitly defined options */ ++ desc++; ++ continue; ++ } ++ ++ if (!strcmp(desc->name, BLOCK_OPT_COMPAT_LEVEL)) { ++ compat = qemu_opt_get(opts, BLOCK_OPT_COMPAT_LEVEL); ++ if (!compat) { ++ /* preserve default */ ++ } else if (!strcmp(compat, "0.10") || !strcmp(compat, "v2")) { ++ new_version = 2; ++ } else if (!strcmp(compat, "1.1") || !strcmp(compat, "v3")) { ++ new_version = 3; ++ } else { ++ error_setg(errp, "Unknown compatibility level %s", compat); ++ return -EINVAL; ++ } ++ } else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) { ++ new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); ++ } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FILE)) { ++ backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); ++ } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FMT)) { ++ backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); ++ } else if (g_str_has_prefix(desc->name, "encrypt.")) { ++ if (!s->crypto) { ++ error_setg(errp, ++ "Can't amend encryption options - encryption not present"); ++ return -EINVAL; ++ } ++ if (s->crypt_method_header != QCOW_CRYPT_LUKS) { ++ error_setg(errp, ++ "Only LUKS encryption options can be amended"); ++ return -ENOTSUP; ++ } ++ encryption_update = true; ++ } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { ++ lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS, ++ lazy_refcounts); ++ } else if (!strcmp(desc->name, BLOCK_OPT_REFCOUNT_BITS)) { ++ refcount_bits = qemu_opt_get_number(opts, BLOCK_OPT_REFCOUNT_BITS, ++ refcount_bits); ++ ++ if (refcount_bits <= 0 || refcount_bits > 64 || ++ !is_power_of_2(refcount_bits)) ++ { ++ error_setg(errp, "Refcount width must be a power of two and " ++ "may not exceed 64 bits"); ++ return -EINVAL; ++ } ++ } else if (!strcmp(desc->name, BLOCK_OPT_DATA_FILE)) { ++ data_file = qemu_opt_get(opts, BLOCK_OPT_DATA_FILE); ++ if (data_file && !has_data_file(bs)) { ++ error_setg(errp, "data-file can only be set for images that " ++ "use an external data file"); ++ return -EINVAL; ++ } ++ } else if (!strcmp(desc->name, BLOCK_OPT_DATA_FILE_RAW)) { ++ data_file_raw = qemu_opt_get_bool(opts, BLOCK_OPT_DATA_FILE_RAW, ++ data_file_raw); ++ if (data_file_raw && !data_file_is_raw(bs)) { ++ error_setg(errp, "data-file-raw cannot be set on existing " ++ "images"); ++ return -EINVAL; ++ } ++ } else { ++ /* if this point is reached, this probably means a new option was ++ * added without having it covered here */ ++ abort(); ++ } ++ ++ desc++; ++ } ++ ++ helper_cb_info = (Qcow2AmendHelperCBInfo){ ++ .original_status_cb = status_cb, ++ .original_cb_opaque = cb_opaque, ++ .total_operations = (new_version != old_version) ++ + (s->refcount_bits != refcount_bits) + ++ (encryption_update == true) ++ }; ++ ++ /* Upgrade first (some features may require compat=1.1) */ ++ if (new_version > old_version) { ++ helper_cb_info.current_operation = QCOW2_UPGRADING; ++ ret = qcow2_upgrade(bs, new_version, &qcow2_amend_helper_cb, ++ &helper_cb_info, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ if (encryption_update) { ++ QDict *amend_opts_dict; ++ QCryptoBlockAmendOptions *amend_opts; ++ ++ helper_cb_info.current_operation = QCOW2_UPDATING_ENCRYPTION; ++ amend_opts_dict = qcow2_extract_crypto_opts(opts, "luks", errp); ++ if (!amend_opts_dict) { ++ return -EINVAL; ++ } ++ amend_opts = block_crypto_amend_opts_init(amend_opts_dict, errp); ++ qobject_unref(amend_opts_dict); ++ if (!amend_opts) { ++ return -EINVAL; ++ } ++ ret = qcrypto_block_amend_options(s->crypto, ++ qcow2_crypto_hdr_read_func, ++ qcow2_crypto_hdr_write_func, ++ bs, ++ amend_opts, ++ force, ++ errp); ++ qapi_free_QCryptoBlockAmendOptions(amend_opts); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ if (s->refcount_bits != refcount_bits) { ++ int refcount_order = ctz32(refcount_bits); ++ ++ if (new_version < 3 && refcount_bits != 16) { ++ error_setg(errp, "Refcount widths other than 16 bits require " ++ "compatibility level 1.1 or above (use compat=1.1 or " ++ "greater)"); ++ return -EINVAL; ++ } ++ ++ helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER; ++ ret = qcow2_change_refcount_order(bs, refcount_order, ++ &qcow2_amend_helper_cb, ++ &helper_cb_info, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ /* data-file-raw blocks backing files, so clear it first if requested */ ++ if (data_file_raw) { ++ s->autoclear_features |= QCOW2_AUTOCLEAR_DATA_FILE_RAW; ++ } else { ++ s->autoclear_features &= ~QCOW2_AUTOCLEAR_DATA_FILE_RAW; ++ } ++ ++ if (data_file) { ++ g_free(s->image_data_file); ++ s->image_data_file = *data_file ? g_strdup(data_file) : NULL; ++ } ++ ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to update the image header"); ++ return ret; ++ } ++ ++ if (backing_file || backing_format) { ++ if (g_strcmp0(backing_file, s->image_backing_file) || ++ g_strcmp0(backing_format, s->image_backing_format)) { ++ error_setg(errp, "Cannot amend the backing file"); ++ error_append_hint(errp, ++ "You can use 'qemu-img rebase' instead.\n"); ++ return -EINVAL; ++ } ++ } ++ ++ if (s->use_lazy_refcounts != lazy_refcounts) { ++ if (lazy_refcounts) { ++ if (new_version < 3) { ++ error_setg(errp, "Lazy refcounts only supported with " ++ "compatibility level 1.1 and above (use compat=1.1 " ++ "or greater)"); ++ return -EINVAL; ++ } ++ s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; ++ error_setg_errno(errp, -ret, "Failed to update the image header"); ++ return ret; ++ } ++ s->use_lazy_refcounts = true; ++ } else { ++ /* make image clean first */ ++ ret = qcow2_mark_clean(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to make the image clean"); ++ return ret; ++ } ++ /* now disallow lazy refcounts */ ++ s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; ++ error_setg_errno(errp, -ret, "Failed to update the image header"); ++ return ret; ++ } ++ s->use_lazy_refcounts = false; ++ } ++ } ++ ++ if (new_size) { ++ BlockBackend *blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, ++ errp); ++ if (!blk) { ++ return -EPERM; ++ } ++ ++ /* ++ * Amending image options should ensure that the image has ++ * exactly the given new values, so pass exact=true here. ++ */ ++ ret = blk_truncate(blk, new_size, true, PREALLOC_MODE_OFF, 0, errp); ++ blk_unref(blk); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ /* Downgrade last (so unsupported features can be removed before) */ ++ if (new_version < old_version) { ++ helper_cb_info.current_operation = QCOW2_DOWNGRADING; ++ ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb, ++ &helper_cb_info, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int coroutine_fn qcow2_co_amend(BlockDriverState *bs, ++ BlockdevAmendOptions *opts, ++ bool force, ++ Error **errp) ++{ ++ BlockdevAmendOptionsQcow2 *qopts = &opts->u.qcow2; ++ BDRVQcow2State *s = bs->opaque; ++ int ret = 0; ++ ++ if (qopts->encrypt) { ++ if (!s->crypto) { ++ error_setg(errp, "image is not encrypted, can't amend"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (qopts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) { ++ error_setg(errp, ++ "Amend can't be used to change the qcow2 encryption format"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (s->crypt_method_header != QCOW_CRYPT_LUKS) { ++ error_setg(errp, ++ "Only LUKS encryption options can be amended for qcow2 with blockdev-amend"); ++ return -EOPNOTSUPP; ++ } ++ ++ ret = qcrypto_block_amend_options(s->crypto, ++ qcow2_crypto_hdr_read_func, ++ qcow2_crypto_hdr_write_func, ++ bs, ++ qopts->encrypt, ++ force, ++ errp); ++ } ++ return ret; ++} ++ ++/* ++ * If offset or size are negative, respectively, they will not be included in ++ * the BLOCK_IMAGE_CORRUPTED event emitted. ++ * fatal will be ignored for read-only BDS; corruptions found there will always ++ * be considered non-fatal. ++ */ ++void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, ++ int64_t size, const char *message_format, ...) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ const char *node_name; ++ char *message; ++ va_list ap; ++ ++ fatal = fatal && bdrv_is_writable(bs); ++ ++ if (s->signaled_corruption && ++ (!fatal || (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT))) ++ { ++ return; ++ } ++ ++ va_start(ap, message_format); ++ message = g_strdup_vprintf(message_format, ap); ++ va_end(ap); ++ ++ if (fatal) { ++ fprintf(stderr, "qcow2: Marking image as corrupt: %s; further " ++ "corruption events will be suppressed\n", message); ++ } else { ++ fprintf(stderr, "qcow2: Image is corrupt: %s; further non-fatal " ++ "corruption events will be suppressed\n", message); ++ } ++ ++ node_name = bdrv_get_node_name(bs); ++ qapi_event_send_block_image_corrupted(bdrv_get_device_name(bs), ++ *node_name ? node_name : NULL, ++ message, offset >= 0, offset, ++ size >= 0, size, ++ fatal); ++ g_free(message); ++ ++ if (fatal) { ++ qcow2_mark_corrupt(bs); ++ bs->drv = NULL; /* make BDS unusable */ ++ } ++ ++ s->signaled_corruption = true; ++} ++ ++#define QCOW_COMMON_OPTIONS \ ++ { \ ++ .name = BLOCK_OPT_SIZE, \ ++ .type = QEMU_OPT_SIZE, \ ++ .help = "Virtual disk size" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_COMPAT_LEVEL, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Compatibility level (v2 [0.10] or v3 [1.1])" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_BACKING_FILE, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "File name of a base image" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_BACKING_FMT, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Image format of the base image" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_DATA_FILE, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "File name of an external data file" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_DATA_FILE_RAW, \ ++ .type = QEMU_OPT_BOOL, \ ++ .help = "The external data file must stay valid " \ ++ "as a raw image" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_LAZY_REFCOUNTS, \ ++ .type = QEMU_OPT_BOOL, \ ++ .help = "Postpone refcount updates", \ ++ .def_value_str = "off" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_REFCOUNT_BITS, \ ++ .type = QEMU_OPT_NUMBER, \ ++ .help = "Width of a reference count entry in bits", \ ++ .def_value_str = "16" \ ++ } ++ ++static QemuOptsList qcow2_create_opts = { ++ .name = "qcow2-create-opts", ++ .head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head), ++ .desc = { ++ { \ ++ .name = BLOCK_OPT_ENCRYPT, \ ++ .type = QEMU_OPT_BOOL, \ ++ .help = "Encrypt the image with format 'aes'. (Deprecated " \ ++ "in favor of " BLOCK_OPT_ENCRYPT_FORMAT "=aes)", \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_ENCRYPT_FORMAT, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Encrypt the image, format choices: 'aes', 'luks'", \ ++ }, \ ++ BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", \ ++ "ID of secret providing qcow AES key or LUKS passphrase"), \ ++ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."), \ ++ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."), \ ++ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."), \ ++ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."), \ ++ BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."), \ ++ BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), \ ++ { \ ++ .name = BLOCK_OPT_CLUSTER_SIZE, \ ++ .type = QEMU_OPT_SIZE, \ ++ .help = "qcow2 cluster size", \ ++ .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_EXTL2, \ ++ .type = QEMU_OPT_BOOL, \ ++ .help = "Extended L2 tables", \ ++ .def_value_str = "off" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_PREALLOC, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Preallocation mode (allowed values: off, " \ ++ "metadata, falloc, full)" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_COMPRESSION_TYPE, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Compression method used for image cluster " \ ++ "compression", \ ++ .def_value_str = "zlib" \ ++ }, ++ QCOW_COMMON_OPTIONS, ++ { /* end of list */ } ++ } ++}; ++ ++static QemuOptsList qcow2_amend_opts = { ++ .name = "qcow2-amend-opts", ++ .head = QTAILQ_HEAD_INITIALIZER(qcow2_amend_opts.head), ++ .desc = { ++ BLOCK_CRYPTO_OPT_DEF_LUKS_STATE("encrypt."), ++ BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT("encrypt."), ++ BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET("encrypt."), ++ BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET("encrypt."), ++ BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), ++ QCOW_COMMON_OPTIONS, ++ { /* end of list */ } ++ } ++}; ++ ++static const char *const qcow2_strong_runtime_opts[] = { ++ "encrypt." BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET, ++ ++ NULL ++}; ++ ++BlockDriver bdrv_qcow2 = { ++ .format_name = "qcow2", ++ .instance_size = sizeof(BDRVQcow2State), ++ .bdrv_probe = qcow2_probe, ++ .bdrv_open = qcow2_open, ++ .bdrv_close = qcow2_close, ++ .bdrv_reopen_prepare = qcow2_reopen_prepare, ++ .bdrv_reopen_commit = qcow2_reopen_commit, ++ .bdrv_reopen_commit_post = qcow2_reopen_commit_post, ++ .bdrv_reopen_abort = qcow2_reopen_abort, ++ .bdrv_join_options = qcow2_join_options, ++ .bdrv_child_perm = bdrv_default_perms, ++ .bdrv_co_create_opts = qcow2_co_create_opts, ++ .bdrv_co_create = qcow2_co_create, ++ .bdrv_has_zero_init = qcow2_has_zero_init, ++ .bdrv_co_block_status = qcow2_co_block_status, ++ ++ .bdrv_co_preadv_part = qcow2_co_preadv_part, ++ .bdrv_co_pwritev_part = qcow2_co_pwritev_part, ++ .bdrv_co_flush_to_os = qcow2_co_flush_to_os, ++ ++ .bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes, ++ .bdrv_co_pdiscard = qcow2_co_pdiscard, ++ .bdrv_co_copy_range_from = qcow2_co_copy_range_from, ++ .bdrv_co_copy_range_to = qcow2_co_copy_range_to, ++ .bdrv_co_truncate = qcow2_co_truncate, ++ .bdrv_co_pwritev_compressed_part = qcow2_co_pwritev_compressed_part, ++ .bdrv_make_empty = qcow2_make_empty, ++ ++ .bdrv_snapshot_create = qcow2_snapshot_create, ++ .bdrv_snapshot_goto = qcow2_snapshot_goto, ++ .bdrv_snapshot_delete = qcow2_snapshot_delete, ++ .bdrv_snapshot_list = qcow2_snapshot_list, ++ .bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp, ++ .bdrv_measure = qcow2_measure, ++ .bdrv_co_get_info = qcow2_co_get_info, ++ .bdrv_get_specific_info = qcow2_get_specific_info, ++ ++ .bdrv_co_save_vmstate = qcow2_co_save_vmstate, ++ .bdrv_co_load_vmstate = qcow2_co_load_vmstate, ++ ++ .is_format = true, ++ .supports_backing = true, ++ .bdrv_co_change_backing_file = qcow2_co_change_backing_file, ++ ++ .bdrv_refresh_limits = qcow2_refresh_limits, ++ .bdrv_co_invalidate_cache = qcow2_co_invalidate_cache, ++ .bdrv_inactivate = qcow2_inactivate, ++ ++ .create_opts = &qcow2_create_opts, ++ .amend_opts = &qcow2_amend_opts, ++ .strong_runtime_opts = qcow2_strong_runtime_opts, ++ .mutable_opts = mutable_opts, ++ .bdrv_co_check = qcow2_co_check, ++ .bdrv_amend_options = qcow2_amend_options, ++ .bdrv_co_amend = qcow2_co_amend, ++ ++ .bdrv_detach_aio_context = qcow2_detach_aio_context, ++ .bdrv_attach_aio_context = qcow2_attach_aio_context, ++ ++ .bdrv_supports_persistent_dirty_bitmap = ++ qcow2_supports_persistent_dirty_bitmap, ++ .bdrv_co_can_store_new_dirty_bitmap = qcow2_co_can_store_new_dirty_bitmap, ++ .bdrv_co_remove_persistent_dirty_bitmap = ++ qcow2_co_remove_persistent_dirty_bitmap, ++}; ++ ++static void bdrv_qcow2_init(void) ++{ ++ bdrv_register(&bdrv_qcow2); ++} ++ ++block_init(bdrv_qcow2_init); +diff --git a/qcow2/lib/qobject/block-qdict.c b/qcow2/lib/qobject/block-qdict.c +new file mode 100644 +index 00000000..4a83bda2 +--- /dev/null ++++ b/qcow2/lib/qobject/block-qdict.c +@@ -0,0 +1,738 @@ ++/* ++ * Special QDict functions used by the block layer ++ * ++ * Copyright (c) 2013-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/qdict.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qemu/cutils.h" ++#include "qapi/error.h" ++ ++/** ++ * qdict_copy_default(): If no entry mapped by 'key' exists in 'dst' yet, the ++ * value of 'key' in 'src' is copied there (and the refcount increased ++ * accordingly). ++ */ ++void qdict_copy_default(QDict *dst, QDict *src, const char *key) ++{ ++ QObject *val; ++ ++ if (qdict_haskey(dst, key)) { ++ return; ++ } ++ ++ val = qdict_get(src, key); ++ if (val) { ++ qdict_put_obj(dst, key, qobject_ref(val)); ++ } ++} ++ ++/** ++ * qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a ++ * new QString initialised by 'val' is put there. ++ */ ++void qdict_set_default_str(QDict *dst, const char *key, const char *val) ++{ ++ if (qdict_haskey(dst, key)) { ++ return; ++ } ++ ++ qdict_put_str(dst, key, val); ++} ++ ++static void qdict_flatten_qdict(QDict *qdict, QDict *target, ++ const char *prefix); ++ ++static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) ++{ ++ QObject *value; ++ const QListEntry *entry; ++ QDict *dict_val; ++ QList *list_val; ++ char *new_key; ++ int i; ++ ++ /* This function is never called with prefix == NULL, i.e., it is always ++ * called from within qdict_flatten_q(list|dict)(). Therefore, it does not ++ * need to remove list entries during the iteration (the whole list will be ++ * deleted eventually anyway from qdict_flatten_qdict()). */ ++ assert(prefix); ++ ++ entry = qlist_first(qlist); ++ ++ for (i = 0; entry; entry = qlist_next(entry), i++) { ++ value = qlist_entry_obj(entry); ++ dict_val = qobject_to(QDict, value); ++ list_val = qobject_to(QList, value); ++ new_key = g_strdup_printf("%s.%i", prefix, i); ++ ++ /* ++ * Flatten non-empty QDict and QList recursively into @target, ++ * copy other objects to @target ++ */ ++ if (dict_val && qdict_size(dict_val)) { ++ qdict_flatten_qdict(dict_val, target, new_key); ++ } else if (list_val && !qlist_empty(list_val)) { ++ qdict_flatten_qlist(list_val, target, new_key); ++ } else { ++ qdict_put_obj(target, new_key, qobject_ref(value)); ++ } ++ ++ g_free(new_key); ++ } ++} ++ ++static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) ++{ ++ QObject *value; ++ const QDictEntry *entry, *next; ++ QDict *dict_val; ++ QList *list_val; ++ char *key, *new_key; ++ ++ entry = qdict_first(qdict); ++ ++ while (entry != NULL) { ++ next = qdict_next(qdict, entry); ++ value = qdict_entry_value(entry); ++ dict_val = qobject_to(QDict, value); ++ list_val = qobject_to(QList, value); ++ ++ if (prefix) { ++ key = new_key = g_strdup_printf("%s.%s", prefix, entry->key); ++ } else { ++ key = entry->key; ++ new_key = NULL; ++ } ++ ++ /* ++ * Flatten non-empty QDict and QList recursively into @target, ++ * copy other objects to @target. ++ * On the root level (if @qdict == @target), remove flattened ++ * nested QDicts and QLists from @qdict. ++ * ++ * (Note that we do not need to remove entries from nested ++ * dicts or lists. Their reference count is decremented on ++ * the root level, so there are no leaks. In fact, if they ++ * have a reference count greater than one, we are probably ++ * well advised not to modify them altogether.) ++ */ ++ if (dict_val && qdict_size(dict_val)) { ++ qdict_flatten_qdict(dict_val, target, key); ++ if (target == qdict) { ++ qdict_del(qdict, entry->key); ++ } ++ } else if (list_val && !qlist_empty(list_val)) { ++ qdict_flatten_qlist(list_val, target, key); ++ if (target == qdict) { ++ qdict_del(qdict, entry->key); ++ } ++ } else if (target != qdict) { ++ qdict_put_obj(target, key, qobject_ref(value)); ++ } ++ ++ g_free(new_key); ++ entry = next; ++ } ++} ++ ++/** ++ * qdict_flatten(): For each nested non-empty QDict with key x, all ++ * fields with key y are moved to this QDict and their key is renamed ++ * to "x.y". For each nested non-empty QList with key x, the field at ++ * index y is moved to this QDict with the key "x.y" (i.e., the ++ * reverse of what qdict_array_split() does). ++ * This operation is applied recursively for nested QDicts and QLists. ++ */ ++void qdict_flatten(QDict *qdict) ++{ ++ qdict_flatten_qdict(qdict, qdict, NULL); ++} ++ ++/* extract all the src QDict entries starting by start into dst. ++ * If dst is NULL then the entries are simply removed from src. */ ++void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) ++ ++{ ++ const QDictEntry *entry, *next; ++ const char *p; ++ ++ if (dst) { ++ *dst = qdict_new(); ++ } ++ entry = qdict_first(src); ++ ++ while (entry != NULL) { ++ next = qdict_next(src, entry); ++ if (strstart(entry->key, start, &p)) { ++ if (dst) { ++ qdict_put_obj(*dst, p, qobject_ref(entry->value)); ++ } ++ qdict_del(src, entry->key); ++ } ++ entry = next; ++ } ++} ++ ++static int qdict_count_prefixed_entries(const QDict *src, const char *start) ++{ ++ const QDictEntry *entry; ++ int count = 0; ++ ++ for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { ++ if (strstart(entry->key, start, NULL)) { ++ if (count == INT_MAX) { ++ return -ERANGE; ++ } ++ count++; ++ } ++ } ++ ++ return count; ++} ++ ++/** ++ * qdict_array_split(): This function moves array-like elements of a QDict into ++ * a new QList. Every entry in the original QDict with a key "%u" or one ++ * prefixed "%u.", where %u designates an unsigned integer starting at 0 and ++ * incrementally counting up, will be moved to a new QDict at index %u in the ++ * output QList with the key prefix removed, if that prefix is "%u.". If the ++ * whole key is just "%u", the whole QObject will be moved unchanged without ++ * creating a new QDict. The function terminates when there is no entry in the ++ * QDict with a prefix directly (incrementally) following the last one; it also ++ * returns if there are both entries with "%u" and "%u." for the same index %u. ++ * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "4.y": 1, "o.o": 7, "2": 66} ++ * (or {"1.x": 0, "4.y": 1, "0.a": 42, "o.o": 7, "0.b": 23, "2": 66}) ++ * => [{"a": 42, "b": 23}, {"x": 0}, 66] ++ * and {"4.y": 1, "o.o": 7} (remainder of the old QDict) ++ */ ++void qdict_array_split(QDict *src, QList **dst) ++{ ++ unsigned i; ++ ++ *dst = qlist_new(); ++ ++ for (i = 0; i < UINT_MAX; i++) { ++ QObject *subqobj; ++ bool is_subqdict; ++ QDict *subqdict; ++ char indexstr[32], prefix[32]; ++ size_t snprintf_ret; ++ ++ snprintf_ret = snprintf(indexstr, 32, "%u", i); ++ assert(snprintf_ret < 32); ++ ++ subqobj = qdict_get(src, indexstr); ++ ++ snprintf_ret = snprintf(prefix, 32, "%u.", i); ++ assert(snprintf_ret < 32); ++ ++ /* Overflow is the same as positive non-zero results */ ++ is_subqdict = qdict_count_prefixed_entries(src, prefix); ++ ++ /* ++ * There may be either a single subordinate object (named ++ * "%u") or multiple objects (each with a key prefixed "%u."), ++ * but not both. ++ */ ++ if (!subqobj == !is_subqdict) { ++ break; ++ } ++ ++ if (is_subqdict) { ++ qdict_extract_subqdict(src, &subqdict, prefix); ++ assert(qdict_size(subqdict) > 0); ++ qlist_append_obj(*dst, QOBJECT(subqdict)); ++ } else { ++ qobject_ref(subqobj); ++ qdict_del(src, indexstr); ++ qlist_append_obj(*dst, subqobj); ++ } ++ } ++} ++ ++/** ++ * qdict_split_flat_key: ++ * @key: the key string to split ++ * @prefix: non-NULL pointer to hold extracted prefix ++ * @suffix: non-NULL pointer to remaining suffix ++ * ++ * Given a flattened key such as 'foo.0.bar', split it into two parts ++ * at the first '.' separator. Allows double dot ('..') to escape the ++ * normal separator. ++ * ++ * e.g. ++ * 'foo.0.bar' -> prefix='foo' and suffix='0.bar' ++ * 'foo..0.bar' -> prefix='foo.0' and suffix='bar' ++ * ++ * The '..' sequence will be unescaped in the returned 'prefix' ++ * string. The 'suffix' string will be left in escaped format, so it ++ * can be fed back into the qdict_split_flat_key() key as the input ++ * later. ++ * ++ * The caller is responsible for freeing the string returned in @prefix ++ * using g_free(). ++ */ ++static void qdict_split_flat_key(const char *key, char **prefix, ++ const char **suffix) ++{ ++ const char *separator; ++ size_t i, j; ++ ++ /* Find first '.' separator, but if there is a pair '..' ++ * that acts as an escape, so skip over '..' */ ++ separator = NULL; ++ do { ++ if (separator) { ++ separator += 2; ++ } else { ++ separator = key; ++ } ++ separator = strchr(separator, '.'); ++ } while (separator && separator[1] == '.'); ++ ++ if (separator) { ++ *prefix = g_strndup(key, separator - key); ++ *suffix = separator + 1; ++ } else { ++ *prefix = g_strdup(key); ++ *suffix = NULL; ++ } ++ ++ /* Unescape the '..' sequence into '.' */ ++ for (i = 0, j = 0; (*prefix)[i] != '\0'; i++, j++) { ++ if ((*prefix)[i] == '.') { ++ assert((*prefix)[i + 1] == '.'); ++ i++; ++ } ++ (*prefix)[j] = (*prefix)[i]; ++ } ++ (*prefix)[j] = '\0'; ++} ++ ++/** ++ * qdict_is_list: ++ * @maybe_list: dict to check if keys represent list elements. ++ * ++ * Determine whether all keys in @maybe_list are valid list elements. ++ * If @maybe_list is non-zero in length and all the keys look like ++ * valid list indexes, this will return 1. If @maybe_list is zero ++ * length or all keys are non-numeric then it will return 0 to indicate ++ * it is a normal qdict. If there is a mix of numeric and non-numeric ++ * keys, or the list indexes are non-contiguous, an error is reported. ++ * ++ * Returns: 1 if a valid list, 0 if a dict, -1 on error ++ */ ++static int qdict_is_list(QDict *maybe_list, Error **errp) ++{ ++ const QDictEntry *ent; ++ ssize_t len = 0; ++ ssize_t max = -1; ++ int is_list = -1; ++ int64_t val; ++ ++ for (ent = qdict_first(maybe_list); ent != NULL; ++ ent = qdict_next(maybe_list, ent)) { ++ int is_index = !qemu_strtoi64(ent->key, NULL, 10, &val); ++ ++ if (is_list == -1) { ++ is_list = is_index; ++ } ++ ++ if (is_index != is_list) { ++ error_setg(errp, "Cannot mix list and non-list keys"); ++ return -1; ++ } ++ ++ if (is_index) { ++ len++; ++ if (val > max) { ++ max = val; ++ } ++ } ++ } ++ ++ if (is_list == -1) { ++ assert(!qdict_size(maybe_list)); ++ is_list = 0; ++ } ++ ++ /* NB this isn't a perfect check - e.g. it won't catch ++ * a list containing '1', '+1', '01', '3', but that ++ * does not matter - we've still proved that the ++ * input is a list. It is up the caller to do a ++ * stricter check if desired */ ++ if (len != (max + 1)) { ++ error_setg(errp, "List indices are not contiguous, " ++ "saw %zd elements but %zd largest index", ++ len, max); ++ return -1; ++ } ++ ++ return is_list; ++} ++ ++/** ++ * qdict_crumple: ++ * @src: the original flat dictionary (only scalar values) to crumple ++ * ++ * Takes a flat dictionary whose keys use '.' separator to indicate ++ * nesting, and values are scalars, empty dictionaries or empty lists, ++ * and crumples it into a nested structure. ++ * ++ * To include a literal '.' in a key name, it must be escaped as '..' ++ * ++ * For example, an input of: ++ * ++ * { 'foo.0.bar': 'one', 'foo.0.wizz': '1', ++ * 'foo.1.bar': 'two', 'foo.1.wizz': '2' } ++ * ++ * will result in an output of: ++ * ++ * { ++ * 'foo': [ ++ * { 'bar': 'one', 'wizz': '1' }, ++ * { 'bar': 'two', 'wizz': '2' } ++ * ], ++ * } ++ * ++ * The following scenarios in the input dict will result in an ++ * error being returned: ++ * ++ * - Any values in @src are non-scalar types ++ * - If keys in @src imply that a particular level is both a ++ * list and a dict. e.g., "foo.0.bar" and "foo.eek.bar". ++ * - If keys in @src imply that a particular level is a list, ++ * but the indices are non-contiguous. e.g. "foo.0.bar" and ++ * "foo.2.bar" without any "foo.1.bar" present. ++ * - If keys in @src represent list indexes, but are not in ++ * the "%zu" format. e.g. "foo.+0.bar" ++ * ++ * Returns: either a QDict or QList for the nested data structure, or NULL ++ * on error ++ */ ++QObject *qdict_crumple(const QDict *src, Error **errp) ++{ ++ const QDictEntry *ent; ++ QDict *two_level, *multi_level = NULL, *child_dict; ++ QDict *dict_val; ++ QList *list_val; ++ QObject *dst = NULL, *child; ++ size_t i; ++ char *prefix = NULL; ++ const char *suffix = NULL; ++ int is_list; ++ ++ two_level = qdict_new(); ++ ++ /* Step 1: split our totally flat dict into a two level dict */ ++ for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) { ++ dict_val = qobject_to(QDict, ent->value); ++ list_val = qobject_to(QList, ent->value); ++ if ((dict_val && qdict_size(dict_val)) ++ || (list_val && !qlist_empty(list_val))) { ++ error_setg(errp, "Value %s is not flat", ent->key); ++ goto error; ++ } ++ ++ qdict_split_flat_key(ent->key, &prefix, &suffix); ++ child = qdict_get(two_level, prefix); ++ child_dict = qobject_to(QDict, child); ++ ++ if (child) { ++ /* ++ * If @child_dict, then all previous keys with this prefix ++ * had a suffix. If @suffix, this one has one as well, ++ * and we're good, else there's a clash. ++ */ ++ if (!child_dict || !suffix) { ++ error_setg(errp, "Cannot mix scalar and non-scalar keys"); ++ goto error; ++ } ++ } ++ ++ if (suffix) { ++ if (!child_dict) { ++ child_dict = qdict_new(); ++ qdict_put(two_level, prefix, child_dict); ++ } ++ qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); ++ } else { ++ qdict_put_obj(two_level, prefix, qobject_ref(ent->value)); ++ } ++ ++ g_free(prefix); ++ prefix = NULL; ++ } ++ ++ /* Step 2: optionally process the two level dict recursively ++ * into a multi-level dict */ ++ multi_level = qdict_new(); ++ for (ent = qdict_first(two_level); ent != NULL; ++ ent = qdict_next(two_level, ent)) { ++ dict_val = qobject_to(QDict, ent->value); ++ if (dict_val && qdict_size(dict_val)) { ++ child = qdict_crumple(dict_val, errp); ++ if (!child) { ++ goto error; ++ } ++ ++ qdict_put_obj(multi_level, ent->key, child); ++ } else { ++ qdict_put_obj(multi_level, ent->key, qobject_ref(ent->value)); ++ } ++ } ++ qobject_unref(two_level); ++ two_level = NULL; ++ ++ /* Step 3: detect if we need to turn our dict into list */ ++ is_list = qdict_is_list(multi_level, errp); ++ if (is_list < 0) { ++ goto error; ++ } ++ ++ if (is_list) { ++ dst = QOBJECT(qlist_new()); ++ ++ for (i = 0; i < qdict_size(multi_level); i++) { ++ char *key = g_strdup_printf("%zu", i); ++ ++ child = qdict_get(multi_level, key); ++ g_free(key); ++ ++ if (!child) { ++ error_setg(errp, "Missing list index %zu", i); ++ goto error; ++ } ++ ++ qlist_append_obj(qobject_to(QList, dst), qobject_ref(child)); ++ } ++ qobject_unref(multi_level); ++ multi_level = NULL; ++ } else { ++ dst = QOBJECT(multi_level); ++ } ++ ++ return dst; ++ ++ error: ++ g_free(prefix); ++ qobject_unref(multi_level); ++ qobject_unref(two_level); ++ qobject_unref(dst); ++ return NULL; ++} ++ ++/** ++ * qdict_crumple_for_keyval_qiv: ++ * @src: the flat dictionary (only scalar values) to crumple ++ * @errp: location to store error ++ * ++ * Like qdict_crumple(), but additionally transforms scalar values so ++ * the result can be passed to qobject_input_visitor_new_keyval(). ++ * ++ * The block subsystem uses this function to prepare its flat QDict ++ * with possibly confused scalar types for a visit. It should not be ++ * used for anything else, and it should go away once the block ++ * subsystem has been cleaned up. ++ */ ++static QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) ++{ ++ QDict *tmp = NULL; ++ char *buf; ++ const char *s; ++ const QDictEntry *ent; ++ QObject *dst; ++ ++ for (ent = qdict_first(src); ent; ent = qdict_next(src, ent)) { ++ buf = NULL; ++ switch (qobject_type(ent->value)) { ++ case QTYPE_QNULL: ++ case QTYPE_QSTRING: ++ continue; ++ case QTYPE_QNUM: ++ s = buf = qnum_to_string(qobject_to(QNum, ent->value)); ++ break; ++ case QTYPE_QDICT: ++ case QTYPE_QLIST: ++ /* @src isn't flat; qdict_crumple() will fail */ ++ continue; ++ case QTYPE_QBOOL: ++ s = qbool_get_bool(qobject_to(QBool, ent->value)) ++ ? "on" : "off"; ++ break; ++ default: ++ abort(); ++ } ++ ++ if (!tmp) { ++ tmp = qdict_clone_shallow(src); ++ } ++ qdict_put_str(tmp, ent->key, s); ++ g_free(buf); ++ } ++ ++ dst = qdict_crumple(tmp ?: src, errp); ++ qobject_unref(tmp); ++ return dst; ++} ++ ++/** ++ * qdict_array_entries(): Returns the number of direct array entries if the ++ * sub-QDict of src specified by the prefix in subqdict (or src itself for ++ * prefix == "") is valid as an array, i.e. the length of the created list if ++ * the sub-QDict would become empty after calling qdict_array_split() on it. If ++ * the array is not valid, -EINVAL is returned. ++ */ ++int qdict_array_entries(QDict *src, const char *subqdict) ++{ ++ const QDictEntry *entry; ++ unsigned i; ++ unsigned entries = 0; ++ size_t subqdict_len = strlen(subqdict); ++ ++ assert(!subqdict_len || subqdict[subqdict_len - 1] == '.'); ++ ++ /* qdict_array_split() loops until UINT_MAX, but as we want to return ++ * negative errors, we only have a signed return value here. Any additional ++ * entries will lead to -EINVAL. */ ++ for (i = 0; i < INT_MAX; i++) { ++ QObject *subqobj; ++ int subqdict_entries; ++ char *prefix = g_strdup_printf("%s%u.", subqdict, i); ++ ++ subqdict_entries = qdict_count_prefixed_entries(src, prefix); ++ ++ /* Remove ending "." */ ++ prefix[strlen(prefix) - 1] = 0; ++ subqobj = qdict_get(src, prefix); ++ ++ g_free(prefix); ++ ++ if (subqdict_entries < 0) { ++ return subqdict_entries; ++ } ++ ++ /* There may be either a single subordinate object (named "%u") or ++ * multiple objects (each with a key prefixed "%u."), but not both. */ ++ if (subqobj && subqdict_entries) { ++ return -EINVAL; ++ } else if (!subqobj && !subqdict_entries) { ++ break; ++ } ++ ++ entries += subqdict_entries ? subqdict_entries : 1; ++ } ++ ++ /* Consider everything handled that isn't part of the given sub-QDict */ ++ for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { ++ if (!strstart(qdict_entry_key(entry), subqdict, NULL)) { ++ entries++; ++ } ++ } ++ ++ /* Anything left in the sub-QDict that wasn't handled? */ ++ if (qdict_size(src) != entries) { ++ return -EINVAL; ++ } ++ ++ return i; ++} ++ ++/** ++ * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all ++ * elements from src to dest. ++ * ++ * If an element from src has a key already present in dest, it will not be ++ * moved unless overwrite is true. ++ * ++ * If overwrite is true, the conflicting values in dest will be discarded and ++ * replaced by the corresponding values from src. ++ * ++ * Therefore, with overwrite being true, the src QDict will always be empty when ++ * this function returns. If overwrite is false, the src QDict will be empty ++ * iff there were no conflicts. ++ */ ++void qdict_join(QDict *dest, QDict *src, bool overwrite) ++{ ++ const QDictEntry *entry, *next; ++ ++ entry = qdict_first(src); ++ while (entry) { ++ next = qdict_next(src, entry); ++ ++ if (overwrite || !qdict_haskey(dest, entry->key)) { ++ qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); ++ qdict_del(src, entry->key); ++ } ++ ++ entry = next; ++ } ++} ++ ++/** ++ * qdict_rename_keys(): Rename keys in qdict according to the replacements ++ * specified in the array renames. The array must be terminated by an entry ++ * with from = NULL. ++ * ++ * The renames are performed individually in the order of the array, so entries ++ * may be renamed multiple times and may or may not conflict depending on the ++ * order of the renames array. ++ * ++ * Returns true for success, false in error cases. ++ */ ++bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) ++{ ++ QObject *qobj; ++ ++ while (renames->from) { ++ if (qdict_haskey(qdict, renames->from)) { ++ if (qdict_haskey(qdict, renames->to)) { ++ error_setg(errp, "'%s' and its alias '%s' can't be used at the " ++ "same time", renames->to, renames->from); ++ return false; ++ } ++ ++ qobj = qdict_get(qdict, renames->from); ++ qdict_put_obj(qdict, renames->to, qobject_ref(qobj)); ++ qdict_del(qdict, renames->from); ++ } ++ ++ renames++; ++ } ++ return true; ++} ++ ++/* ++ * Create a QObject input visitor for flat @qdict with possibly ++ * confused scalar types. ++ * ++ * The block subsystem uses this function to visit its flat QDict with ++ * possibly confused scalar types. It should not be used for anything ++ * else, and it should go away once the block subsystem has been ++ * cleaned up. ++ */ ++Visitor *qobject_input_visitor_new_flat_confused(QDict *qdict, ++ Error **errp) ++{ ++ QObject *crumpled; ++ Visitor *v; ++ ++ crumpled = qdict_crumple_for_keyval_qiv(qdict, errp); ++ if (!crumpled) { ++ return NULL; ++ } ++ ++ v = qobject_input_visitor_new_keyval(crumpled); ++ qobject_unref(crumpled); ++ return v; ++} +diff --git a/qcow2/lib/qobject/json-lexer.c b/qcow2/lib/qobject/json-lexer.c +new file mode 100644 +index 00000000..51341d96 +--- /dev/null ++++ b/qcow2/lib/qobject/json-lexer.c +@@ -0,0 +1,365 @@ ++/* ++ * JSON lexer ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "json-parser-int.h" ++ ++#define MAX_TOKEN_SIZE (64ULL << 20) ++ ++/* ++ * From RFC 8259 "The JavaScript Object Notation (JSON) Data ++ * Interchange Format", with [comments in brackets]: ++ * ++ * The set of tokens includes six structural characters, strings, ++ * numbers, and three literal names. ++ * ++ * These are the six structural characters: ++ * ++ * begin-array = ws %x5B ws ; [ left square bracket ++ * begin-object = ws %x7B ws ; { left curly bracket ++ * end-array = ws %x5D ws ; ] right square bracket ++ * end-object = ws %x7D ws ; } right curly bracket ++ * name-separator = ws %x3A ws ; : colon ++ * value-separator = ws %x2C ws ; , comma ++ * ++ * Insignificant whitespace is allowed before or after any of the six ++ * structural characters. ++ * [This lexer accepts it before or after any token, which is actually ++ * the same, as the grammar always has structural characters between ++ * other tokens.] ++ * ++ * ws = *( ++ * %x20 / ; Space ++ * %x09 / ; Horizontal tab ++ * %x0A / ; Line feed or New line ++ * %x0D ) ; Carriage return ++ * ++ * [...] three literal names: ++ * false null true ++ * [This lexer accepts [a-z]+, and leaves rejecting unknown literal ++ * names to the parser.] ++ * ++ * [Numbers:] ++ * ++ * number = [ minus ] int [ frac ] [ exp ] ++ * decimal-point = %x2E ; . ++ * digit1-9 = %x31-39 ; 1-9 ++ * e = %x65 / %x45 ; e E ++ * exp = e [ minus / plus ] 1*DIGIT ++ * frac = decimal-point 1*DIGIT ++ * int = zero / ( digit1-9 *DIGIT ) ++ * minus = %x2D ; - ++ * plus = %x2B ; + ++ * zero = %x30 ; 0 ++ * ++ * [Strings:] ++ * string = quotation-mark *char quotation-mark ++ * ++ * char = unescaped / ++ * escape ( ++ * %x22 / ; " quotation mark U+0022 ++ * %x5C / ; \ reverse solidus U+005C ++ * %x2F / ; / solidus U+002F ++ * %x62 / ; b backspace U+0008 ++ * %x66 / ; f form feed U+000C ++ * %x6E / ; n line feed U+000A ++ * %x72 / ; r carriage return U+000D ++ * %x74 / ; t tab U+0009 ++ * %x75 4HEXDIG ) ; uXXXX U+XXXX ++ * escape = %x5C ; \ ++ * quotation-mark = %x22 ; " ++ * unescaped = %x20-21 / %x23-5B / %x5D-10FFFF ++ * [This lexer accepts any non-control character after escape, and ++ * leaves rejecting invalid ones to the parser.] ++ * ++ * ++ * Extensions over RFC 8259: ++ * - Extra escape sequence in strings: ++ * 0x27 (apostrophe) is recognized after escape, too ++ * - Single-quoted strings: ++ * Like double-quoted strings, except they're delimited by %x27 ++ * (apostrophe) instead of %x22 (quotation mark), and can't contain ++ * unescaped apostrophe, but can contain unescaped quotation mark. ++ * - Interpolation, if enabled: ++ * The lexer accepts %[A-Za-z0-9]*, and leaves rejecting invalid ++ * ones to the parser. ++ * ++ * Note: ++ * - Input must be encoded in modified UTF-8. ++ * - Decoding and validating is left to the parser. ++ */ ++ ++enum json_lexer_state { ++ IN_RECOVERY = 1, ++ IN_DQ_STRING_ESCAPE, ++ IN_DQ_STRING, ++ IN_SQ_STRING_ESCAPE, ++ IN_SQ_STRING, ++ IN_ZERO, ++ IN_EXP_DIGITS, ++ IN_EXP_SIGN, ++ IN_EXP_E, ++ IN_MANTISSA, ++ IN_MANTISSA_DIGITS, ++ IN_DIGITS, ++ IN_SIGN, ++ IN_KEYWORD, ++ IN_INTERP, ++ IN_START, ++ IN_START_INTERP, /* must be IN_START + 1 */ ++}; ++ ++QEMU_BUILD_BUG_ON(JSON_ERROR != 0); ++QEMU_BUILD_BUG_ON(IN_RECOVERY != JSON_ERROR + 1); ++QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START_INTERP); ++QEMU_BUILD_BUG_ON(JSON_MAX >= 0x80); ++QEMU_BUILD_BUG_ON(IN_START_INTERP != IN_START + 1); ++ ++#define LOOKAHEAD 0x80 ++#define TERMINAL(state) [0 ... 0xFF] = ((state) | LOOKAHEAD) ++ ++static const uint8_t json_lexer[][256] = { ++ /* Relies on default initialization to IN_ERROR! */ ++ ++ /* error recovery */ ++ [IN_RECOVERY] = { ++ /* ++ * Skip characters until a structural character, an ASCII ++ * control character other than '\t', or impossible UTF-8 ++ * bytes '\xFE', '\xFF'. Structural characters and line ++ * endings are promising resynchronization points. Clients ++ * may use the others to force the JSON parser into known-good ++ * state; see docs/interop/qmp-spec.rst. ++ */ ++ [0 ... 0x1F] = IN_START | LOOKAHEAD, ++ [0x20 ... 0xFD] = IN_RECOVERY, ++ [0xFE ... 0xFF] = IN_START | LOOKAHEAD, ++ ['\t'] = IN_RECOVERY, ++ ['['] = IN_START | LOOKAHEAD, ++ [']'] = IN_START | LOOKAHEAD, ++ ['{'] = IN_START | LOOKAHEAD, ++ ['}'] = IN_START | LOOKAHEAD, ++ [':'] = IN_START | LOOKAHEAD, ++ [','] = IN_START | LOOKAHEAD, ++ }, ++ ++ /* double quote string */ ++ [IN_DQ_STRING_ESCAPE] = { ++ [0x20 ... 0xFD] = IN_DQ_STRING, ++ }, ++ [IN_DQ_STRING] = { ++ [0x20 ... 0xFD] = IN_DQ_STRING, ++ ['\\'] = IN_DQ_STRING_ESCAPE, ++ ['"'] = JSON_STRING, ++ }, ++ ++ /* single quote string */ ++ [IN_SQ_STRING_ESCAPE] = { ++ [0x20 ... 0xFD] = IN_SQ_STRING, ++ }, ++ [IN_SQ_STRING] = { ++ [0x20 ... 0xFD] = IN_SQ_STRING, ++ ['\\'] = IN_SQ_STRING_ESCAPE, ++ ['\''] = JSON_STRING, ++ }, ++ ++ /* Zero */ ++ [IN_ZERO] = { ++ TERMINAL(JSON_INTEGER), ++ ['0' ... '9'] = JSON_ERROR, ++ ['.'] = IN_MANTISSA, ++ }, ++ ++ /* Float */ ++ [IN_EXP_DIGITS] = { ++ TERMINAL(JSON_FLOAT), ++ ['0' ... '9'] = IN_EXP_DIGITS, ++ }, ++ ++ [IN_EXP_SIGN] = { ++ ['0' ... '9'] = IN_EXP_DIGITS, ++ }, ++ ++ [IN_EXP_E] = { ++ ['-'] = IN_EXP_SIGN, ++ ['+'] = IN_EXP_SIGN, ++ ['0' ... '9'] = IN_EXP_DIGITS, ++ }, ++ ++ [IN_MANTISSA_DIGITS] = { ++ TERMINAL(JSON_FLOAT), ++ ['0' ... '9'] = IN_MANTISSA_DIGITS, ++ ['e'] = IN_EXP_E, ++ ['E'] = IN_EXP_E, ++ }, ++ ++ [IN_MANTISSA] = { ++ ['0' ... '9'] = IN_MANTISSA_DIGITS, ++ }, ++ ++ /* Number */ ++ [IN_DIGITS] = { ++ TERMINAL(JSON_INTEGER), ++ ['0' ... '9'] = IN_DIGITS, ++ ['e'] = IN_EXP_E, ++ ['E'] = IN_EXP_E, ++ ['.'] = IN_MANTISSA, ++ }, ++ ++ [IN_SIGN] = { ++ ['0'] = IN_ZERO, ++ ['1' ... '9'] = IN_DIGITS, ++ }, ++ ++ /* keywords */ ++ [IN_KEYWORD] = { ++ TERMINAL(JSON_KEYWORD), ++ ['a' ... 'z'] = IN_KEYWORD, ++ }, ++ ++ /* interpolation */ ++ [IN_INTERP] = { ++ TERMINAL(JSON_INTERP), ++ ['A' ... 'Z'] = IN_INTERP, ++ ['a' ... 'z'] = IN_INTERP, ++ ['0' ... '9'] = IN_INTERP, ++ }, ++ ++ /* ++ * Two start states: ++ * - IN_START recognizes JSON tokens with our string extensions ++ * - IN_START_INTERP additionally recognizes interpolation. ++ */ ++ [IN_START ... IN_START_INTERP] = { ++ ['"'] = IN_DQ_STRING, ++ ['\''] = IN_SQ_STRING, ++ ['0'] = IN_ZERO, ++ ['1' ... '9'] = IN_DIGITS, ++ ['-'] = IN_SIGN, ++ ['{'] = JSON_LCURLY, ++ ['}'] = JSON_RCURLY, ++ ['['] = JSON_LSQUARE, ++ [']'] = JSON_RSQUARE, ++ [','] = JSON_COMMA, ++ [':'] = JSON_COLON, ++ ['a' ... 'z'] = IN_KEYWORD, ++ [' '] = IN_START, ++ ['\t'] = IN_START, ++ ['\r'] = IN_START, ++ ['\n'] = IN_START, ++ }, ++ [IN_START_INTERP]['%'] = IN_INTERP, ++}; ++ ++static inline uint8_t next_state(JSONLexer *lexer, char ch, bool flush, ++ bool *char_consumed) ++{ ++ uint8_t next; ++ ++ assert(lexer->state < ARRAY_SIZE(json_lexer)); ++ next = json_lexer[lexer->state][(uint8_t)ch]; ++ *char_consumed = !flush && !(next & LOOKAHEAD); ++ return next & ~LOOKAHEAD; ++} ++ ++void json_lexer_init(JSONLexer *lexer, bool enable_interpolation) ++{ ++ lexer->start_state = lexer->state = enable_interpolation ++ ? IN_START_INTERP : IN_START; ++ lexer->token = g_string_sized_new(3); ++ lexer->x = lexer->y = 0; ++} ++ ++static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) ++{ ++ int new_state; ++ bool char_consumed = false; ++ ++ lexer->x++; ++ if (ch == '\n') { ++ lexer->x = 0; ++ lexer->y++; ++ } ++ ++ while (flush ? lexer->state != lexer->start_state : !char_consumed) { ++ new_state = next_state(lexer, ch, flush, &char_consumed); ++ if (char_consumed) { ++ assert(!flush); ++ g_string_append_c(lexer->token, ch); ++ } ++ ++ switch (new_state) { ++ case JSON_LCURLY: ++ case JSON_RCURLY: ++ case JSON_LSQUARE: ++ case JSON_RSQUARE: ++ case JSON_COLON: ++ case JSON_COMMA: ++ case JSON_INTERP: ++ case JSON_INTEGER: ++ case JSON_FLOAT: ++ case JSON_KEYWORD: ++ case JSON_STRING: ++ json_message_process_token(lexer, lexer->token, new_state, ++ lexer->x, lexer->y); ++ /* fall through */ ++ case IN_START: ++ g_string_truncate(lexer->token, 0); ++ new_state = lexer->start_state; ++ break; ++ case JSON_ERROR: ++ json_message_process_token(lexer, lexer->token, JSON_ERROR, ++ lexer->x, lexer->y); ++ new_state = IN_RECOVERY; ++ /* fall through */ ++ case IN_RECOVERY: ++ g_string_truncate(lexer->token, 0); ++ break; ++ default: ++ break; ++ } ++ lexer->state = new_state; ++ } ++ ++ /* Do not let a single token grow to an arbitrarily large size, ++ * this is a security consideration. ++ */ ++ if (lexer->token->len > MAX_TOKEN_SIZE) { ++ json_message_process_token(lexer, lexer->token, lexer->state, ++ lexer->x, lexer->y); ++ g_string_truncate(lexer->token, 0); ++ lexer->state = lexer->start_state; ++ } ++} ++ ++void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size) ++{ ++ size_t i; ++ ++ for (i = 0; i < size; i++) { ++ json_lexer_feed_char(lexer, buffer[i], false); ++ } ++} ++ ++void json_lexer_flush(JSONLexer *lexer) ++{ ++ json_lexer_feed_char(lexer, 0, true); ++ assert(lexer->state == lexer->start_state); ++ json_message_process_token(lexer, lexer->token, JSON_END_OF_INPUT, ++ lexer->x, lexer->y); ++} ++ ++void json_lexer_destroy(JSONLexer *lexer) ++{ ++ g_string_free(lexer->token, true); ++} +diff --git a/qcow2/lib/qobject/json-parser-int.h b/qcow2/lib/qobject/json-parser-int.h +new file mode 100644 +index 00000000..16a25d00 +--- /dev/null ++++ b/qcow2/lib/qobject/json-parser-int.h +@@ -0,0 +1,54 @@ ++/* ++ * JSON Parser ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef JSON_PARSER_INT_H ++#define JSON_PARSER_INT_H ++ ++#include "qapi/qmp/json-parser.h" ++ ++typedef enum json_token_type { ++ JSON_ERROR = 0, /* must be zero, see json_lexer[] */ ++ /* Gap for lexer states */ ++ JSON_LCURLY = 100, ++ JSON_MIN = JSON_LCURLY, ++ JSON_RCURLY, ++ JSON_LSQUARE, ++ JSON_RSQUARE, ++ JSON_COLON, ++ JSON_COMMA, ++ JSON_INTEGER, ++ JSON_FLOAT, ++ JSON_KEYWORD, ++ JSON_STRING, ++ JSON_INTERP, ++ JSON_END_OF_INPUT, ++ JSON_MAX = JSON_END_OF_INPUT ++} JSONTokenType; ++ ++typedef struct JSONToken JSONToken; ++ ++/* json-lexer.c */ ++void json_lexer_init(JSONLexer *lexer, bool enable_interpolation); ++void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size); ++void json_lexer_flush(JSONLexer *lexer); ++void json_lexer_destroy(JSONLexer *lexer); ++ ++/* json-streamer.c */ ++void json_message_process_token(JSONLexer *lexer, GString *input, ++ JSONTokenType type, int x, int y); ++ ++/* json-parser.c */ ++JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr); ++QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp); ++ ++#endif +diff --git a/qcow2/lib/qobject/json-parser.c b/qcow2/lib/qobject/json-parser.c +new file mode 100644 +index 00000000..d498db6e +--- /dev/null ++++ b/qcow2/lib/qobject/json-parser.c +@@ -0,0 +1,590 @@ ++/* ++ * JSON Parser ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/ctype.h" ++#include "qemu/cutils.h" ++#include "qemu/unicode.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "json-parser-int.h" ++ ++struct JSONToken { ++ JSONTokenType type; ++ int x; ++ int y; ++ char str[]; ++}; ++ ++typedef struct JSONParserContext { ++ Error *err; ++ JSONToken *current; ++ GQueue *buf; ++ va_list *ap; ++} JSONParserContext; ++ ++#define BUG_ON(cond) assert(!(cond)) ++ ++/** ++ * TODO ++ * ++ * 0) make errors meaningful again ++ * 1) add geometry information to tokens ++ * 3) should we return a parsed size? ++ * 4) deal with premature EOI ++ */ ++ ++static QObject *parse_value(JSONParserContext *ctxt); ++ ++/** ++ * Error handler ++ */ ++static void G_GNUC_PRINTF(3, 4) parse_error(JSONParserContext *ctxt, ++ JSONToken *token, const char *msg, ...) ++{ ++ va_list ap; ++ char message[1024]; ++ ++ if (ctxt->err) { ++ return; ++ } ++ va_start(ap, msg); ++ vsnprintf(message, sizeof(message), msg, ap); ++ va_end(ap); ++ error_setg(&ctxt->err, "JSON parse error, %s", message); ++} ++ ++static int cvt4hex(const char *s) ++{ ++ int cp, i; ++ ++ cp = 0; ++ for (i = 0; i < 4; i++) { ++ if (!qemu_isxdigit(s[i])) { ++ return -1; ++ } ++ cp <<= 4; ++ if (s[i] >= '0' && s[i] <= '9') { ++ cp |= s[i] - '0'; ++ } else if (s[i] >= 'a' && s[i] <= 'f') { ++ cp |= 10 + s[i] - 'a'; ++ } else if (s[i] >= 'A' && s[i] <= 'F') { ++ cp |= 10 + s[i] - 'A'; ++ } else { ++ return -1; ++ } ++ } ++ return cp; ++} ++ ++/** ++ * parse_string(): Parse a JSON string ++ * ++ * From RFC 8259 "The JavaScript Object Notation (JSON) Data ++ * Interchange Format": ++ * ++ * char = unescaped / ++ * escape ( ++ * %x22 / ; " quotation mark U+0022 ++ * %x5C / ; \ reverse solidus U+005C ++ * %x2F / ; / solidus U+002F ++ * %x62 / ; b backspace U+0008 ++ * %x66 / ; f form feed U+000C ++ * %x6E / ; n line feed U+000A ++ * %x72 / ; r carriage return U+000D ++ * %x74 / ; t tab U+0009 ++ * %x75 4HEXDIG ) ; uXXXX U+XXXX ++ * escape = %x5C ; \ ++ * quotation-mark = %x22 ; " ++ * unescaped = %x20-21 / %x23-5B / %x5D-10FFFF ++ * ++ * Extensions over RFC 8259: ++ * - Extra escape sequence in strings: ++ * 0x27 (apostrophe) is recognized after escape, too ++ * - Single-quoted strings: ++ * Like double-quoted strings, except they're delimited by %x27 ++ * (apostrophe) instead of %x22 (quotation mark), and can't contain ++ * unescaped apostrophe, but can contain unescaped quotation mark. ++ * ++ * Note: ++ * - Encoding is modified UTF-8. ++ * - Invalid Unicode characters are rejected. ++ * - Control characters \x00..\x1F are rejected by the lexer. ++ */ ++static QString *parse_string(JSONParserContext *ctxt, JSONToken *token) ++{ ++ const char *ptr = token->str; ++ GString *str; ++ char quote; ++ const char *beg; ++ int cp, trailing; ++ char *end; ++ ssize_t len; ++ char utf8_buf[5]; ++ ++ assert(*ptr == '"' || *ptr == '\''); ++ quote = *ptr++; ++ str = g_string_new(NULL); ++ ++ while (*ptr != quote) { ++ assert(*ptr); ++ switch (*ptr) { ++ case '\\': ++ beg = ptr++; ++ switch (*ptr++) { ++ case '"': ++ g_string_append_c(str, '"'); ++ break; ++ case '\'': ++ g_string_append_c(str, '\''); ++ break; ++ case '\\': ++ g_string_append_c(str, '\\'); ++ break; ++ case '/': ++ g_string_append_c(str, '/'); ++ break; ++ case 'b': ++ g_string_append_c(str, '\b'); ++ break; ++ case 'f': ++ g_string_append_c(str, '\f'); ++ break; ++ case 'n': ++ g_string_append_c(str, '\n'); ++ break; ++ case 'r': ++ g_string_append_c(str, '\r'); ++ break; ++ case 't': ++ g_string_append_c(str, '\t'); ++ break; ++ case 'u': ++ cp = cvt4hex(ptr); ++ ptr += 4; ++ ++ /* handle surrogate pairs */ ++ if (cp >= 0xD800 && cp <= 0xDBFF ++ && ptr[0] == '\\' && ptr[1] == 'u') { ++ /* leading surrogate followed by \u */ ++ cp = 0x10000 + ((cp & 0x3FF) << 10); ++ trailing = cvt4hex(ptr + 2); ++ if (trailing >= 0xDC00 && trailing <= 0xDFFF) { ++ /* followed by trailing surrogate */ ++ cp |= trailing & 0x3FF; ++ ptr += 6; ++ } else { ++ cp = -1; /* invalid */ ++ } ++ } ++ ++ if (mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp) < 0) { ++ parse_error(ctxt, token, ++ "%.*s is not a valid Unicode character", ++ (int)(ptr - beg), beg); ++ goto out; ++ } ++ g_string_append(str, utf8_buf); ++ break; ++ default: ++ parse_error(ctxt, token, "invalid escape sequence in string"); ++ goto out; ++ } ++ break; ++ case '%': ++ if (ctxt->ap) { ++ if (ptr[1] != '%') { ++ parse_error(ctxt, token, "can't interpolate into string"); ++ goto out; ++ } ++ ptr++; ++ } ++ /* fall through */ ++ default: ++ cp = mod_utf8_codepoint(ptr, 6, &end); ++ if (cp < 0) { ++ parse_error(ctxt, token, "invalid UTF-8 sequence in string"); ++ goto out; ++ } ++ ptr = end; ++ len = mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp); ++ assert(len >= 0); ++ g_string_append(str, utf8_buf); ++ } ++ } ++ ++ return qstring_from_gstring(str); ++ ++out: ++ g_string_free(str, true); ++ return NULL; ++} ++ ++/* Note: the token object returned by parser_context_peek_token or ++ * parser_context_pop_token is deleted as soon as parser_context_pop_token ++ * is called again. ++ */ ++static JSONToken *parser_context_pop_token(JSONParserContext *ctxt) ++{ ++ g_free(ctxt->current); ++ ctxt->current = g_queue_pop_head(ctxt->buf); ++ return ctxt->current; ++} ++ ++static JSONToken *parser_context_peek_token(JSONParserContext *ctxt) ++{ ++ return g_queue_peek_head(ctxt->buf); ++} ++ ++/** ++ * Parsing rules ++ */ ++static int parse_pair(JSONParserContext *ctxt, QDict *dict) ++{ ++ QObject *key_obj = NULL; ++ QString *key; ++ QObject *value; ++ JSONToken *peek, *token; ++ ++ peek = parser_context_peek_token(ctxt); ++ if (peek == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ ++ key_obj = parse_value(ctxt); ++ key = qobject_to(QString, key_obj); ++ if (!key) { ++ parse_error(ctxt, peek, "key is not a string in object"); ++ goto out; ++ } ++ ++ token = parser_context_pop_token(ctxt); ++ if (token == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ ++ if (token->type != JSON_COLON) { ++ parse_error(ctxt, token, "missing : in object pair"); ++ goto out; ++ } ++ ++ value = parse_value(ctxt); ++ if (value == NULL) { ++ parse_error(ctxt, token, "Missing value in dict"); ++ goto out; ++ } ++ ++ if (qdict_haskey(dict, qstring_get_str(key))) { ++ parse_error(ctxt, token, "duplicate key"); ++ goto out; ++ } ++ ++ qdict_put_obj(dict, qstring_get_str(key), value); ++ ++ qobject_unref(key_obj); ++ return 0; ++ ++out: ++ qobject_unref(key_obj); ++ return -1; ++} ++ ++static QObject *parse_object(JSONParserContext *ctxt) ++{ ++ QDict *dict = NULL; ++ JSONToken *token, *peek; ++ ++ token = parser_context_pop_token(ctxt); ++ assert(token && token->type == JSON_LCURLY); ++ ++ dict = qdict_new(); ++ ++ peek = parser_context_peek_token(ctxt); ++ if (peek == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ ++ if (peek->type != JSON_RCURLY) { ++ if (parse_pair(ctxt, dict) == -1) { ++ goto out; ++ } ++ ++ token = parser_context_pop_token(ctxt); ++ if (token == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ ++ while (token->type != JSON_RCURLY) { ++ if (token->type != JSON_COMMA) { ++ parse_error(ctxt, token, "expected separator in dict"); ++ goto out; ++ } ++ ++ if (parse_pair(ctxt, dict) == -1) { ++ goto out; ++ } ++ ++ token = parser_context_pop_token(ctxt); ++ if (token == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ } ++ } else { ++ (void)parser_context_pop_token(ctxt); ++ } ++ ++ return QOBJECT(dict); ++ ++out: ++ qobject_unref(dict); ++ return NULL; ++} ++ ++static QObject *parse_array(JSONParserContext *ctxt) ++{ ++ QList *list = NULL; ++ JSONToken *token, *peek; ++ ++ token = parser_context_pop_token(ctxt); ++ assert(token && token->type == JSON_LSQUARE); ++ ++ list = qlist_new(); ++ ++ peek = parser_context_peek_token(ctxt); ++ if (peek == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ ++ if (peek->type != JSON_RSQUARE) { ++ QObject *obj; ++ ++ obj = parse_value(ctxt); ++ if (obj == NULL) { ++ parse_error(ctxt, token, "expecting value"); ++ goto out; ++ } ++ ++ qlist_append_obj(list, obj); ++ ++ token = parser_context_pop_token(ctxt); ++ if (token == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ ++ while (token->type != JSON_RSQUARE) { ++ if (token->type != JSON_COMMA) { ++ parse_error(ctxt, token, "expected separator in list"); ++ goto out; ++ } ++ ++ obj = parse_value(ctxt); ++ if (obj == NULL) { ++ parse_error(ctxt, token, "expecting value"); ++ goto out; ++ } ++ ++ qlist_append_obj(list, obj); ++ ++ token = parser_context_pop_token(ctxt); ++ if (token == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ } ++ } else { ++ (void)parser_context_pop_token(ctxt); ++ } ++ ++ return QOBJECT(list); ++ ++out: ++ qobject_unref(list); ++ return NULL; ++} ++ ++static QObject *parse_keyword(JSONParserContext *ctxt) ++{ ++ JSONToken *token; ++ ++ token = parser_context_pop_token(ctxt); ++ assert(token && token->type == JSON_KEYWORD); ++ ++ if (!strcmp(token->str, "true")) { ++ return QOBJECT(qbool_from_bool(true)); ++ } else if (!strcmp(token->str, "false")) { ++ return QOBJECT(qbool_from_bool(false)); ++ } else if (!strcmp(token->str, "null")) { ++ return QOBJECT(qnull()); ++ } ++ parse_error(ctxt, token, "invalid keyword '%s'", token->str); ++ return NULL; ++} ++ ++static QObject *parse_interpolation(JSONParserContext *ctxt) ++{ ++ JSONToken *token; ++ ++ token = parser_context_pop_token(ctxt); ++ assert(token && token->type == JSON_INTERP); ++ ++ if (!strcmp(token->str, "%p")) { ++ return va_arg(*ctxt->ap, QObject *); ++ } else if (!strcmp(token->str, "%i")) { ++ return QOBJECT(qbool_from_bool(va_arg(*ctxt->ap, int))); ++ } else if (!strcmp(token->str, "%d")) { ++ return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, int))); ++ } else if (!strcmp(token->str, "%ld")) { ++ return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, long))); ++ } else if (!strcmp(token->str, "%lld")) { ++ return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, long long))); ++ } else if (!strcmp(token->str, "%" PRId64)) { ++ return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, int64_t))); ++ } else if (!strcmp(token->str, "%u")) { ++ return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned int))); ++ } else if (!strcmp(token->str, "%lu")) { ++ return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned long))); ++ } else if (!strcmp(token->str, "%llu")) { ++ return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned long long))); ++ } else if (!strcmp(token->str, "%" PRIu64)) { ++ return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, uint64_t))); ++ } else if (!strcmp(token->str, "%s")) { ++ return QOBJECT(qstring_from_str(va_arg(*ctxt->ap, const char *))); ++ } else if (!strcmp(token->str, "%f")) { ++ return QOBJECT(qnum_from_double(va_arg(*ctxt->ap, double))); ++ } ++ parse_error(ctxt, token, "invalid interpolation '%s'", token->str); ++ return NULL; ++} ++ ++static QObject *parse_literal(JSONParserContext *ctxt) ++{ ++ JSONToken *token; ++ ++ token = parser_context_pop_token(ctxt); ++ assert(token); ++ ++ switch (token->type) { ++ case JSON_STRING: ++ return QOBJECT(parse_string(ctxt, token)); ++ case JSON_INTEGER: { ++ /* ++ * Represent JSON_INTEGER as QNUM_I64 if possible, else as ++ * QNUM_U64, else as QNUM_DOUBLE. Note that qemu_strtoi64() ++ * and qemu_strtou64() fail with ERANGE when it's not ++ * possible. ++ * ++ * qnum_get_int() will then work for any signed 64-bit ++ * JSON_INTEGER, qnum_get_uint() for any unsigned 64-bit ++ * integer, and qnum_get_double() both for any JSON_INTEGER ++ * and any JSON_FLOAT (with precision loss for integers beyond ++ * 53 bits) ++ */ ++ int ret; ++ int64_t value; ++ uint64_t uvalue; ++ ++ ret = qemu_strtoi64(token->str, NULL, 10, &value); ++ if (!ret) { ++ return QOBJECT(qnum_from_int(value)); ++ } ++ assert(ret == -ERANGE); ++ ++ if (token->str[0] != '-') { ++ ret = qemu_strtou64(token->str, NULL, 10, &uvalue); ++ if (!ret) { ++ return QOBJECT(qnum_from_uint(uvalue)); ++ } ++ assert(ret == -ERANGE); ++ } ++ } ++ /* fall through to JSON_FLOAT */ ++ case JSON_FLOAT: ++ /* FIXME dependent on locale; a pervasive issue in QEMU */ ++ /* FIXME our lexer matches RFC 8259 in forbidding Inf or NaN, ++ * but those might be useful extensions beyond JSON */ ++ return QOBJECT(qnum_from_double(strtod(token->str, NULL))); ++ default: ++ abort(); ++ } ++} ++ ++static QObject *parse_value(JSONParserContext *ctxt) ++{ ++ JSONToken *token; ++ ++ token = parser_context_peek_token(ctxt); ++ if (token == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ return NULL; ++ } ++ ++ switch (token->type) { ++ case JSON_LCURLY: ++ return parse_object(ctxt); ++ case JSON_LSQUARE: ++ return parse_array(ctxt); ++ case JSON_INTERP: ++ return parse_interpolation(ctxt); ++ case JSON_INTEGER: ++ case JSON_FLOAT: ++ case JSON_STRING: ++ return parse_literal(ctxt); ++ case JSON_KEYWORD: ++ return parse_keyword(ctxt); ++ default: ++ parse_error(ctxt, token, "expecting value"); ++ return NULL; ++ } ++} ++ ++JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr) ++{ ++ JSONToken *token = g_malloc(sizeof(JSONToken) + tokstr->len + 1); ++ ++ token->type = type; ++ memcpy(token->str, tokstr->str, tokstr->len); ++ token->str[tokstr->len] = 0; ++ token->x = x; ++ token->y = y; ++ return token; ++} ++ ++QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp) ++{ ++ JSONParserContext ctxt = { .buf = tokens, .ap = ap }; ++ QObject *result; ++ ++ result = parse_value(&ctxt); ++ assert(ctxt.err || g_queue_is_empty(ctxt.buf)); ++ ++ error_propagate(errp, ctxt.err); ++ ++ while (!g_queue_is_empty(ctxt.buf)) { ++ parser_context_pop_token(&ctxt); ++ } ++ g_free(ctxt.current); ++ ++ return result; ++} +diff --git a/qcow2/lib/qobject/json-streamer.c b/qcow2/lib/qobject/json-streamer.c +new file mode 100644 +index 00000000..b93d97b9 +--- /dev/null ++++ b/qcow2/lib/qobject/json-streamer.c +@@ -0,0 +1,134 @@ ++/* ++ * JSON streaming support ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "json-parser-int.h" ++ ++#define MAX_TOKEN_SIZE (64ULL << 20) ++#define MAX_TOKEN_COUNT (2ULL << 20) ++#define MAX_NESTING (1 << 10) ++ ++static void json_message_free_tokens(JSONMessageParser *parser) ++{ ++ JSONToken *token; ++ ++ while ((token = g_queue_pop_head(&parser->tokens))) { ++ g_free(token); ++ } ++} ++ ++void json_message_process_token(JSONLexer *lexer, GString *input, ++ JSONTokenType type, int x, int y) ++{ ++ JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer); ++ QObject *json = NULL; ++ Error *err = NULL; ++ JSONToken *token; ++ ++ switch (type) { ++ case JSON_LCURLY: ++ parser->brace_count++; ++ break; ++ case JSON_RCURLY: ++ parser->brace_count--; ++ break; ++ case JSON_LSQUARE: ++ parser->bracket_count++; ++ break; ++ case JSON_RSQUARE: ++ parser->bracket_count--; ++ break; ++ case JSON_ERROR: ++ error_setg(&err, "JSON parse error, stray '%s'", input->str); ++ goto out_emit; ++ case JSON_END_OF_INPUT: ++ if (g_queue_is_empty(&parser->tokens)) { ++ return; ++ } ++ json = json_parser_parse(&parser->tokens, parser->ap, &err); ++ goto out_emit; ++ default: ++ break; ++ } ++ ++ /* ++ * Security consideration, we limit total memory allocated per object ++ * and the maximum recursion depth that a message can force. ++ */ ++ if (parser->token_size + input->len + 1 > MAX_TOKEN_SIZE) { ++ error_setg(&err, "JSON token size limit exceeded"); ++ goto out_emit; ++ } ++ if (g_queue_get_length(&parser->tokens) + 1 > MAX_TOKEN_COUNT) { ++ error_setg(&err, "JSON token count limit exceeded"); ++ goto out_emit; ++ } ++ if (parser->bracket_count + parser->brace_count > MAX_NESTING) { ++ error_setg(&err, "JSON nesting depth limit exceeded"); ++ goto out_emit; ++ } ++ ++ token = json_token(type, x, y, input); ++ parser->token_size += input->len; ++ ++ g_queue_push_tail(&parser->tokens, token); ++ ++ if ((parser->brace_count > 0 || parser->bracket_count > 0) ++ && parser->brace_count >= 0 && parser->bracket_count >= 0) { ++ return; ++ } ++ ++ json = json_parser_parse(&parser->tokens, parser->ap, &err); ++ ++out_emit: ++ parser->brace_count = 0; ++ parser->bracket_count = 0; ++ json_message_free_tokens(parser); ++ parser->token_size = 0; ++ parser->emit(parser->opaque, json, err); ++} ++ ++void json_message_parser_init(JSONMessageParser *parser, ++ void (*emit)(void *opaque, QObject *json, ++ Error *err), ++ void *opaque, va_list *ap) ++{ ++ parser->emit = emit; ++ parser->opaque = opaque; ++ parser->ap = ap; ++ parser->brace_count = 0; ++ parser->bracket_count = 0; ++ g_queue_init(&parser->tokens); ++ parser->token_size = 0; ++ ++ json_lexer_init(&parser->lexer, !!ap); ++} ++ ++void json_message_parser_feed(JSONMessageParser *parser, ++ const char *buffer, size_t size) ++{ ++ json_lexer_feed(&parser->lexer, buffer, size); ++} ++ ++void json_message_parser_flush(JSONMessageParser *parser) ++{ ++ json_lexer_flush(&parser->lexer); ++ assert(g_queue_is_empty(&parser->tokens)); ++} ++ ++void json_message_parser_destroy(JSONMessageParser *parser) ++{ ++ json_lexer_destroy(&parser->lexer); ++ json_message_free_tokens(parser); ++} +diff --git a/qcow2/lib/qobject/json-writer.c b/qcow2/lib/qobject/json-writer.c +new file mode 100644 +index 00000000..309a31d5 +--- /dev/null ++++ b/qcow2/lib/qobject/json-writer.c +@@ -0,0 +1,247 @@ ++/* ++ * JSON Writer ++ * ++ * Copyright IBM, Corp. 2009 ++ * Copyright (c) 2010-2020 Red Hat Inc. ++ * ++ * Authors: ++ * Anthony Liguori ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/json-writer.h" ++#include "qemu/unicode.h" ++ ++struct JSONWriter { ++ bool pretty; ++ bool need_comma; ++ GString *contents; ++ GByteArray *container_is_array; ++}; ++ ++JSONWriter *json_writer_new(bool pretty) ++{ ++ JSONWriter *writer = g_new(JSONWriter, 1); ++ ++ writer->pretty = pretty; ++ writer->need_comma = false; ++ writer->contents = g_string_new(NULL); ++ writer->container_is_array = g_byte_array_new(); ++ return writer; ++} ++ ++const char *json_writer_get(JSONWriter *writer) ++{ ++ g_assert(!writer->container_is_array->len); ++ return writer->contents->str; ++} ++ ++GString *json_writer_get_and_free(JSONWriter *writer) ++{ ++ GString *contents = writer->contents; ++ ++ writer->contents = NULL; ++ g_byte_array_free(writer->container_is_array, true); ++ g_free(writer); ++ return contents; ++} ++ ++void json_writer_free(JSONWriter *writer) ++{ ++ if (writer) { ++ g_string_free(json_writer_get_and_free(writer), true); ++ } ++} ++ ++static void enter_container(JSONWriter *writer, bool is_array) ++{ ++ unsigned depth = writer->container_is_array->len; ++ ++ g_byte_array_set_size(writer->container_is_array, depth + 1); ++ writer->container_is_array->data[depth] = is_array; ++ writer->need_comma = false; ++} ++ ++static void leave_container(JSONWriter *writer, bool is_array) ++{ ++ unsigned depth = writer->container_is_array->len; ++ ++ assert(depth); ++ assert(writer->container_is_array->data[depth - 1] == is_array); ++ g_byte_array_set_size(writer->container_is_array, depth - 1); ++ writer->need_comma = true; ++} ++ ++static bool in_object(JSONWriter *writer) ++{ ++ unsigned depth = writer->container_is_array->len; ++ ++ return depth && !writer->container_is_array->data[depth - 1]; ++} ++ ++static void pretty_newline(JSONWriter *writer) ++{ ++ if (writer->pretty) { ++ g_string_append_printf(writer->contents, "\n%*s", ++ writer->container_is_array->len * 4, ""); ++ } ++} ++ ++static void pretty_newline_or_space(JSONWriter *writer) ++{ ++ if (writer->pretty) { ++ g_string_append_printf(writer->contents, "\n%*s", ++ writer->container_is_array->len * 4, ""); ++ } else { ++ g_string_append_c(writer->contents, ' '); ++ } ++} ++ ++static void quoted_str(JSONWriter *writer, const char *str) ++{ ++ const char *ptr; ++ char *end; ++ int cp; ++ ++ g_string_append_c(writer->contents, '"'); ++ ++ for (ptr = str; *ptr; ptr = end) { ++ cp = mod_utf8_codepoint(ptr, 6, &end); ++ switch (cp) { ++ case '\"': ++ g_string_append(writer->contents, "\\\""); ++ break; ++ case '\\': ++ g_string_append(writer->contents, "\\\\"); ++ break; ++ case '\b': ++ g_string_append(writer->contents, "\\b"); ++ break; ++ case '\f': ++ g_string_append(writer->contents, "\\f"); ++ break; ++ case '\n': ++ g_string_append(writer->contents, "\\n"); ++ break; ++ case '\r': ++ g_string_append(writer->contents, "\\r"); ++ break; ++ case '\t': ++ g_string_append(writer->contents, "\\t"); ++ break; ++ default: ++ if (cp < 0) { ++ cp = 0xFFFD; /* replacement character */ ++ } ++ if (cp > 0xFFFF) { ++ /* beyond BMP; need a surrogate pair */ ++ g_string_append_printf(writer->contents, "\\u%04X\\u%04X", ++ 0xD800 + ((cp - 0x10000) >> 10), ++ 0xDC00 + ((cp - 0x10000) & 0x3FF)); ++ } else if (cp < 0x20 || cp >= 0x7F) { ++ g_string_append_printf(writer->contents, "\\u%04X", cp); ++ } else { ++ g_string_append_c(writer->contents, cp); ++ } ++ } ++ }; ++ ++ g_string_append_c(writer->contents, '"'); ++} ++ ++static void maybe_comma_name(JSONWriter *writer, const char *name) ++{ ++ if (writer->need_comma) { ++ g_string_append_c(writer->contents, ','); ++ pretty_newline_or_space(writer); ++ } else { ++ if (writer->contents->len) { ++ pretty_newline(writer); ++ } ++ writer->need_comma = true; ++ } ++ ++ if (in_object(writer)) { ++ quoted_str(writer, name); ++ g_string_append(writer->contents, ": "); ++ } ++} ++ ++void json_writer_start_object(JSONWriter *writer, const char *name) ++{ ++ maybe_comma_name(writer, name); ++ g_string_append_c(writer->contents, '{'); ++ enter_container(writer, false); ++} ++ ++void json_writer_end_object(JSONWriter *writer) ++{ ++ leave_container(writer, false); ++ pretty_newline(writer); ++ g_string_append_c(writer->contents, '}'); ++} ++ ++void json_writer_start_array(JSONWriter *writer, const char *name) ++{ ++ maybe_comma_name(writer, name); ++ g_string_append_c(writer->contents, '['); ++ enter_container(writer, true); ++} ++ ++void json_writer_end_array(JSONWriter *writer) ++{ ++ leave_container(writer, true); ++ pretty_newline(writer); ++ g_string_append_c(writer->contents, ']'); ++} ++ ++void json_writer_bool(JSONWriter *writer, const char *name, bool val) ++{ ++ maybe_comma_name(writer, name); ++ g_string_append(writer->contents, val ? "true" : "false"); ++} ++ ++void json_writer_null(JSONWriter *writer, const char *name) ++{ ++ maybe_comma_name(writer, name); ++ g_string_append(writer->contents, "null"); ++} ++ ++void json_writer_int64(JSONWriter *writer, const char *name, int64_t val) ++{ ++ maybe_comma_name(writer, name); ++ g_string_append_printf(writer->contents, "%" PRId64, val); ++} ++ ++void json_writer_uint64(JSONWriter *writer, const char *name, uint64_t val) ++{ ++ maybe_comma_name(writer, name); ++ g_string_append_printf(writer->contents, "%" PRIu64, val); ++} ++ ++void json_writer_double(JSONWriter *writer, const char *name, double val) ++{ ++ maybe_comma_name(writer, name); ++ ++ /* ++ * FIXME: g_string_append_printf() is locale dependent; but JSON ++ * requires numbers to be formatted as if in the C locale. ++ * Dependence on C locale is a pervasive issue in QEMU. ++ */ ++ /* ++ * FIXME: This risks printing Inf or NaN, which are not valid ++ * JSON values. ++ */ ++ g_string_append_printf(writer->contents, "%.17g", val); ++} ++ ++void json_writer_str(JSONWriter *writer, const char *name, const char *str) ++{ ++ maybe_comma_name(writer, name); ++ quoted_str(writer, str); ++} +diff --git a/qcow2/lib/qobject/qbool.c b/qcow2/lib/qobject/qbool.c +new file mode 100644 +index 00000000..c7049c0c +--- /dev/null ++++ b/qcow2/lib/qobject/qbool.c +@@ -0,0 +1,63 @@ ++/* ++ * QBool Module ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qbool.h" ++#include "qobject-internal.h" ++ ++/** ++ * qbool_from_bool(): Create a new QBool from a bool ++ * ++ * Return strong reference. ++ */ ++QBool *qbool_from_bool(bool value) ++{ ++ QBool *qb; ++ ++ qb = g_malloc(sizeof(*qb)); ++ qobject_init(QOBJECT(qb), QTYPE_QBOOL); ++ qb->value = value; ++ ++ return qb; ++} ++ ++/** ++ * qbool_get_bool(): Get the stored bool ++ */ ++bool qbool_get_bool(const QBool *qb) ++{ ++ return qb->value; ++} ++ ++/** ++ * qbool_is_equal(): Test whether the two QBools are equal ++ */ ++bool qbool_is_equal(const QObject *x, const QObject *y) ++{ ++ return qobject_to(QBool, x)->value == qobject_to(QBool, y)->value; ++} ++ ++/** ++ * qbool_destroy_obj(): Free all memory allocated by a ++ * QBool object ++ */ ++void qbool_destroy_obj(QObject *obj) ++{ ++ assert(obj != NULL); ++ g_free(qobject_to(QBool, obj)); ++} ++ ++void qbool_unref(QBool *q) ++{ ++ qobject_unref(q); ++} +diff --git a/qcow2/lib/qobject/qdict.c b/qcow2/lib/qobject/qdict.c +new file mode 100644 +index 00000000..8faff230 +--- /dev/null ++++ b/qcow2/lib/qobject/qdict.c +@@ -0,0 +1,449 @@ ++/* ++ * QDict Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qstring.h" ++#include "qobject-internal.h" ++ ++/** ++ * qdict_new(): Create a new QDict ++ * ++ * Return strong reference. ++ */ ++QDict *qdict_new(void) ++{ ++ QDict *qdict; ++ ++ qdict = g_malloc0(sizeof(*qdict)); ++ qobject_init(QOBJECT(qdict), QTYPE_QDICT); ++ ++ return qdict; ++} ++ ++/** ++ * tdb_hash(): based on the hash algorithm from gdbm, via tdb ++ * (from module-init-tools) ++ */ ++static unsigned int tdb_hash(const char *name) ++{ ++ unsigned value; /* Used to compute the hash value. */ ++ unsigned i; /* Used to cycle through random values. */ ++ ++ /* Set the initial value from the key size. */ ++ for (value = 0x238F13AF * strlen(name), i = 0; name[i]; i++) { ++ value = (value + (((const unsigned char *)name)[i] << (i * 5 % 24))); ++ } ++ ++ return (1103515243 * value + 12345); ++} ++ ++/** ++ * alloc_entry(): allocate a new QDictEntry ++ */ ++static QDictEntry *alloc_entry(const char *key, QObject *value) ++{ ++ QDictEntry *entry; ++ ++ entry = g_malloc0(sizeof(*entry)); ++ entry->key = g_strdup(key); ++ entry->value = value; ++ ++ return entry; ++} ++ ++/** ++ * qdict_entry_value(): Return qdict entry value ++ * ++ * Return weak reference. ++ */ ++QObject *qdict_entry_value(const QDictEntry *entry) ++{ ++ return entry->value; ++} ++ ++/** ++ * qdict_entry_key(): Return qdict entry key ++ * ++ * Return a *pointer* to the string, it has to be duplicated before being ++ * stored. ++ */ ++const char *qdict_entry_key(const QDictEntry *entry) ++{ ++ return entry->key; ++} ++ ++/** ++ * qdict_find(): List lookup function ++ */ ++static QDictEntry *qdict_find(const QDict *qdict, ++ const char *key, unsigned int bucket) ++{ ++ QDictEntry *entry; ++ ++ QLIST_FOREACH(entry, &qdict->table[bucket], next) ++ if (!strcmp(entry->key, key)) { ++ return entry; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * qdict_put_obj(): Put a new QObject into the dictionary ++ * ++ * Insert the pair 'key:value' into 'qdict', if 'key' already exists ++ * its 'value' will be replaced. ++ * ++ * This is done by freeing the reference to the stored QObject and ++ * storing the new one in the same entry. ++ * ++ * NOTE: ownership of 'value' is transferred to the QDict ++ */ ++void qdict_put_obj(QDict *qdict, const char *key, QObject *value) ++{ ++ unsigned int bucket; ++ QDictEntry *entry; ++ ++ bucket = tdb_hash(key) % QDICT_BUCKET_MAX; ++ entry = qdict_find(qdict, key, bucket); ++ if (entry) { ++ /* replace key's value */ ++ qobject_unref(entry->value); ++ entry->value = value; ++ } else { ++ /* allocate a new entry */ ++ entry = alloc_entry(key, value); ++ QLIST_INSERT_HEAD(&qdict->table[bucket], entry, next); ++ qdict->size++; ++ } ++} ++ ++void qdict_put_int(QDict *qdict, const char *key, int64_t value) ++{ ++ qdict_put(qdict, key, qnum_from_int(value)); ++} ++ ++void qdict_put_bool(QDict *qdict, const char *key, bool value) ++{ ++ qdict_put(qdict, key, qbool_from_bool(value)); ++} ++ ++void qdict_put_str(QDict *qdict, const char *key, const char *value) ++{ ++ qdict_put(qdict, key, qstring_from_str(value)); ++} ++ ++void qdict_put_null(QDict *qdict, const char *key) ++{ ++ qdict_put(qdict, key, qnull()); ++} ++ ++/** ++ * qdict_get(): Lookup for a given 'key' ++ * ++ * Return a weak reference to the QObject associated with 'key' if ++ * 'key' is present in the dictionary, NULL otherwise. ++ */ ++QObject *qdict_get(const QDict *qdict, const char *key) ++{ ++ QDictEntry *entry; ++ ++ entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX); ++ return (entry == NULL ? NULL : entry->value); ++} ++ ++/** ++ * qdict_haskey(): Check if 'key' exists ++ * ++ * Return 1 if 'key' exists in the dict, 0 otherwise ++ */ ++int qdict_haskey(const QDict *qdict, const char *key) ++{ ++ unsigned int bucket = tdb_hash(key) % QDICT_BUCKET_MAX; ++ return (qdict_find(qdict, key, bucket) == NULL ? 0 : 1); ++} ++ ++/** ++ * qdict_size(): Return the size of the dictionary ++ */ ++size_t qdict_size(const QDict *qdict) ++{ ++ return qdict->size; ++} ++ ++/** ++ * qdict_get_double(): Get an number mapped by 'key' ++ * ++ * This function assumes that 'key' exists and it stores a QNum. ++ * ++ * Return number mapped by 'key'. ++ */ ++double qdict_get_double(const QDict *qdict, const char *key) ++{ ++ return qnum_get_double(qobject_to(QNum, qdict_get(qdict, key))); ++} ++ ++/** ++ * qdict_get_int(): Get an integer mapped by 'key' ++ * ++ * This function assumes that 'key' exists and it stores a ++ * QNum representable as int. ++ * ++ * Return integer mapped by 'key'. ++ */ ++int64_t qdict_get_int(const QDict *qdict, const char *key) ++{ ++ return qnum_get_int(qobject_to(QNum, qdict_get(qdict, key))); ++} ++ ++/** ++ * qdict_get_bool(): Get a bool mapped by 'key' ++ * ++ * This function assumes that 'key' exists and it stores a ++ * QBool object. ++ * ++ * Return bool mapped by 'key'. ++ */ ++bool qdict_get_bool(const QDict *qdict, const char *key) ++{ ++ return qbool_get_bool(qobject_to(QBool, qdict_get(qdict, key))); ++} ++ ++/** ++ * qdict_get_qlist(): If @qdict maps @key to a QList, return it, else NULL. ++ */ ++QList *qdict_get_qlist(const QDict *qdict, const char *key) ++{ ++ return qobject_to(QList, qdict_get(qdict, key)); ++} ++ ++/** ++ * qdict_get_qdict(): If @qdict maps @key to a QDict, return it, else NULL. ++ */ ++QDict *qdict_get_qdict(const QDict *qdict, const char *key) ++{ ++ return qobject_to(QDict, qdict_get(qdict, key)); ++} ++ ++/** ++ * qdict_get_str(): Get a pointer to the stored string mapped ++ * by 'key' ++ * ++ * This function assumes that 'key' exists and it stores a ++ * QString object. ++ * ++ * Return pointer to the string mapped by 'key'. ++ */ ++const char *qdict_get_str(const QDict *qdict, const char *key) ++{ ++ return qstring_get_str(qobject_to(QString, qdict_get(qdict, key))); ++} ++ ++/** ++ * qdict_get_try_int(): Try to get integer mapped by 'key' ++ * ++ * Return integer mapped by 'key', if it is not present in the ++ * dictionary or if the stored object is not a QNum representing an ++ * integer, 'def_value' will be returned. ++ */ ++int64_t qdict_get_try_int(const QDict *qdict, const char *key, ++ int64_t def_value) ++{ ++ QNum *qnum = qobject_to(QNum, qdict_get(qdict, key)); ++ int64_t val; ++ ++ if (!qnum || !qnum_get_try_int(qnum, &val)) { ++ return def_value; ++ } ++ ++ return val; ++} ++ ++/** ++ * qdict_get_try_bool(): Try to get a bool mapped by 'key' ++ * ++ * Return bool mapped by 'key', if it is not present in the ++ * dictionary or if the stored object is not of QBool type ++ * 'def_value' will be returned. ++ */ ++bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value) ++{ ++ QBool *qbool = qobject_to(QBool, qdict_get(qdict, key)); ++ ++ return qbool ? qbool_get_bool(qbool) : def_value; ++} ++ ++/** ++ * qdict_get_try_str(): Try to get a pointer to the stored string ++ * mapped by 'key' ++ * ++ * Return a pointer to the string mapped by 'key', if it is not present ++ * in the dictionary or if the stored object is not of QString type ++ * NULL will be returned. ++ */ ++const char *qdict_get_try_str(const QDict *qdict, const char *key) ++{ ++ QString *qstr = qobject_to(QString, qdict_get(qdict, key)); ++ ++ return qstr ? qstring_get_str(qstr) : NULL; ++} ++ ++static QDictEntry *qdict_next_entry(const QDict *qdict, int first_bucket) ++{ ++ int i; ++ ++ for (i = first_bucket; i < QDICT_BUCKET_MAX; i++) { ++ if (!QLIST_EMPTY(&qdict->table[i])) { ++ return QLIST_FIRST(&qdict->table[i]); ++ } ++ } ++ ++ return NULL; ++} ++ ++/** ++ * qdict_first(): Return first qdict entry for iteration. ++ */ ++const QDictEntry *qdict_first(const QDict *qdict) ++{ ++ return qdict_next_entry(qdict, 0); ++} ++ ++/** ++ * qdict_next(): Return next qdict entry in an iteration. ++ */ ++const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry) ++{ ++ QDictEntry *ret; ++ ++ ret = QLIST_NEXT(entry, next); ++ if (!ret) { ++ unsigned int bucket = tdb_hash(entry->key) % QDICT_BUCKET_MAX; ++ ret = qdict_next_entry(qdict, bucket + 1); ++ } ++ ++ return ret; ++} ++ ++/** ++ * qdict_clone_shallow(): Clones a given QDict. Its entries are not copied, but ++ * another reference is added. ++ */ ++QDict *qdict_clone_shallow(const QDict *src) ++{ ++ QDict *dest; ++ QDictEntry *entry; ++ int i; ++ ++ dest = qdict_new(); ++ ++ for (i = 0; i < QDICT_BUCKET_MAX; i++) { ++ QLIST_FOREACH(entry, &src->table[i], next) { ++ qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); ++ } ++ } ++ ++ return dest; ++} ++ ++/** ++ * qentry_destroy(): Free all the memory allocated by a QDictEntry ++ */ ++static void qentry_destroy(QDictEntry *e) ++{ ++ assert(e != NULL); ++ assert(e->key != NULL); ++ assert(e->value != NULL); ++ ++ qobject_unref(e->value); ++ g_free(e->key); ++ g_free(e); ++} ++ ++/** ++ * qdict_del(): Delete a 'key:value' pair from the dictionary ++ * ++ * This will destroy all data allocated by this entry. ++ */ ++void qdict_del(QDict *qdict, const char *key) ++{ ++ QDictEntry *entry; ++ ++ entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX); ++ if (entry) { ++ QLIST_REMOVE(entry, next); ++ qentry_destroy(entry); ++ qdict->size--; ++ } ++} ++ ++/** ++ * qdict_is_equal(): Test whether the two QDicts are equal ++ * ++ * Here, equality means whether they contain the same keys and whether ++ * the respective values are in turn equal (i.e. invoking ++ * qobject_is_equal() on them yields true). ++ */ ++bool qdict_is_equal(const QObject *x, const QObject *y) ++{ ++ const QDict *dict_x = qobject_to(QDict, x); ++ const QDict *dict_y = qobject_to(QDict, y); ++ const QDictEntry *e; ++ ++ if (qdict_size(dict_x) != qdict_size(dict_y)) { ++ return false; ++ } ++ ++ for (e = qdict_first(dict_x); e; e = qdict_next(dict_x, e)) { ++ const QObject *obj_x = qdict_entry_value(e); ++ const QObject *obj_y = qdict_get(dict_y, qdict_entry_key(e)); ++ ++ if (!qobject_is_equal(obj_x, obj_y)) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/** ++ * qdict_destroy_obj(): Free all the memory allocated by a QDict ++ */ ++void qdict_destroy_obj(QObject *obj) ++{ ++ int i; ++ QDict *qdict; ++ ++ assert(obj != NULL); ++ qdict = qobject_to(QDict, obj); ++ ++ for (i = 0; i < QDICT_BUCKET_MAX; i++) { ++ QDictEntry *entry = QLIST_FIRST(&qdict->table[i]); ++ while (entry) { ++ QDictEntry *tmp = QLIST_NEXT(entry, next); ++ QLIST_REMOVE(entry, next); ++ qentry_destroy(entry); ++ entry = tmp; ++ } ++ } ++ ++ g_free(qdict); ++} ++ ++void qdict_unref(QDict *q) ++{ ++ qobject_unref(q); ++} +diff --git a/qcow2/lib/qobject/qjson.c b/qcow2/lib/qobject/qjson.c +new file mode 100644 +index 00000000..167fcb42 +--- /dev/null ++++ b/qcow2/lib/qobject/qjson.c +@@ -0,0 +1,232 @@ ++/* ++ * QObject JSON integration ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi/qmp/json-parser.h" ++#include "qapi/qmp/json-writer.h" ++#include "qapi/qmp/qjson.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++ ++typedef struct JSONParsingState { ++ JSONMessageParser parser; ++ QObject *result; ++ Error *err; ++} JSONParsingState; ++ ++static void consume_json(void *opaque, QObject *json, Error *err) ++{ ++ JSONParsingState *s = opaque; ++ ++ assert(!json != !err); ++ assert(!s->result || !s->err); ++ ++ if (s->result) { ++ qobject_unref(s->result); ++ s->result = NULL; ++ error_setg(&s->err, "Expecting at most one JSON value"); ++ } ++ if (s->err) { ++ qobject_unref(json); ++ error_free(err); ++ return; ++ } ++ s->result = json; ++ s->err = err; ++} ++ ++/* ++ * Parse @string as JSON value. ++ * If @ap is non-null, interpolate %-escapes. ++ * Takes ownership of %p arguments. ++ * On success, return the JSON value. ++ * On failure, store an error through @errp and return NULL. ++ * Ownership of %p arguments becomes indeterminate then. To avoid ++ * leaks, callers passing %p must terminate on error, e.g. by passing ++ * &error_abort. ++ */ ++static QObject *qobject_from_jsonv(const char *string, va_list *ap, ++ Error **errp) ++{ ++ JSONParsingState state = {}; ++ ++ json_message_parser_init(&state.parser, consume_json, &state, ap); ++ json_message_parser_feed(&state.parser, string, strlen(string)); ++ json_message_parser_flush(&state.parser); ++ json_message_parser_destroy(&state.parser); ++ ++ if (!state.result && !state.err) { ++ error_setg(&state.err, "Expecting a JSON value"); ++ } ++ ++ error_propagate(errp, state.err); ++ return state.result; ++} ++ ++QObject *qobject_from_json(const char *string, Error **errp) ++{ ++ return qobject_from_jsonv(string, NULL, errp); ++} ++ ++/* ++ * Parse @string as JSON value with %-escapes interpolated. ++ * Abort on error. Do not use with untrusted @string. ++ * Return the resulting QObject. It is never null. ++ */ ++QObject *qobject_from_vjsonf_nofail(const char *string, va_list ap) ++{ ++ va_list ap_copy; ++ QObject *obj; ++ ++ /* va_copy() is needed when va_list is an array type */ ++ va_copy(ap_copy, ap); ++ obj = qobject_from_jsonv(string, &ap_copy, &error_abort); ++ va_end(ap_copy); ++ ++ assert(obj); ++ return obj; ++} ++ ++/* ++ * Parse @string as JSON value with %-escapes interpolated. ++ * Abort on error. Do not use with untrusted @string. ++ * Return the resulting QObject. It is never null. ++ */ ++QObject *qobject_from_jsonf_nofail(const char *string, ...) ++{ ++ QObject *obj; ++ va_list ap; ++ ++ va_start(ap, string); ++ obj = qobject_from_vjsonf_nofail(string, ap); ++ va_end(ap); ++ ++ return obj; ++} ++ ++/* ++ * Parse @string as JSON object with %-escapes interpolated. ++ * Abort on error. Do not use with untrusted @string. ++ * Return the resulting QDict. It is never null. ++ */ ++QDict *qdict_from_vjsonf_nofail(const char *string, va_list ap) ++{ ++ QDict *qdict; ++ ++ qdict = qobject_to(QDict, qobject_from_vjsonf_nofail(string, ap)); ++ assert(qdict); ++ return qdict; ++} ++ ++/* ++ * Parse @string as JSON object with %-escapes interpolated. ++ * Abort on error. Do not use with untrusted @string. ++ * Return the resulting QDict. It is never null. ++ */ ++QDict *qdict_from_jsonf_nofail(const char *string, ...) ++{ ++ QDict *qdict; ++ va_list ap; ++ ++ va_start(ap, string); ++ qdict = qdict_from_vjsonf_nofail(string, ap); ++ va_end(ap); ++ return qdict; ++} ++ ++static void to_json(JSONWriter *writer, const char *name, ++ const QObject *obj) ++{ ++ switch (qobject_type(obj)) { ++ case QTYPE_QNULL: ++ json_writer_null(writer, name); ++ break; ++ case QTYPE_QNUM: { ++ QNum *val = qobject_to(QNum, obj); ++ ++ switch (val->kind) { ++ case QNUM_I64: ++ json_writer_int64(writer, name, val->u.i64); ++ break; ++ case QNUM_U64: ++ json_writer_uint64(writer, name, val->u.u64); ++ break; ++ case QNUM_DOUBLE: ++ json_writer_double(writer, name, val->u.dbl); ++ break; ++ default: ++ abort(); ++ } ++ break; ++ } ++ case QTYPE_QSTRING: { ++ QString *val = qobject_to(QString, obj); ++ ++ json_writer_str(writer, name, qstring_get_str(val)); ++ break; ++ } ++ case QTYPE_QDICT: { ++ QDict *val = qobject_to(QDict, obj); ++ const QDictEntry *entry; ++ ++ json_writer_start_object(writer, name); ++ ++ for (entry = qdict_first(val); ++ entry; ++ entry = qdict_next(val, entry)) { ++ to_json(writer, qdict_entry_key(entry), qdict_entry_value(entry)); ++ } ++ ++ json_writer_end_object(writer); ++ break; ++ } ++ case QTYPE_QLIST: { ++ QList *val = qobject_to(QList, obj); ++ QListEntry *entry; ++ ++ json_writer_start_array(writer, name); ++ ++ QLIST_FOREACH_ENTRY(val, entry) { ++ to_json(writer, NULL, qlist_entry_obj(entry)); ++ } ++ ++ json_writer_end_array(writer); ++ break; ++ } ++ case QTYPE_QBOOL: { ++ QBool *val = qobject_to(QBool, obj); ++ ++ json_writer_bool(writer, name, qbool_get_bool(val)); ++ break; ++ } ++ default: ++ abort(); ++ } ++} ++ ++GString *qobject_to_json_pretty(const QObject *obj, bool pretty) ++{ ++ JSONWriter *writer = json_writer_new(pretty); ++ ++ to_json(writer, NULL, obj); ++ return json_writer_get_and_free(writer); ++} ++ ++GString *qobject_to_json(const QObject *obj) ++{ ++ return qobject_to_json_pretty(obj, false); ++} +diff --git a/qcow2/lib/qobject/qlist.c b/qcow2/lib/qobject/qlist.c +new file mode 100644 +index 00000000..356ad946 +--- /dev/null ++++ b/qcow2/lib/qobject/qlist.c +@@ -0,0 +1,189 @@ ++/* ++ * QList Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qemu/queue.h" ++#include "qobject-internal.h" ++ ++/** ++ * qlist_new(): Create a new QList ++ * ++ * Return strong reference. ++ */ ++QList *qlist_new(void) ++{ ++ QList *qlist; ++ ++ qlist = g_malloc(sizeof(*qlist)); ++ qobject_init(QOBJECT(qlist), QTYPE_QLIST); ++ QTAILQ_INIT(&qlist->head); ++ ++ return qlist; ++} ++ ++QList *qlist_copy(QList *src) ++{ ++ QList *dst = qlist_new(); ++ QListEntry *entry; ++ QObject *elt; ++ ++ QLIST_FOREACH_ENTRY(src, entry) { ++ elt = qlist_entry_obj(entry); ++ qobject_ref(elt); ++ qlist_append_obj(dst, elt); ++ } ++ return dst; ++} ++ ++/** ++ * qlist_append_obj(): Append an QObject into QList ++ * ++ * NOTE: ownership of 'value' is transferred to the QList ++ */ ++void qlist_append_obj(QList *qlist, QObject *value) ++{ ++ QListEntry *entry; ++ ++ entry = g_malloc(sizeof(*entry)); ++ entry->value = value; ++ ++ QTAILQ_INSERT_TAIL(&qlist->head, entry, next); ++} ++ ++void qlist_append_int(QList *qlist, int64_t value) ++{ ++ qlist_append(qlist, qnum_from_int(value)); ++} ++ ++void qlist_append_bool(QList *qlist, bool value) ++{ ++ qlist_append(qlist, qbool_from_bool(value)); ++} ++ ++void qlist_append_str(QList *qlist, const char *value) ++{ ++ qlist_append(qlist, qstring_from_str(value)); ++} ++ ++void qlist_append_null(QList *qlist) ++{ ++ qlist_append(qlist, qnull()); ++} ++ ++QObject *qlist_pop(QList *qlist) ++{ ++ QListEntry *entry; ++ QObject *ret; ++ ++ if (qlist == NULL || QTAILQ_EMPTY(&qlist->head)) { ++ return NULL; ++ } ++ ++ entry = QTAILQ_FIRST(&qlist->head); ++ QTAILQ_REMOVE(&qlist->head, entry, next); ++ ++ ret = entry->value; ++ g_free(entry); ++ ++ return ret; ++} ++ ++QObject *qlist_peek(QList *qlist) ++{ ++ QListEntry *entry; ++ ++ if (qlist == NULL || QTAILQ_EMPTY(&qlist->head)) { ++ return NULL; ++ } ++ ++ entry = QTAILQ_FIRST(&qlist->head); ++ ++ return entry->value; ++} ++ ++int qlist_empty(const QList *qlist) ++{ ++ return QTAILQ_EMPTY(&qlist->head); ++} ++ ++size_t qlist_size(const QList *qlist) ++{ ++ size_t count = 0; ++ QListEntry *entry; ++ ++ QLIST_FOREACH_ENTRY(qlist, entry) { ++ count++; ++ } ++ return count; ++} ++ ++/** ++ * qlist_is_equal(): Test whether the two QLists are equal ++ * ++ * In order to be considered equal, the respective two objects at each ++ * index of the two lists have to compare equal (regarding ++ * qobject_is_equal()), and both lists have to have the same number of ++ * elements. ++ * That means both lists have to contain equal objects in equal order. ++ */ ++bool qlist_is_equal(const QObject *x, const QObject *y) ++{ ++ const QList *list_x = qobject_to(QList, x); ++ const QList *list_y = qobject_to(QList, y); ++ const QListEntry *entry_x, *entry_y; ++ ++ entry_x = qlist_first(list_x); ++ entry_y = qlist_first(list_y); ++ ++ while (entry_x && entry_y) { ++ if (!qobject_is_equal(qlist_entry_obj(entry_x), ++ qlist_entry_obj(entry_y))) ++ { ++ return false; ++ } ++ ++ entry_x = qlist_next(entry_x); ++ entry_y = qlist_next(entry_y); ++ } ++ ++ return !entry_x && !entry_y; ++} ++ ++/** ++ * qlist_destroy_obj(): Free all the memory allocated by a QList ++ */ ++void qlist_destroy_obj(QObject *obj) ++{ ++ QList *qlist; ++ QListEntry *entry, *next_entry; ++ ++ assert(obj != NULL); ++ qlist = qobject_to(QList, obj); ++ ++ QTAILQ_FOREACH_SAFE(entry, &qlist->head, next, next_entry) { ++ QTAILQ_REMOVE(&qlist->head, entry, next); ++ qobject_unref(entry->value); ++ g_free(entry); ++ } ++ ++ g_free(qlist); ++} ++ ++void qlist_unref(QList *q) ++{ ++ qobject_unref(q); ++} +diff --git a/qcow2/lib/qobject/qnull.c b/qcow2/lib/qobject/qnull.c +new file mode 100644 +index 00000000..445a5db7 +--- /dev/null ++++ b/qcow2/lib/qobject/qnull.c +@@ -0,0 +1,36 @@ ++/* ++ * QNull ++ * ++ * Copyright (C) 2015 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qnull.h" ++#include "qobject-internal.h" ++ ++QNull qnull_ = { ++ .base = { ++ .type = QTYPE_QNULL, ++ .refcnt = 1, ++ }, ++}; ++ ++/** ++ * qnull_is_equal(): Always return true because any two QNull objects ++ * are equal. ++ */ ++bool qnull_is_equal(const QObject *x, const QObject *y) ++{ ++ return true; ++} ++ ++void qnull_unref(QNull *q) ++{ ++ qobject_unref(q); ++} +diff --git a/qcow2/lib/qobject/qnum.c b/qcow2/lib/qobject/qnum.c +new file mode 100644 +index 00000000..2bbeaedc +--- /dev/null ++++ b/qcow2/lib/qobject/qnum.c +@@ -0,0 +1,246 @@ ++/* ++ * QNum Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * Anthony Liguori ++ * Marc-André Lureau ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qnum.h" ++#include "qobject-internal.h" ++ ++/** ++ * qnum_from_int(): Create a new QNum from an int64_t ++ * ++ * Return strong reference. ++ */ ++QNum *qnum_from_int(int64_t value) ++{ ++ QNum *qn = g_new(QNum, 1); ++ ++ qobject_init(QOBJECT(qn), QTYPE_QNUM); ++ qn->kind = QNUM_I64; ++ qn->u.i64 = value; ++ ++ return qn; ++} ++ ++/** ++ * qnum_from_uint(): Create a new QNum from an uint64_t ++ * ++ * Return strong reference. ++ */ ++QNum *qnum_from_uint(uint64_t value) ++{ ++ QNum *qn = g_new(QNum, 1); ++ ++ qobject_init(QOBJECT(qn), QTYPE_QNUM); ++ qn->kind = QNUM_U64; ++ qn->u.u64 = value; ++ ++ return qn; ++} ++ ++/** ++ * qnum_from_double(): Create a new QNum from a double ++ * ++ * Return strong reference. ++ */ ++QNum *qnum_from_double(double value) ++{ ++ QNum *qn = g_new(QNum, 1); ++ ++ qobject_init(QOBJECT(qn), QTYPE_QNUM); ++ qn->kind = QNUM_DOUBLE; ++ qn->u.dbl = value; ++ ++ return qn; ++} ++ ++/** ++ * qnum_get_try_int(): Get an integer representation of the number ++ * ++ * Return true on success. ++ */ ++bool qnum_get_try_int(const QNum *qn, int64_t *val) ++{ ++ switch (qn->kind) { ++ case QNUM_I64: ++ *val = qn->u.i64; ++ return true; ++ case QNUM_U64: ++ if (qn->u.u64 > INT64_MAX) { ++ return false; ++ } ++ *val = qn->u.u64; ++ return true; ++ case QNUM_DOUBLE: ++ return false; ++ } ++ ++ assert(0); ++ return false; ++} ++ ++/** ++ * qnum_get_int(): Get an integer representation of the number ++ * ++ * assert() on failure. ++ */ ++int64_t qnum_get_int(const QNum *qn) ++{ ++ int64_t val; ++ bool success = qnum_get_try_int(qn, &val); ++ assert(success); ++ return val; ++} ++ ++/** ++ * qnum_get_uint(): Get an unsigned integer from the number ++ * ++ * Return true on success. ++ */ ++bool qnum_get_try_uint(const QNum *qn, uint64_t *val) ++{ ++ switch (qn->kind) { ++ case QNUM_I64: ++ if (qn->u.i64 < 0) { ++ return false; ++ } ++ *val = qn->u.i64; ++ return true; ++ case QNUM_U64: ++ *val = qn->u.u64; ++ return true; ++ case QNUM_DOUBLE: ++ return false; ++ } ++ ++ assert(0); ++ return false; ++} ++ ++/** ++ * qnum_get_uint(): Get an unsigned integer from the number ++ * ++ * assert() on failure. ++ */ ++uint64_t qnum_get_uint(const QNum *qn) ++{ ++ uint64_t val; ++ bool success = qnum_get_try_uint(qn, &val); ++ assert(success); ++ return val; ++} ++ ++/** ++ * qnum_get_double(): Get a float representation of the number ++ * ++ * qnum_get_double() loses precision for integers beyond 53 bits. ++ */ ++double qnum_get_double(QNum *qn) ++{ ++ switch (qn->kind) { ++ case QNUM_I64: ++ return qn->u.i64; ++ case QNUM_U64: ++ return qn->u.u64; ++ case QNUM_DOUBLE: ++ return qn->u.dbl; ++ } ++ ++ assert(0); ++ return 0.0; ++} ++ ++char *qnum_to_string(QNum *qn) ++{ ++ switch (qn->kind) { ++ case QNUM_I64: ++ return g_strdup_printf("%" PRId64, qn->u.i64); ++ case QNUM_U64: ++ return g_strdup_printf("%" PRIu64, qn->u.u64); ++ case QNUM_DOUBLE: ++ /* 17 digits suffice for IEEE double */ ++ return g_strdup_printf("%.17g", qn->u.dbl); ++ } ++ ++ assert(0); ++ return NULL; ++} ++ ++/** ++ * qnum_is_equal(): Test whether the two QNums are equal ++ * ++ * Negative integers are never considered equal to unsigned integers, ++ * but positive integers in the range [0, INT64_MAX] are considered ++ * equal independently of whether the QNum's kind is i64 or u64. ++ * ++ * Doubles are never considered equal to integers. ++ */ ++bool qnum_is_equal(const QObject *x, const QObject *y) ++{ ++ QNum *num_x = qobject_to(QNum, x); ++ QNum *num_y = qobject_to(QNum, y); ++ ++ switch (num_x->kind) { ++ case QNUM_I64: ++ switch (num_y->kind) { ++ case QNUM_I64: ++ /* Comparison in native int64_t type */ ++ return num_x->u.i64 == num_y->u.i64; ++ case QNUM_U64: ++ /* Implicit conversion of x to uin64_t, so we have to ++ * check its sign before */ ++ return num_x->u.i64 >= 0 && num_x->u.i64 == num_y->u.u64; ++ case QNUM_DOUBLE: ++ return false; ++ } ++ abort(); ++ case QNUM_U64: ++ switch (num_y->kind) { ++ case QNUM_I64: ++ return qnum_is_equal(y, x); ++ case QNUM_U64: ++ /* Comparison in native uint64_t type */ ++ return num_x->u.u64 == num_y->u.u64; ++ case QNUM_DOUBLE: ++ return false; ++ } ++ abort(); ++ case QNUM_DOUBLE: ++ switch (num_y->kind) { ++ case QNUM_I64: ++ case QNUM_U64: ++ return false; ++ case QNUM_DOUBLE: ++ /* Comparison in native double type */ ++ return num_x->u.dbl == num_y->u.dbl; ++ } ++ abort(); ++ } ++ ++ abort(); ++} ++ ++/** ++ * qnum_destroy_obj(): Free all memory allocated by a ++ * QNum object ++ */ ++void qnum_destroy_obj(QObject *obj) ++{ ++ assert(obj != NULL); ++ g_free(qobject_to(QNum, obj)); ++} ++ ++void qnum_unref(QNum *q) ++{ ++ qobject_unref(q); ++} +diff --git a/qcow2/lib/qobject/qobject-internal.h b/qcow2/lib/qobject/qobject-internal.h +new file mode 100644 +index 00000000..b310c8e1 +--- /dev/null ++++ b/qcow2/lib/qobject/qobject-internal.h +@@ -0,0 +1,39 @@ ++/* ++ * QObject internals ++ * ++ * Copyright (C) 2015 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QOBJECT_INTERNAL_H ++#define QOBJECT_INTERNAL_H ++ ++#include "qapi/qmp/qobject.h" ++ ++static inline void qobject_init(QObject *obj, QType type) ++{ ++ assert(QTYPE_NONE < type && type < QTYPE__MAX); ++ obj->base.refcnt = 1; ++ obj->base.type = type; ++} ++ ++void qbool_destroy_obj(QObject *obj); ++bool qbool_is_equal(const QObject *x, const QObject *y); ++ ++void qdict_destroy_obj(QObject *obj); ++bool qdict_is_equal(const QObject *x, const QObject *y); ++ ++void qlist_destroy_obj(QObject *obj); ++bool qlist_is_equal(const QObject *x, const QObject *y); ++ ++bool qnull_is_equal(const QObject *x, const QObject *y); ++ ++void qnum_destroy_obj(QObject *obj); ++bool qnum_is_equal(const QObject *x, const QObject *y); ++ ++void qstring_destroy_obj(QObject *obj); ++bool qstring_is_equal(const QObject *x, const QObject *y); ++ ++#endif +diff --git a/qcow2/lib/qobject/qobject.c b/qcow2/lib/qobject/qobject.c +new file mode 100644 +index 00000000..d7077b8f +--- /dev/null ++++ b/qcow2/lib/qobject/qobject.c +@@ -0,0 +1,72 @@ ++/* ++ * QObject ++ * ++ * Copyright (C) 2015 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qstring.h" ++#include "qobject-internal.h" ++ ++QEMU_BUILD_BUG_MSG( ++ offsetof(QNull, base) != 0 || ++ offsetof(QNum, base) != 0 || ++ offsetof(QString, base) != 0 || ++ offsetof(QDict, base) != 0 || ++ offsetof(QList, base) != 0 || ++ offsetof(QBool, base) != 0, ++ "base qobject must be at offset 0"); ++ ++static void (*qdestroy[QTYPE__MAX])(QObject *) = { ++ [QTYPE_NONE] = NULL, /* No such object exists */ ++ [QTYPE_QNULL] = NULL, /* qnull_ is indestructible */ ++ [QTYPE_QNUM] = qnum_destroy_obj, ++ [QTYPE_QSTRING] = qstring_destroy_obj, ++ [QTYPE_QDICT] = qdict_destroy_obj, ++ [QTYPE_QLIST] = qlist_destroy_obj, ++ [QTYPE_QBOOL] = qbool_destroy_obj, ++}; ++ ++void qobject_destroy(QObject *obj) ++{ ++ assert(!obj->base.refcnt); ++ assert(QTYPE_QNULL < obj->base.type && obj->base.type < QTYPE__MAX); ++ qdestroy[obj->base.type](obj); ++} ++ ++ ++static bool (*qis_equal[QTYPE__MAX])(const QObject *, const QObject *) = { ++ [QTYPE_NONE] = NULL, /* No such object exists */ ++ [QTYPE_QNULL] = qnull_is_equal, ++ [QTYPE_QNUM] = qnum_is_equal, ++ [QTYPE_QSTRING] = qstring_is_equal, ++ [QTYPE_QDICT] = qdict_is_equal, ++ [QTYPE_QLIST] = qlist_is_equal, ++ [QTYPE_QBOOL] = qbool_is_equal, ++}; ++ ++bool qobject_is_equal(const QObject *x, const QObject *y) ++{ ++ /* We cannot test x == y because an object does not need to be ++ * equal to itself (e.g. NaN floats are not). */ ++ ++ if (!x && !y) { ++ return true; ++ } ++ ++ if (!x || !y || x->base.type != y->base.type) { ++ return false; ++ } ++ ++ assert(QTYPE_NONE < x->base.type && x->base.type < QTYPE__MAX); ++ ++ return qis_equal[x->base.type](x, y); ++} +diff --git a/qcow2/lib/qobject/qstring.c b/qcow2/lib/qobject/qstring.c +new file mode 100644 +index 00000000..794f8c93 +--- /dev/null ++++ b/qcow2/lib/qobject/qstring.c +@@ -0,0 +1,107 @@ ++/* ++ * QString Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qstring.h" ++#include "qobject-internal.h" ++ ++/** ++ * qstring_new(): Create a new empty QString ++ * ++ * Return strong reference. ++ */ ++QString *qstring_new(void) ++{ ++ return qstring_from_str(""); ++} ++ ++/** ++ * qstring_from_substr(): Create a new QString from a C string substring ++ * ++ * Return string reference ++ */ ++QString *qstring_from_substr(const char *str, size_t start, size_t end) ++{ ++ QString *qstring; ++ ++ assert(start <= end); ++ qstring = g_malloc(sizeof(*qstring)); ++ qobject_init(QOBJECT(qstring), QTYPE_QSTRING); ++ qstring->string = g_strndup(str + start, end - start); ++ return qstring; ++} ++ ++/** ++ * qstring_from_str(): Create a new QString from a regular C string ++ * ++ * Return strong reference. ++ */ ++QString *qstring_from_str(const char *str) ++{ ++ return qstring_from_substr(str, 0, strlen(str)); ++} ++ ++/** ++ * qstring_from_gstring(): Convert a GString to a QString ++ * ++ * Return strong reference. ++ */ ++ ++QString *qstring_from_gstring(GString *gstr) ++{ ++ QString *qstring; ++ ++ qstring = g_malloc(sizeof(*qstring)); ++ qobject_init(QOBJECT(qstring), QTYPE_QSTRING); ++ qstring->string = g_string_free(gstr, false); ++ return qstring; ++} ++ ++ ++/** ++ * qstring_get_str(): Return a pointer to the stored string ++ * ++ * NOTE: Should be used with caution, if the object is deallocated ++ * this pointer becomes invalid. ++ */ ++const char *qstring_get_str(const QString *qstring) ++{ ++ return qstring->string; ++} ++ ++/** ++ * qstring_is_equal(): Test whether the two QStrings are equal ++ */ ++bool qstring_is_equal(const QObject *x, const QObject *y) ++{ ++ return !strcmp(qobject_to(QString, x)->string, ++ qobject_to(QString, y)->string); ++} ++ ++/** ++ * qstring_destroy_obj(): Free all memory allocated by a QString ++ * object ++ */ ++void qstring_destroy_obj(QObject *obj) ++{ ++ QString *qs; ++ ++ assert(obj != NULL); ++ qs = qobject_to(QString, obj); ++ g_free((char *)qs->string); ++ g_free(qs); ++} ++ ++void qstring_unref(QString *q) ++{ ++ qobject_unref(q); ++} +diff --git a/qcow2/lib/qom/container.c b/qcow2/lib/qom/container.c +new file mode 100644 +index 00000000..455e8410 +--- /dev/null ++++ b/qcow2/lib/qom/container.c +@@ -0,0 +1,52 @@ ++/* ++ * Device Container ++ * ++ * Copyright IBM, Corp. 2012 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qom/object.h" ++#include "qemu/module.h" ++ ++static const TypeInfo container_info = { ++ .name = "container", ++ .parent = TYPE_OBJECT, ++}; ++ ++static void container_register_types(void) ++{ ++ type_register_static(&container_info); ++} ++ ++Object *container_get(Object *root, const char *path) ++{ ++ Object *obj, *child; ++ char **parts; ++ int i; ++ ++ parts = g_strsplit(path, "/", 0); ++ assert(parts != NULL && parts[0] != NULL && !parts[0][0]); ++ obj = root; ++ ++ for (i = 1; parts[i] != NULL; i++, obj = child) { ++ child = object_resolve_path_component(obj, parts[i]); ++ if (!child) { ++ child = object_new("container"); ++ object_property_add_child(obj, parts[i], child); ++ object_unref(child); ++ } ++ } ++ ++ g_strfreev(parts); ++ ++ return obj; ++} ++ ++ ++type_init(container_register_types) +diff --git a/qcow2/lib/qom/object.c b/qcow2/lib/qom/object.c +new file mode 100644 +index 00000000..157a45c5 +--- /dev/null ++++ b/qcow2/lib/qom/object.c +@@ -0,0 +1,2899 @@ ++/* ++ * QEMU Object Model ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "hw/qdev-core.h" ++#include "qapi/error.h" ++#include "qom/object.h" ++#include "qom/object_interfaces.h" ++#include "qemu/cutils.h" ++#include "qemu/memalign.h" ++#include "qapi/visitor.h" ++#include "qapi/string-input-visitor.h" ++#include "qapi/string-output-visitor.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qapi/forward-visitor.h" ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi/qmp/qjson.h" ++#include "trace.h" ++ ++/* TODO: replace QObject with a simpler visitor to avoid a dependency ++ * of the QOM core on QObject? */ ++#include "qom/qom-qobject.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qemu/error-report.h" ++ ++#define MAX_INTERFACES 32 ++ ++typedef struct InterfaceImpl InterfaceImpl; ++typedef struct TypeImpl TypeImpl; ++ ++struct InterfaceImpl ++{ ++ const char *typename; ++}; ++ ++struct TypeImpl ++{ ++ const char *name; ++ ++ size_t class_size; ++ ++ size_t instance_size; ++ size_t instance_align; ++ ++ void (*class_init)(ObjectClass *klass, void *data); ++ void (*class_base_init)(ObjectClass *klass, void *data); ++ ++ void *class_data; ++ ++ void (*instance_init)(Object *obj); ++ void (*instance_post_init)(Object *obj); ++ void (*instance_finalize)(Object *obj); ++ ++ bool abstract; ++ ++ const char *parent; ++ TypeImpl *parent_type; ++ ++ ObjectClass *class; ++ ++ int num_interfaces; ++ InterfaceImpl interfaces[MAX_INTERFACES]; ++}; ++ ++static Type type_interface; ++ ++static GHashTable *type_table_get(void) ++{ ++ static GHashTable *type_table; ++ ++ if (type_table == NULL) { ++ type_table = g_hash_table_new(g_str_hash, g_str_equal); ++ } ++ ++ return type_table; ++} ++ ++static bool enumerating_types; ++ ++static void type_table_add(TypeImpl *ti) ++{ ++ assert(!enumerating_types); ++ g_hash_table_insert(type_table_get(), (void *)ti->name, ti); ++} ++ ++static TypeImpl *type_table_lookup(const char *name) ++{ ++ return g_hash_table_lookup(type_table_get(), name); ++} ++ ++static TypeImpl *type_new(const TypeInfo *info) ++{ ++ TypeImpl *ti = g_malloc0(sizeof(*ti)); ++ int i; ++ ++ g_assert(info->name != NULL); ++ ++ if (type_table_lookup(info->name) != NULL) { ++ fprintf(stderr, "Registering `%s' which already exists\n", info->name); ++ abort(); ++ } ++ ++ ti->name = g_strdup(info->name); ++ ti->parent = g_strdup(info->parent); ++ ++ ti->class_size = info->class_size; ++ ti->instance_size = info->instance_size; ++ ti->instance_align = info->instance_align; ++ ++ ti->class_init = info->class_init; ++ ti->class_base_init = info->class_base_init; ++ ti->class_data = info->class_data; ++ ++ ti->instance_init = info->instance_init; ++ ti->instance_post_init = info->instance_post_init; ++ ti->instance_finalize = info->instance_finalize; ++ ++ ti->abstract = info->abstract; ++ ++ for (i = 0; info->interfaces && info->interfaces[i].type; i++) { ++ ti->interfaces[i].typename = g_strdup(info->interfaces[i].type); ++ } ++ ti->num_interfaces = i; ++ ++ return ti; ++} ++ ++static bool type_name_is_valid(const char *name) ++{ ++ const int slen = strlen(name); ++ int plen; ++ ++ g_assert(slen > 1); ++ ++ /* ++ * Ideally, the name should start with a letter - however, we've got ++ * too many names starting with a digit already, so allow digits here, ++ * too (except '0' which is not used yet) ++ */ ++ if (!g_ascii_isalnum(name[0]) || name[0] == '0') { ++ return false; ++ } ++ ++ plen = strspn(name, "abcdefghijklmnopqrstuvwxyz" ++ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ++ "0123456789-_."); ++ ++ return plen == slen; ++} ++ ++static TypeImpl *type_register_internal(const TypeInfo *info) ++{ ++ TypeImpl *ti; ++ ++ if (!type_name_is_valid(info->name)) { ++ fprintf(stderr, "Registering '%s' with illegal type name\n", info->name); ++ abort(); ++ } ++ ++ ti = type_new(info); ++ ++ type_table_add(ti); ++ return ti; ++} ++ ++TypeImpl *type_register(const TypeInfo *info) ++{ ++ assert(info->parent); ++ return type_register_internal(info); ++} ++ ++TypeImpl *type_register_static(const TypeInfo *info) ++{ ++ return type_register(info); ++} ++ ++void type_register_static_array(const TypeInfo *infos, int nr_infos) ++{ ++ int i; ++ ++ for (i = 0; i < nr_infos; i++) { ++ type_register_static(&infos[i]); ++ } ++} ++ ++static TypeImpl *type_get_by_name(const char *name) ++{ ++ if (name == NULL) { ++ return NULL; ++ } ++ ++ return type_table_lookup(name); ++} ++ ++static TypeImpl *type_get_parent(TypeImpl *type) ++{ ++ if (!type->parent_type && type->parent) { ++ type->parent_type = type_get_by_name(type->parent); ++ if (!type->parent_type) { ++ fprintf(stderr, "Type '%s' is missing its parent '%s'\n", ++ type->name, type->parent); ++ abort(); ++ } ++ } ++ ++ return type->parent_type; ++} ++ ++static bool type_has_parent(TypeImpl *type) ++{ ++ return (type->parent != NULL); ++} ++ ++static size_t type_class_get_size(TypeImpl *ti) ++{ ++ if (ti->class_size) { ++ return ti->class_size; ++ } ++ ++ if (type_has_parent(ti)) { ++ return type_class_get_size(type_get_parent(ti)); ++ } ++ ++ return sizeof(ObjectClass); ++} ++ ++static size_t type_object_get_size(TypeImpl *ti) ++{ ++ if (ti->instance_size) { ++ return ti->instance_size; ++ } ++ ++ if (type_has_parent(ti)) { ++ return type_object_get_size(type_get_parent(ti)); ++ } ++ ++ return 0; ++} ++ ++static size_t type_object_get_align(TypeImpl *ti) ++{ ++ if (ti->instance_align) { ++ return ti->instance_align; ++ } ++ ++ if (type_has_parent(ti)) { ++ return type_object_get_align(type_get_parent(ti)); ++ } ++ ++ return 0; ++} ++ ++size_t object_type_get_instance_size(const char *typename) ++{ ++ TypeImpl *type = type_get_by_name(typename); ++ ++ g_assert(type != NULL); ++ return type_object_get_size(type); ++} ++ ++static bool type_is_ancestor(TypeImpl *type, TypeImpl *target_type) ++{ ++ assert(target_type); ++ ++ /* Check if target_type is a direct ancestor of type */ ++ while (type) { ++ if (type == target_type) { ++ return true; ++ } ++ ++ type = type_get_parent(type); ++ } ++ ++ return false; ++} ++ ++static void type_initialize(TypeImpl *ti); ++ ++static void type_initialize_interface(TypeImpl *ti, TypeImpl *interface_type, ++ TypeImpl *parent_type) ++{ ++ InterfaceClass *new_iface; ++ TypeInfo info = { }; ++ TypeImpl *iface_impl; ++ ++ info.parent = parent_type->name; ++ info.name = g_strdup_printf("%s::%s", ti->name, interface_type->name); ++ info.abstract = true; ++ ++ iface_impl = type_new(&info); ++ iface_impl->parent_type = parent_type; ++ type_initialize(iface_impl); ++ g_free((char *)info.name); ++ ++ new_iface = (InterfaceClass *)iface_impl->class; ++ new_iface->concrete_class = ti->class; ++ new_iface->interface_type = interface_type; ++ ++ ti->class->interfaces = g_slist_append(ti->class->interfaces, new_iface); ++} ++ ++static void object_property_free(gpointer data) ++{ ++ ObjectProperty *prop = data; ++ ++ if (prop->defval) { ++ qobject_unref(prop->defval); ++ prop->defval = NULL; ++ } ++ g_free(prop->name); ++ g_free(prop->type); ++ g_free(prop->description); ++ g_free(prop); ++} ++ ++static void type_initialize(TypeImpl *ti) ++{ ++ TypeImpl *parent; ++ ++ if (ti->class) { ++ return; ++ } ++ ++ ti->class_size = type_class_get_size(ti); ++ ti->instance_size = type_object_get_size(ti); ++ ti->instance_align = type_object_get_align(ti); ++ /* Any type with zero instance_size is implicitly abstract. ++ * This means interface types are all abstract. ++ */ ++ if (ti->instance_size == 0) { ++ ti->abstract = true; ++ } ++ if (type_is_ancestor(ti, type_interface)) { ++ assert(ti->instance_size == 0); ++ assert(ti->abstract); ++ assert(!ti->instance_init); ++ assert(!ti->instance_post_init); ++ assert(!ti->instance_finalize); ++ assert(!ti->num_interfaces); ++ } ++ ti->class = g_malloc0(ti->class_size); ++ ++ parent = type_get_parent(ti); ++ if (parent) { ++ type_initialize(parent); ++ GSList *e; ++ int i; ++ ++ g_assert(parent->class_size <= ti->class_size); ++ g_assert(parent->instance_size <= ti->instance_size); ++ memcpy(ti->class, parent->class, parent->class_size); ++ ti->class->interfaces = NULL; ++ ++ for (e = parent->class->interfaces; e; e = e->next) { ++ InterfaceClass *iface = e->data; ++ ObjectClass *klass = OBJECT_CLASS(iface); ++ ++ type_initialize_interface(ti, iface->interface_type, klass->type); ++ } ++ ++ for (i = 0; i < ti->num_interfaces; i++) { ++ TypeImpl *t = type_get_by_name(ti->interfaces[i].typename); ++ if (!t) { ++ error_report("missing interface '%s' for object '%s'", ++ ti->interfaces[i].typename, parent->name); ++ abort(); ++ } ++ for (e = ti->class->interfaces; e; e = e->next) { ++ TypeImpl *target_type = OBJECT_CLASS(e->data)->type; ++ ++ if (type_is_ancestor(target_type, t)) { ++ break; ++ } ++ } ++ ++ if (e) { ++ continue; ++ } ++ ++ type_initialize_interface(ti, t, t); ++ } ++ } ++ ++ ti->class->properties = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, ++ object_property_free); ++ ++ ti->class->type = ti; ++ ++ while (parent) { ++ if (parent->class_base_init) { ++ parent->class_base_init(ti->class, ti->class_data); ++ } ++ parent = type_get_parent(parent); ++ } ++ ++ if (ti->class_init) { ++ ti->class_init(ti->class, ti->class_data); ++ } ++} ++ ++static void object_init_with_type(Object *obj, TypeImpl *ti) ++{ ++ if (type_has_parent(ti)) { ++ object_init_with_type(obj, type_get_parent(ti)); ++ } ++ ++ if (ti->instance_init) { ++ ti->instance_init(obj); ++ } ++} ++ ++static void object_post_init_with_type(Object *obj, TypeImpl *ti) ++{ ++ if (ti->instance_post_init) { ++ ti->instance_post_init(obj); ++ } ++ ++ if (type_has_parent(ti)) { ++ object_post_init_with_type(obj, type_get_parent(ti)); ++ } ++} ++ ++bool object_apply_global_props(Object *obj, const GPtrArray *props, ++ Error **errp) ++{ ++ int i; ++ ++ if (!props) { ++ return true; ++ } ++ ++ for (i = 0; i < props->len; i++) { ++ GlobalProperty *p = g_ptr_array_index(props, i); ++ Error *err = NULL; ++ ++ if (object_dynamic_cast(obj, p->driver) == NULL) { ++ continue; ++ } ++ if (p->optional && !object_property_find(obj, p->property)) { ++ continue; ++ } ++ p->used = true; ++ if (!object_property_parse(obj, p->property, p->value, &err)) { ++ error_prepend(&err, "can't apply global %s.%s=%s: ", ++ p->driver, p->property, p->value); ++ /* ++ * If errp != NULL, propagate error and return. ++ * If errp == NULL, report a warning, but keep going ++ * with the remaining globals. ++ */ ++ if (errp) { ++ error_propagate(errp, err); ++ return false; ++ } else { ++ warn_report_err(err); ++ } ++ } ++ } ++ ++ return true; ++} ++ ++/* ++ * Global property defaults ++ * Slot 0: accelerator's global property defaults ++ * Slot 1: machine's global property defaults ++ * Slot 2: global properties from legacy command line option ++ * Each is a GPtrArray of of GlobalProperty. ++ * Applied in order, later entries override earlier ones. ++ */ ++static GPtrArray *object_compat_props[3]; ++ ++/* ++ * Retrieve @GPtrArray for global property defined with options ++ * other than "-global". These are generally used for syntactic ++ * sugar and legacy command line options. ++ */ ++void object_register_sugar_prop(const char *driver, const char *prop, ++ const char *value, bool optional) ++{ ++ GlobalProperty *g; ++ if (!object_compat_props[2]) { ++ object_compat_props[2] = g_ptr_array_new(); ++ } ++ g = g_new0(GlobalProperty, 1); ++ g->driver = g_strdup(driver); ++ g->property = g_strdup(prop); ++ g->value = g_strdup(value); ++ g->optional = optional; ++ g_ptr_array_add(object_compat_props[2], g); ++} ++ ++/* ++ * Set machine's global property defaults to @compat_props. ++ * May be called at most once. ++ */ ++void object_set_machine_compat_props(GPtrArray *compat_props) ++{ ++ assert(!object_compat_props[1]); ++ object_compat_props[1] = compat_props; ++} ++ ++/* ++ * Set accelerator's global property defaults to @compat_props. ++ * May be called at most once. ++ */ ++void object_set_accelerator_compat_props(GPtrArray *compat_props) ++{ ++ assert(!object_compat_props[0]); ++ object_compat_props[0] = compat_props; ++} ++ ++void object_apply_compat_props(Object *obj) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(object_compat_props); i++) { ++ object_apply_global_props(obj, object_compat_props[i], ++ i == 2 ? &error_fatal : &error_abort); ++ } ++} ++ ++static void object_class_property_init_all(Object *obj) ++{ ++ ObjectPropertyIterator iter; ++ ObjectProperty *prop; ++ ++ object_class_property_iter_init(&iter, object_get_class(obj)); ++ while ((prop = object_property_iter_next(&iter))) { ++ if (prop->init) { ++ prop->init(obj, prop); ++ } ++ } ++} ++ ++static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type) ++{ ++ type_initialize(type); ++ ++ g_assert(type->instance_size >= sizeof(Object)); ++ g_assert(type->abstract == false); ++ g_assert(size >= type->instance_size); ++ ++ memset(obj, 0, type->instance_size); ++ obj->class = type->class; ++ object_ref(obj); ++ object_class_property_init_all(obj); ++ obj->properties = g_hash_table_new_full(g_str_hash, g_str_equal, ++ NULL, object_property_free); ++ object_init_with_type(obj, type); ++ object_post_init_with_type(obj, type); ++} ++ ++void object_initialize(void *data, size_t size, const char *typename) ++{ ++ TypeImpl *type = type_get_by_name(typename); ++ ++#ifdef CONFIG_MODULES ++ if (!type) { ++ int rv = module_load_qom(typename, &error_fatal); ++ if (rv > 0) { ++ type = type_get_by_name(typename); ++ } else { ++ error_report("missing object type '%s'", typename); ++ exit(1); ++ } ++ } ++#endif ++ if (!type) { ++ error_report("missing object type '%s'", typename); ++ abort(); ++ } ++ ++ object_initialize_with_type(data, size, type); ++} ++ ++bool object_initialize_child_with_props(Object *parentobj, ++ const char *propname, ++ void *childobj, size_t size, ++ const char *type, ++ Error **errp, ...) ++{ ++ va_list vargs; ++ bool ok; ++ ++ va_start(vargs, errp); ++ ok = object_initialize_child_with_propsv(parentobj, propname, ++ childobj, size, type, errp, ++ vargs); ++ va_end(vargs); ++ return ok; ++} ++ ++bool object_initialize_child_with_propsv(Object *parentobj, ++ const char *propname, ++ void *childobj, size_t size, ++ const char *type, ++ Error **errp, va_list vargs) ++{ ++ bool ok = false; ++ Object *obj; ++ UserCreatable *uc; ++ ++ object_initialize(childobj, size, type); ++ obj = OBJECT(childobj); ++ ++ if (!object_set_propv(obj, errp, vargs)) { ++ goto out; ++ } ++ ++ object_property_add_child(parentobj, propname, obj); ++ ++ uc = (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE); ++ if (uc) { ++ if (!user_creatable_complete(uc, errp)) { ++ object_unparent(obj); ++ goto out; ++ } ++ } ++ ++ ok = true; ++ ++out: ++ /* ++ * We want @obj's reference to be 1 on success, 0 on failure. ++ * On success, it's 2: one taken by object_initialize(), and one ++ * by object_property_add_child(). ++ * On failure in object_initialize() or earlier, it's 1. ++ * On failure afterwards, it's also 1: object_unparent() releases ++ * the reference taken by object_property_add_child(). ++ */ ++ object_unref(obj); ++ return ok; ++} ++ ++void object_initialize_child_internal(Object *parent, ++ const char *propname, ++ void *child, size_t size, ++ const char *type) ++{ ++ object_initialize_child_with_props(parent, propname, child, size, type, ++ &error_abort, NULL); ++} ++ ++static inline bool object_property_is_child(ObjectProperty *prop) ++{ ++ return strstart(prop->type, "child<", NULL); ++} ++ ++static void object_property_del_all(Object *obj) ++{ ++ g_autoptr(GHashTable) done = g_hash_table_new(NULL, NULL); ++ ObjectProperty *prop; ++ ObjectPropertyIterator iter; ++ bool released; ++ ++ do { ++ released = false; ++ object_property_iter_init(&iter, obj); ++ while ((prop = object_property_iter_next(&iter)) != NULL) { ++ if (g_hash_table_add(done, prop)) { ++ if (prop->release) { ++ prop->release(obj, prop->name, prop->opaque); ++ released = true; ++ break; ++ } ++ } ++ } ++ } while (released); ++ ++ g_hash_table_unref(obj->properties); ++} ++ ++static void object_property_del_child(Object *obj, Object *child) ++{ ++ ObjectProperty *prop; ++ GHashTableIter iter; ++ gpointer key, value; ++ ++ g_hash_table_iter_init(&iter, obj->properties); ++ while (g_hash_table_iter_next(&iter, &key, &value)) { ++ prop = value; ++ if (object_property_is_child(prop) && prop->opaque == child) { ++ if (prop->release) { ++ prop->release(obj, prop->name, prop->opaque); ++ prop->release = NULL; ++ } ++ break; ++ } ++ } ++ g_hash_table_iter_init(&iter, obj->properties); ++ while (g_hash_table_iter_next(&iter, &key, &value)) { ++ prop = value; ++ if (object_property_is_child(prop) && prop->opaque == child) { ++ g_hash_table_iter_remove(&iter); ++ break; ++ } ++ } ++} ++ ++void object_unparent(Object *obj) ++{ ++ if (obj->parent) { ++ object_property_del_child(obj->parent, obj); ++ } ++} ++ ++static void object_deinit(Object *obj, TypeImpl *type) ++{ ++ if (type->instance_finalize) { ++ type->instance_finalize(obj); ++ } ++ ++ if (type_has_parent(type)) { ++ object_deinit(obj, type_get_parent(type)); ++ } ++} ++ ++static void object_finalize(void *data) ++{ ++ Object *obj = data; ++ TypeImpl *ti = obj->class->type; ++ ++ object_property_del_all(obj); ++ object_deinit(obj, ti); ++ ++ g_assert(obj->ref == 0); ++ g_assert(obj->parent == NULL); ++ if (obj->free) { ++ obj->free(obj); ++ } ++} ++ ++/* Find the minimum alignment guaranteed by the system malloc. */ ++#if __STDC_VERSION__ >= 201112L ++typedef max_align_t qemu_max_align_t; ++#else ++typedef union { ++ long l; ++ void *p; ++ double d; ++ long double ld; ++} qemu_max_align_t; ++#endif ++ ++static Object *object_new_with_type(Type type) ++{ ++ Object *obj; ++ size_t size, align; ++ void (*obj_free)(void *); ++ ++ g_assert(type != NULL); ++ type_initialize(type); ++ ++ size = type->instance_size; ++ align = type->instance_align; ++ ++ /* ++ * Do not use qemu_memalign unless required. Depending on the ++ * implementation, extra alignment implies extra overhead. ++ */ ++ if (likely(align <= __alignof__(qemu_max_align_t))) { ++ obj = g_malloc(size); ++ obj_free = g_free; ++ } else { ++ obj = qemu_memalign(align, size); ++ obj_free = qemu_vfree; ++ } ++ ++ object_initialize_with_type(obj, size, type); ++ obj->free = obj_free; ++ ++ return obj; ++} ++ ++Object *object_new_with_class(ObjectClass *klass) ++{ ++ return object_new_with_type(klass->type); ++} ++ ++Object *object_new(const char *typename) ++{ ++ TypeImpl *ti = type_get_by_name(typename); ++ ++ return object_new_with_type(ti); ++} ++ ++ ++Object *object_new_with_props(const char *typename, ++ Object *parent, ++ const char *id, ++ Error **errp, ++ ...) ++{ ++ va_list vargs; ++ Object *obj; ++ ++ va_start(vargs, errp); ++ obj = object_new_with_propv(typename, parent, id, errp, vargs); ++ va_end(vargs); ++ ++ return obj; ++} ++ ++ ++Object *object_new_with_propv(const char *typename, ++ Object *parent, ++ const char *id, ++ Error **errp, ++ va_list vargs) ++{ ++ Object *obj; ++ ObjectClass *klass; ++ UserCreatable *uc; ++ ++ klass = object_class_by_name(typename); ++ if (!klass) { ++ error_setg(errp, "invalid object type: %s", typename); ++ return NULL; ++ } ++ ++ if (object_class_is_abstract(klass)) { ++ error_setg(errp, "object type '%s' is abstract", typename); ++ return NULL; ++ } ++ obj = object_new_with_type(klass->type); ++ ++ if (!object_set_propv(obj, errp, vargs)) { ++ goto error; ++ } ++ ++ if (id != NULL) { ++ object_property_add_child(parent, id, obj); ++ } ++ ++ uc = (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE); ++ if (uc) { ++ if (!user_creatable_complete(uc, errp)) { ++ if (id != NULL) { ++ object_unparent(obj); ++ } ++ goto error; ++ } ++ } ++ ++ object_unref(obj); ++ return obj; ++ ++ error: ++ object_unref(obj); ++ return NULL; ++} ++ ++ ++bool object_set_props(Object *obj, ++ Error **errp, ++ ...) ++{ ++ va_list vargs; ++ bool ret; ++ ++ va_start(vargs, errp); ++ ret = object_set_propv(obj, errp, vargs); ++ va_end(vargs); ++ ++ return ret; ++} ++ ++ ++bool object_set_propv(Object *obj, ++ Error **errp, ++ va_list vargs) ++{ ++ const char *propname; ++ ++ propname = va_arg(vargs, char *); ++ while (propname != NULL) { ++ const char *value = va_arg(vargs, char *); ++ ++ g_assert(value != NULL); ++ if (!object_property_parse(obj, propname, value, errp)) { ++ return false; ++ } ++ propname = va_arg(vargs, char *); ++ } ++ ++ return true; ++} ++ ++ ++Object *object_dynamic_cast(Object *obj, const char *typename) ++{ ++ if (obj && object_class_dynamic_cast(object_get_class(obj), typename)) { ++ return obj; ++ } ++ ++ return NULL; ++} ++ ++Object *object_dynamic_cast_assert(Object *obj, const char *typename, ++ const char *file, int line, const char *func) ++{ ++ trace_object_dynamic_cast_assert(obj ? obj->class->type->name : "(null)", ++ typename, file, line, func); ++ ++#ifdef CONFIG_QOM_CAST_DEBUG ++ int i; ++ Object *inst; ++ ++ for (i = 0; obj && i < OBJECT_CLASS_CAST_CACHE; i++) { ++ if (qatomic_read(&obj->class->object_cast_cache[i]) == typename) { ++ goto out; ++ } ++ } ++ ++ inst = object_dynamic_cast(obj, typename); ++ ++ if (!inst && obj) { ++ fprintf(stderr, "%s:%d:%s: Object %p is not an instance of type %s\n", ++ file, line, func, obj, typename); ++ abort(); ++ } ++ ++ assert(obj == inst); ++ ++ if (obj && obj == inst) { ++ for (i = 1; i < OBJECT_CLASS_CAST_CACHE; i++) { ++ qatomic_set(&obj->class->object_cast_cache[i - 1], ++ qatomic_read(&obj->class->object_cast_cache[i])); ++ } ++ qatomic_set(&obj->class->object_cast_cache[i - 1], typename); ++ } ++ ++out: ++#endif ++ return obj; ++} ++ ++ObjectClass *object_class_dynamic_cast(ObjectClass *class, ++ const char *typename) ++{ ++ ObjectClass *ret = NULL; ++ TypeImpl *target_type; ++ TypeImpl *type; ++ ++ if (!class) { ++ return NULL; ++ } ++ ++ /* A simple fast path that can trigger a lot for leaf classes. */ ++ type = class->type; ++ if (type->name == typename) { ++ return class; ++ } ++ ++ target_type = type_get_by_name(typename); ++ if (!target_type) { ++ /* target class type unknown, so fail the cast */ ++ return NULL; ++ } ++ ++ if (type->class->interfaces && ++ type_is_ancestor(target_type, type_interface)) { ++ int found = 0; ++ GSList *i; ++ ++ for (i = class->interfaces; i; i = i->next) { ++ ObjectClass *target_class = i->data; ++ ++ if (type_is_ancestor(target_class->type, target_type)) { ++ ret = target_class; ++ found++; ++ } ++ } ++ ++ /* The match was ambiguous, don't allow a cast */ ++ if (found > 1) { ++ ret = NULL; ++ } ++ } else if (type_is_ancestor(type, target_type)) { ++ ret = class; ++ } ++ ++ return ret; ++} ++ ++ObjectClass *object_class_dynamic_cast_assert(ObjectClass *class, ++ const char *typename, ++ const char *file, int line, ++ const char *func) ++{ ++ ObjectClass *ret; ++ ++ trace_object_class_dynamic_cast_assert(class ? class->type->name : "(null)", ++ typename, file, line, func); ++ ++#ifdef CONFIG_QOM_CAST_DEBUG ++ int i; ++ ++ for (i = 0; class && i < OBJECT_CLASS_CAST_CACHE; i++) { ++ if (qatomic_read(&class->class_cast_cache[i]) == typename) { ++ ret = class; ++ goto out; ++ } ++ } ++#else ++ if (!class || !class->interfaces) { ++ return class; ++ } ++#endif ++ ++ ret = object_class_dynamic_cast(class, typename); ++ if (!ret && class) { ++ fprintf(stderr, "%s:%d:%s: Object %p is not an instance of type %s\n", ++ file, line, func, class, typename); ++ abort(); ++ } ++ ++#ifdef CONFIG_QOM_CAST_DEBUG ++ if (class && ret == class) { ++ for (i = 1; i < OBJECT_CLASS_CAST_CACHE; i++) { ++ qatomic_set(&class->class_cast_cache[i - 1], ++ qatomic_read(&class->class_cast_cache[i])); ++ } ++ qatomic_set(&class->class_cast_cache[i - 1], typename); ++ } ++out: ++#endif ++ return ret; ++} ++ ++const char *object_get_typename(const Object *obj) ++{ ++ return obj->class->type->name; ++} ++ ++ObjectClass *object_get_class(Object *obj) ++{ ++ return obj->class; ++} ++ ++bool object_class_is_abstract(ObjectClass *klass) ++{ ++ return klass->type->abstract; ++} ++ ++const char *object_class_get_name(ObjectClass *klass) ++{ ++ return klass->type->name; ++} ++ ++ObjectClass *object_class_by_name(const char *typename) ++{ ++ TypeImpl *type = type_get_by_name(typename); ++ ++ if (!type) { ++ return NULL; ++ } ++ ++ type_initialize(type); ++ ++ return type->class; ++} ++ ++ObjectClass *module_object_class_by_name(const char *typename) ++{ ++ ObjectClass *oc; ++ ++ oc = object_class_by_name(typename); ++#ifdef CONFIG_MODULES ++ if (!oc) { ++ Error *local_err = NULL; ++ int rv = module_load_qom(typename, &local_err); ++ if (rv > 0) { ++ oc = object_class_by_name(typename); ++ } else if (rv < 0) { ++ error_report_err(local_err); ++ } ++ } ++#endif ++ return oc; ++} ++ ++ObjectClass *object_class_get_parent(ObjectClass *class) ++{ ++ TypeImpl *type = type_get_parent(class->type); ++ ++ if (!type) { ++ return NULL; ++ } ++ ++ type_initialize(type); ++ ++ return type->class; ++} ++ ++typedef struct OCFData ++{ ++ void (*fn)(ObjectClass *klass, void *opaque); ++ const char *implements_type; ++ bool include_abstract; ++ void *opaque; ++} OCFData; ++ ++static void object_class_foreach_tramp(gpointer key, gpointer value, ++ gpointer opaque) ++{ ++ OCFData *data = opaque; ++ TypeImpl *type = value; ++ ObjectClass *k; ++ ++ type_initialize(type); ++ k = type->class; ++ ++ if (!data->include_abstract && type->abstract) { ++ return; ++ } ++ ++ if (data->implements_type && ++ !object_class_dynamic_cast(k, data->implements_type)) { ++ return; ++ } ++ ++ data->fn(k, data->opaque); ++} ++ ++void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque), ++ const char *implements_type, bool include_abstract, ++ void *opaque) ++{ ++ OCFData data = { fn, implements_type, include_abstract, opaque }; ++ ++ enumerating_types = true; ++ g_hash_table_foreach(type_table_get(), object_class_foreach_tramp, &data); ++ enumerating_types = false; ++} ++ ++static int do_object_child_foreach(Object *obj, ++ int (*fn)(Object *child, void *opaque), ++ void *opaque, bool recurse) ++{ ++ GHashTableIter iter; ++ ObjectProperty *prop; ++ int ret = 0; ++ ++ g_hash_table_iter_init(&iter, obj->properties); ++ while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { ++ if (object_property_is_child(prop)) { ++ Object *child = prop->opaque; ++ ++ ret = fn(child, opaque); ++ if (ret != 0) { ++ break; ++ } ++ if (recurse) { ++ ret = do_object_child_foreach(child, fn, opaque, true); ++ if (ret != 0) { ++ break; ++ } ++ } ++ } ++ } ++ return ret; ++} ++ ++int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque), ++ void *opaque) ++{ ++ return do_object_child_foreach(obj, fn, opaque, false); ++} ++ ++int object_child_foreach_recursive(Object *obj, ++ int (*fn)(Object *child, void *opaque), ++ void *opaque) ++{ ++ return do_object_child_foreach(obj, fn, opaque, true); ++} ++ ++static void object_class_get_list_tramp(ObjectClass *klass, void *opaque) ++{ ++ GSList **list = opaque; ++ ++ *list = g_slist_prepend(*list, klass); ++} ++ ++GSList *object_class_get_list(const char *implements_type, ++ bool include_abstract) ++{ ++ GSList *list = NULL; ++ ++ object_class_foreach(object_class_get_list_tramp, ++ implements_type, include_abstract, &list); ++ return list; ++} ++ ++static gint object_class_cmp(gconstpointer a, gconstpointer b) ++{ ++ return strcasecmp(object_class_get_name((ObjectClass *)a), ++ object_class_get_name((ObjectClass *)b)); ++} ++ ++GSList *object_class_get_list_sorted(const char *implements_type, ++ bool include_abstract) ++{ ++ return g_slist_sort(object_class_get_list(implements_type, include_abstract), ++ object_class_cmp); ++} ++ ++Object *object_ref(void *objptr) ++{ ++ Object *obj = OBJECT(objptr); ++ uint32_t ref; ++ ++ if (!obj) { ++ return NULL; ++ } ++ ref = qatomic_fetch_inc(&obj->ref); ++ /* Assert waaay before the integer overflows */ ++ g_assert(ref < INT_MAX); ++ return obj; ++} ++ ++void object_unref(void *objptr) ++{ ++ Object *obj = OBJECT(objptr); ++ if (!obj) { ++ return; ++ } ++ g_assert(obj->ref > 0); ++ ++ /* parent always holds a reference to its children */ ++ if (qatomic_fetch_dec(&obj->ref) == 1) { ++ object_finalize(obj); ++ } ++} ++ ++ObjectProperty * ++object_property_try_add(Object *obj, const char *name, const char *type, ++ ObjectPropertyAccessor *get, ++ ObjectPropertyAccessor *set, ++ ObjectPropertyRelease *release, ++ void *opaque, Error **errp) ++{ ++ ObjectProperty *prop; ++ size_t name_len = strlen(name); ++ ++ if (name_len >= 3 && !memcmp(name + name_len - 3, "[*]", 4)) { ++ int i; ++ ObjectProperty *ret = NULL; ++ char *name_no_array = g_strdup(name); ++ ++ name_no_array[name_len - 3] = '\0'; ++ for (i = 0; i < INT16_MAX; ++i) { ++ char *full_name = g_strdup_printf("%s[%d]", name_no_array, i); ++ ++ ret = object_property_try_add(obj, full_name, type, get, set, ++ release, opaque, NULL); ++ g_free(full_name); ++ if (ret) { ++ break; ++ } ++ } ++ g_free(name_no_array); ++ assert(ret); ++ return ret; ++ } ++ ++ if (object_property_find(obj, name) != NULL) { ++ error_setg(errp, "attempt to add duplicate property '%s' to object (type '%s')", ++ name, object_get_typename(obj)); ++ return NULL; ++ } ++ ++ prop = g_malloc0(sizeof(*prop)); ++ ++ prop->name = g_strdup(name); ++ prop->type = g_strdup(type); ++ ++ prop->get = get; ++ prop->set = set; ++ prop->release = release; ++ prop->opaque = opaque; ++ ++ g_hash_table_insert(obj->properties, prop->name, prop); ++ return prop; ++} ++ ++ObjectProperty * ++object_property_add(Object *obj, const char *name, const char *type, ++ ObjectPropertyAccessor *get, ++ ObjectPropertyAccessor *set, ++ ObjectPropertyRelease *release, ++ void *opaque) ++{ ++ return object_property_try_add(obj, name, type, get, set, release, ++ opaque, &error_abort); ++} ++ ++ObjectProperty * ++object_class_property_add(ObjectClass *klass, ++ const char *name, ++ const char *type, ++ ObjectPropertyAccessor *get, ++ ObjectPropertyAccessor *set, ++ ObjectPropertyRelease *release, ++ void *opaque) ++{ ++ ObjectProperty *prop; ++ ++ assert(!object_class_property_find(klass, name)); ++ ++ prop = g_malloc0(sizeof(*prop)); ++ ++ prop->name = g_strdup(name); ++ prop->type = g_strdup(type); ++ ++ prop->get = get; ++ prop->set = set; ++ prop->release = release; ++ prop->opaque = opaque; ++ ++ g_hash_table_insert(klass->properties, prop->name, prop); ++ ++ return prop; ++} ++ ++ObjectProperty *object_property_find(Object *obj, const char *name) ++{ ++ ObjectProperty *prop; ++ ObjectClass *klass = object_get_class(obj); ++ ++ prop = object_class_property_find(klass, name); ++ if (prop) { ++ return prop; ++ } ++ ++ return g_hash_table_lookup(obj->properties, name); ++} ++ ++ObjectProperty *object_property_find_err(Object *obj, const char *name, ++ Error **errp) ++{ ++ ObjectProperty *prop = object_property_find(obj, name); ++ if (!prop) { ++ error_setg(errp, "Property '%s.%s' not found", ++ object_get_typename(obj), name); ++ } ++ return prop; ++} ++ ++void object_property_iter_init(ObjectPropertyIterator *iter, ++ Object *obj) ++{ ++ g_hash_table_iter_init(&iter->iter, obj->properties); ++ iter->nextclass = object_get_class(obj); ++} ++ ++ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter) ++{ ++ gpointer key, val; ++ while (!g_hash_table_iter_next(&iter->iter, &key, &val)) { ++ if (!iter->nextclass) { ++ return NULL; ++ } ++ g_hash_table_iter_init(&iter->iter, iter->nextclass->properties); ++ iter->nextclass = object_class_get_parent(iter->nextclass); ++ } ++ return val; ++} ++ ++void object_class_property_iter_init(ObjectPropertyIterator *iter, ++ ObjectClass *klass) ++{ ++ g_hash_table_iter_init(&iter->iter, klass->properties); ++ iter->nextclass = object_class_get_parent(klass); ++} ++ ++ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name) ++{ ++ ObjectClass *parent_klass; ++ ++ parent_klass = object_class_get_parent(klass); ++ if (parent_klass) { ++ ObjectProperty *prop = ++ object_class_property_find(parent_klass, name); ++ if (prop) { ++ return prop; ++ } ++ } ++ ++ return g_hash_table_lookup(klass->properties, name); ++} ++ ++ObjectProperty *object_class_property_find_err(ObjectClass *klass, ++ const char *name, ++ Error **errp) ++{ ++ ObjectProperty *prop = object_class_property_find(klass, name); ++ if (!prop) { ++ error_setg(errp, "Property '.%s' not found", name); ++ } ++ return prop; ++} ++ ++ ++void object_property_del(Object *obj, const char *name) ++{ ++ ObjectProperty *prop = g_hash_table_lookup(obj->properties, name); ++ ++ if (prop->release) { ++ prop->release(obj, name, prop->opaque); ++ } ++ g_hash_table_remove(obj->properties, name); ++} ++ ++bool object_property_get(Object *obj, const char *name, Visitor *v, ++ Error **errp) ++{ ++ Error *err = NULL; ++ ObjectProperty *prop = object_property_find_err(obj, name, errp); ++ ++ if (prop == NULL) { ++ return false; ++ } ++ ++ if (!prop->get) { ++ error_setg(errp, "Property '%s.%s' is not readable", ++ object_get_typename(obj), name); ++ return false; ++ } ++ prop->get(obj, v, name, prop->opaque, &err); ++ error_propagate(errp, err); ++ return !err; ++} ++ ++bool object_property_set(Object *obj, const char *name, Visitor *v, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ ObjectProperty *prop = object_property_find_err(obj, name, errp); ++ ++ if (prop == NULL) { ++ return false; ++ } ++ ++ if (!prop->set) { ++ error_setg(errp, "Property '%s.%s' is not writable", ++ object_get_typename(obj), name); ++ return false; ++ } ++ prop->set(obj, v, name, prop->opaque, errp); ++ return !*errp; ++} ++ ++bool object_property_set_str(Object *obj, const char *name, ++ const char *value, Error **errp) ++{ ++ QString *qstr = qstring_from_str(value); ++ bool ok = object_property_set_qobject(obj, name, QOBJECT(qstr), errp); ++ ++ qobject_unref(qstr); ++ return ok; ++} ++ ++char *object_property_get_str(Object *obj, const char *name, ++ Error **errp) ++{ ++ QObject *ret = object_property_get_qobject(obj, name, errp); ++ QString *qstring; ++ char *retval; ++ ++ if (!ret) { ++ return NULL; ++ } ++ qstring = qobject_to(QString, ret); ++ if (!qstring) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: string", ++ name); ++ retval = NULL; ++ } else { ++ retval = g_strdup(qstring_get_str(qstring)); ++ } ++ ++ qobject_unref(ret); ++ return retval; ++} ++ ++bool object_property_set_link(Object *obj, const char *name, ++ Object *value, Error **errp) ++{ ++ g_autofree char *path = NULL; ++ ++ if (value) { ++ path = object_get_canonical_path(value); ++ } ++ return object_property_set_str(obj, name, path ?: "", errp); ++} ++ ++Object *object_property_get_link(Object *obj, const char *name, ++ Error **errp) ++{ ++ char *str = object_property_get_str(obj, name, errp); ++ Object *target = NULL; ++ ++ if (str && *str) { ++ target = object_resolve_path(str, NULL); ++ if (!target) { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "Device '%s' not found", str); ++ } ++ } ++ ++ g_free(str); ++ return target; ++} ++ ++bool object_property_set_bool(Object *obj, const char *name, ++ bool value, Error **errp) ++{ ++ QBool *qbool = qbool_from_bool(value); ++ bool ok = object_property_set_qobject(obj, name, QOBJECT(qbool), errp); ++ ++ qobject_unref(qbool); ++ return ok; ++} ++ ++bool object_property_get_bool(Object *obj, const char *name, ++ Error **errp) ++{ ++ QObject *ret = object_property_get_qobject(obj, name, errp); ++ QBool *qbool; ++ bool retval; ++ ++ if (!ret) { ++ return false; ++ } ++ qbool = qobject_to(QBool, ret); ++ if (!qbool) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: boolean", ++ name); ++ retval = false; ++ } else { ++ retval = qbool_get_bool(qbool); ++ } ++ ++ qobject_unref(ret); ++ return retval; ++} ++ ++bool object_property_set_int(Object *obj, const char *name, ++ int64_t value, Error **errp) ++{ ++ QNum *qnum = qnum_from_int(value); ++ bool ok = object_property_set_qobject(obj, name, QOBJECT(qnum), errp); ++ ++ qobject_unref(qnum); ++ return ok; ++} ++ ++int64_t object_property_get_int(Object *obj, const char *name, ++ Error **errp) ++{ ++ QObject *ret = object_property_get_qobject(obj, name, errp); ++ QNum *qnum; ++ int64_t retval; ++ ++ if (!ret) { ++ return -1; ++ } ++ ++ qnum = qobject_to(QNum, ret); ++ if (!qnum || !qnum_get_try_int(qnum, &retval)) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: int", ++ name); ++ retval = -1; ++ } ++ ++ qobject_unref(ret); ++ return retval; ++} ++ ++static void object_property_init_defval(Object *obj, ObjectProperty *prop) ++{ ++ Visitor *v = qobject_input_visitor_new(prop->defval); ++ ++ assert(prop->set != NULL); ++ prop->set(obj, v, prop->name, prop->opaque, &error_abort); ++ ++ visit_free(v); ++} ++ ++static void object_property_set_default(ObjectProperty *prop, QObject *defval) ++{ ++ assert(!prop->defval); ++ assert(!prop->init); ++ ++ prop->defval = defval; ++ prop->init = object_property_init_defval; ++} ++ ++void object_property_set_default_bool(ObjectProperty *prop, bool value) ++{ ++ object_property_set_default(prop, QOBJECT(qbool_from_bool(value))); ++} ++ ++void object_property_set_default_str(ObjectProperty *prop, const char *value) ++{ ++ object_property_set_default(prop, QOBJECT(qstring_from_str(value))); ++} ++ ++void object_property_set_default_list(ObjectProperty *prop) ++{ ++ object_property_set_default(prop, QOBJECT(qlist_new())); ++} ++ ++void object_property_set_default_int(ObjectProperty *prop, int64_t value) ++{ ++ object_property_set_default(prop, QOBJECT(qnum_from_int(value))); ++} ++ ++void object_property_set_default_uint(ObjectProperty *prop, uint64_t value) ++{ ++ object_property_set_default(prop, QOBJECT(qnum_from_uint(value))); ++} ++ ++bool object_property_set_uint(Object *obj, const char *name, ++ uint64_t value, Error **errp) ++{ ++ QNum *qnum = qnum_from_uint(value); ++ bool ok = object_property_set_qobject(obj, name, QOBJECT(qnum), errp); ++ ++ qobject_unref(qnum); ++ return ok; ++} ++ ++uint64_t object_property_get_uint(Object *obj, const char *name, ++ Error **errp) ++{ ++ QObject *ret = object_property_get_qobject(obj, name, errp); ++ QNum *qnum; ++ uint64_t retval; ++ ++ if (!ret) { ++ return 0; ++ } ++ qnum = qobject_to(QNum, ret); ++ if (!qnum || !qnum_get_try_uint(qnum, &retval)) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: uint", ++ name); ++ retval = 0; ++ } ++ ++ qobject_unref(ret); ++ return retval; ++} ++ ++typedef struct EnumProperty { ++ const QEnumLookup *lookup; ++ int (*get)(Object *, Error **); ++ void (*set)(Object *, int, Error **); ++} EnumProperty; ++ ++int object_property_get_enum(Object *obj, const char *name, ++ const char *typename, Error **errp) ++{ ++ char *str; ++ int ret; ++ ObjectProperty *prop = object_property_find_err(obj, name, errp); ++ EnumProperty *enumprop; ++ ++ if (prop == NULL) { ++ return -1; ++ } ++ ++ if (!g_str_equal(prop->type, typename)) { ++ error_setg(errp, "Property %s on %s is not '%s' enum type", ++ name, object_class_get_name( ++ object_get_class(obj)), typename); ++ return -1; ++ } ++ ++ enumprop = prop->opaque; ++ ++ str = object_property_get_str(obj, name, errp); ++ if (!str) { ++ return -1; ++ } ++ ++ ret = qapi_enum_parse(enumprop->lookup, str, -1, errp); ++ g_free(str); ++ ++ return ret; ++} ++ ++bool object_property_parse(Object *obj, const char *name, ++ const char *string, Error **errp) ++{ ++ Visitor *v = string_input_visitor_new(string); ++ bool ok = object_property_set(obj, name, v, errp); ++ ++ visit_free(v); ++ return ok; ++} ++ ++char *object_property_print(Object *obj, const char *name, bool human, ++ Error **errp) ++{ ++ Visitor *v; ++ char *string = NULL; ++ ++ v = string_output_visitor_new(human, &string); ++ if (!object_property_get(obj, name, v, errp)) { ++ goto out; ++ } ++ ++ visit_complete(v, &string); ++ ++out: ++ visit_free(v); ++ return string; ++} ++ ++const char *object_property_get_type(Object *obj, const char *name, Error **errp) ++{ ++ ObjectProperty *prop = object_property_find_err(obj, name, errp); ++ if (prop == NULL) { ++ return NULL; ++ } ++ ++ return prop->type; ++} ++ ++Object *object_get_root(void) ++{ ++ static Object *root; ++ ++ if (!root) { ++ root = object_new("container"); ++ } ++ ++ return root; ++} ++ ++Object *object_get_objects_root(void) ++{ ++ return container_get(object_get_root(), "/objects"); ++} ++ ++Object *object_get_internal_root(void) ++{ ++ static Object *internal_root; ++ ++ if (!internal_root) { ++ internal_root = object_new("container"); ++ } ++ ++ return internal_root; ++} ++ ++static void object_get_child_property(Object *obj, Visitor *v, ++ const char *name, void *opaque, ++ Error **errp) ++{ ++ Object *child = opaque; ++ char *path; ++ ++ path = object_get_canonical_path(child); ++ visit_type_str(v, name, &path, errp); ++ g_free(path); ++} ++ ++static Object *object_resolve_child_property(Object *parent, void *opaque, ++ const char *part) ++{ ++ return opaque; ++} ++ ++static void object_finalize_child_property(Object *obj, const char *name, ++ void *opaque) ++{ ++ Object *child = opaque; ++ ++ if (child->class->unparent) { ++ (child->class->unparent)(child); ++ } ++ child->parent = NULL; ++ object_unref(child); ++} ++ ++ObjectProperty * ++object_property_try_add_child(Object *obj, const char *name, ++ Object *child, Error **errp) ++{ ++ g_autofree char *type = NULL; ++ ObjectProperty *op; ++ ++ assert(!child->parent); ++ ++ type = g_strdup_printf("child<%s>", object_get_typename(child)); ++ ++ op = object_property_try_add(obj, name, type, object_get_child_property, ++ NULL, object_finalize_child_property, ++ child, errp); ++ if (!op) { ++ return NULL; ++ } ++ op->resolve = object_resolve_child_property; ++ object_ref(child); ++ child->parent = obj; ++ return op; ++} ++ ++ObjectProperty * ++object_property_add_child(Object *obj, const char *name, ++ Object *child) ++{ ++ return object_property_try_add_child(obj, name, child, &error_abort); ++} ++ ++void object_property_allow_set_link(const Object *obj, const char *name, ++ Object *val, Error **errp) ++{ ++ /* Allow the link to be set, always */ ++} ++ ++typedef struct { ++ union { ++ Object **targetp; ++ Object *target; /* if OBJ_PROP_LINK_DIRECT, when holding the pointer */ ++ ptrdiff_t offset; /* if OBJ_PROP_LINK_CLASS */ ++ }; ++ void (*check)(const Object *, const char *, Object *, Error **); ++ ObjectPropertyLinkFlags flags; ++} LinkProperty; ++ ++static Object ** ++object_link_get_targetp(Object *obj, LinkProperty *lprop) ++{ ++ if (lprop->flags & OBJ_PROP_LINK_DIRECT) { ++ return &lprop->target; ++ } else if (lprop->flags & OBJ_PROP_LINK_CLASS) { ++ return (void *)obj + lprop->offset; ++ } else { ++ return lprop->targetp; ++ } ++} ++ ++static void object_get_link_property(Object *obj, Visitor *v, ++ const char *name, void *opaque, ++ Error **errp) ++{ ++ LinkProperty *lprop = opaque; ++ Object **targetp = object_link_get_targetp(obj, lprop); ++ char *path; ++ ++ if (*targetp) { ++ path = object_get_canonical_path(*targetp); ++ visit_type_str(v, name, &path, errp); ++ g_free(path); ++ } else { ++ path = (char *)""; ++ visit_type_str(v, name, &path, errp); ++ } ++} ++ ++/* ++ * object_resolve_link: ++ * ++ * Lookup an object and ensure its type matches the link property type. This ++ * is similar to object_resolve_path() except type verification against the ++ * link property is performed. ++ * ++ * Returns: The matched object or NULL on path lookup failures. ++ */ ++static Object *object_resolve_link(Object *obj, const char *name, ++ const char *path, Error **errp) ++{ ++ const char *type; ++ char *target_type; ++ bool ambiguous = false; ++ Object *target; ++ ++ /* Go from link to FOO. */ ++ type = object_property_get_type(obj, name, NULL); ++ target_type = g_strndup(&type[5], strlen(type) - 6); ++ target = object_resolve_path_type(path, target_type, &ambiguous); ++ ++ if (ambiguous) { ++ error_setg(errp, "Path '%s' does not uniquely identify an object", ++ path); ++ } else if (!target) { ++ target = object_resolve_path(path, &ambiguous); ++ if (target || ambiguous) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: %s", ++ name, target_type); ++ } else { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "Device '%s' not found", path); ++ } ++ target = NULL; ++ } ++ g_free(target_type); ++ ++ return target; ++} ++ ++static void object_set_link_property(Object *obj, Visitor *v, ++ const char *name, void *opaque, ++ Error **errp) ++{ ++ Error *local_err = NULL; ++ LinkProperty *prop = opaque; ++ Object **targetp = object_link_get_targetp(obj, prop); ++ Object *old_target = *targetp; ++ Object *new_target; ++ char *path = NULL; ++ ++ if (!visit_type_str(v, name, &path, errp)) { ++ return; ++ } ++ ++ if (*path) { ++ new_target = object_resolve_link(obj, name, path, errp); ++ if (!new_target) { ++ g_free(path); ++ return; ++ } ++ } else { ++ new_target = NULL; ++ } ++ ++ g_free(path); ++ ++ prop->check(obj, name, new_target, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ *targetp = new_target; ++ if (prop->flags & OBJ_PROP_LINK_STRONG) { ++ object_ref(new_target); ++ object_unref(old_target); ++ } ++} ++ ++static Object *object_resolve_link_property(Object *parent, void *opaque, ++ const char *part) ++{ ++ LinkProperty *lprop = opaque; ++ ++ return *object_link_get_targetp(parent, lprop); ++} ++ ++static void object_release_link_property(Object *obj, const char *name, ++ void *opaque) ++{ ++ LinkProperty *prop = opaque; ++ Object **targetp = object_link_get_targetp(obj, prop); ++ ++ if ((prop->flags & OBJ_PROP_LINK_STRONG) && *targetp) { ++ object_unref(*targetp); ++ } ++ if (!(prop->flags & OBJ_PROP_LINK_CLASS)) { ++ g_free(prop); ++ } ++} ++ ++static ObjectProperty * ++object_add_link_prop(Object *obj, const char *name, ++ const char *type, void *ptr, ++ void (*check)(const Object *, const char *, ++ Object *, Error **), ++ ObjectPropertyLinkFlags flags) ++{ ++ LinkProperty *prop = g_malloc(sizeof(*prop)); ++ g_autofree char *full_type = NULL; ++ ObjectProperty *op; ++ ++ if (flags & OBJ_PROP_LINK_DIRECT) { ++ prop->target = ptr; ++ } else { ++ prop->targetp = ptr; ++ } ++ prop->check = check; ++ prop->flags = flags; ++ ++ full_type = g_strdup_printf("link<%s>", type); ++ ++ op = object_property_add(obj, name, full_type, ++ object_get_link_property, ++ check ? object_set_link_property : NULL, ++ object_release_link_property, ++ prop); ++ op->resolve = object_resolve_link_property; ++ return op; ++} ++ ++ObjectProperty * ++object_property_add_link(Object *obj, const char *name, ++ const char *type, Object **targetp, ++ void (*check)(const Object *, const char *, ++ Object *, Error **), ++ ObjectPropertyLinkFlags flags) ++{ ++ return object_add_link_prop(obj, name, type, targetp, check, flags); ++} ++ ++ObjectProperty * ++object_class_property_add_link(ObjectClass *oc, ++ const char *name, ++ const char *type, ptrdiff_t offset, ++ void (*check)(const Object *obj, const char *name, ++ Object *val, Error **errp), ++ ObjectPropertyLinkFlags flags) ++{ ++ LinkProperty *prop = g_new0(LinkProperty, 1); ++ char *full_type; ++ ObjectProperty *op; ++ ++ prop->offset = offset; ++ prop->check = check; ++ prop->flags = flags | OBJ_PROP_LINK_CLASS; ++ ++ full_type = g_strdup_printf("link<%s>", type); ++ ++ op = object_class_property_add(oc, name, full_type, ++ object_get_link_property, ++ check ? object_set_link_property : NULL, ++ object_release_link_property, ++ prop); ++ ++ op->resolve = object_resolve_link_property; ++ ++ g_free(full_type); ++ return op; ++} ++ ++ObjectProperty * ++object_property_add_const_link(Object *obj, const char *name, ++ Object *target) ++{ ++ return object_add_link_prop(obj, name, ++ object_get_typename(target), target, ++ NULL, OBJ_PROP_LINK_DIRECT); ++} ++ ++const char *object_get_canonical_path_component(const Object *obj) ++{ ++ ObjectProperty *prop = NULL; ++ GHashTableIter iter; ++ ++ if (obj->parent == NULL) { ++ return NULL; ++ } ++ ++ g_hash_table_iter_init(&iter, obj->parent->properties); ++ while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { ++ if (!object_property_is_child(prop)) { ++ continue; ++ } ++ ++ if (prop->opaque == obj) { ++ return prop->name; ++ } ++ } ++ ++ /* obj had a parent but was not a child, should never happen */ ++ g_assert_not_reached(); ++ return NULL; ++} ++ ++char *object_get_canonical_path(const Object *obj) ++{ ++ Object *root = object_get_root(); ++ char *newpath, *path = NULL; ++ ++ if (obj == root) { ++ return g_strdup("/"); ++ } ++ ++ do { ++ const char *component = object_get_canonical_path_component(obj); ++ ++ if (!component) { ++ /* A canonical path must be complete, so discard what was ++ * collected so far. ++ */ ++ g_free(path); ++ return NULL; ++ } ++ ++ newpath = g_strdup_printf("/%s%s", component, path ? path : ""); ++ g_free(path); ++ path = newpath; ++ obj = obj->parent; ++ } while (obj != root); ++ ++ return path; ++} ++ ++Object *object_resolve_path_component(Object *parent, const char *part) ++{ ++ ObjectProperty *prop = object_property_find(parent, part); ++ if (prop == NULL) { ++ return NULL; ++ } ++ ++ if (prop->resolve) { ++ return prop->resolve(parent, prop->opaque, part); ++ } else { ++ return NULL; ++ } ++} ++ ++static Object *object_resolve_abs_path(Object *parent, ++ char **parts, ++ const char *typename) ++{ ++ Object *child; ++ ++ if (*parts == NULL) { ++ return object_dynamic_cast(parent, typename); ++ } ++ ++ if (strcmp(*parts, "") == 0) { ++ return object_resolve_abs_path(parent, parts + 1, typename); ++ } ++ ++ child = object_resolve_path_component(parent, *parts); ++ if (!child) { ++ return NULL; ++ } ++ ++ return object_resolve_abs_path(child, parts + 1, typename); ++} ++ ++static Object *object_resolve_partial_path(Object *parent, ++ char **parts, ++ const char *typename, ++ bool *ambiguous) ++{ ++ Object *obj; ++ GHashTableIter iter; ++ ObjectProperty *prop; ++ ++ obj = object_resolve_abs_path(parent, parts, typename); ++ ++ g_hash_table_iter_init(&iter, parent->properties); ++ while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { ++ Object *found; ++ ++ if (!object_property_is_child(prop)) { ++ continue; ++ } ++ ++ found = object_resolve_partial_path(prop->opaque, parts, ++ typename, ambiguous); ++ if (found) { ++ if (obj) { ++ *ambiguous = true; ++ return NULL; ++ } ++ obj = found; ++ } ++ ++ if (*ambiguous) { ++ return NULL; ++ } ++ } ++ ++ return obj; ++} ++ ++Object *object_resolve_path_type(const char *path, const char *typename, ++ bool *ambiguousp) ++{ ++ Object *obj; ++ char **parts; ++ ++ parts = g_strsplit(path, "/", 0); ++ assert(parts); ++ ++ if (parts[0] == NULL || strcmp(parts[0], "") != 0) { ++ bool ambiguous = false; ++ obj = object_resolve_partial_path(object_get_root(), parts, ++ typename, &ambiguous); ++ if (ambiguousp) { ++ *ambiguousp = ambiguous; ++ } ++ } else { ++ obj = object_resolve_abs_path(object_get_root(), parts + 1, typename); ++ } ++ ++ g_strfreev(parts); ++ ++ return obj; ++} ++ ++Object *object_resolve_path(const char *path, bool *ambiguous) ++{ ++ return object_resolve_path_type(path, TYPE_OBJECT, ambiguous); ++} ++ ++Object *object_resolve_path_at(Object *parent, const char *path) ++{ ++ g_auto(GStrv) parts = g_strsplit(path, "/", 0); ++ ++ if (*path == '/') { ++ return object_resolve_abs_path(object_get_root(), parts + 1, ++ TYPE_OBJECT); ++ } ++ return object_resolve_abs_path(parent, parts, TYPE_OBJECT); ++} ++ ++Object *object_resolve_type_unambiguous(const char *typename, Error **errp) ++{ ++ bool ambig; ++ Object *o = object_resolve_path_type("", typename, &ambig); ++ ++ if (ambig) { ++ error_setg(errp, "More than one object of type %s", typename); ++ return NULL; ++ } ++ if (!o) { ++ error_setg(errp, "No object found of type %s", typename); ++ return NULL; ++ } ++ return o; ++} ++ ++typedef struct StringProperty ++{ ++ char *(*get)(Object *, Error **); ++ void (*set)(Object *, const char *, Error **); ++} StringProperty; ++ ++static void property_get_str(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ StringProperty *prop = opaque; ++ char *value; ++ Error *err = NULL; ++ ++ value = prop->get(obj, &err); ++ if (err) { ++ error_propagate(errp, err); ++ return; ++ } ++ ++ visit_type_str(v, name, &value, errp); ++ g_free(value); ++} ++ ++static void property_set_str(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ StringProperty *prop = opaque; ++ char *value; ++ ++ if (!visit_type_str(v, name, &value, errp)) { ++ return; ++ } ++ ++ prop->set(obj, value, errp); ++ g_free(value); ++} ++ ++static void property_release_data(Object *obj, const char *name, ++ void *opaque) ++{ ++ g_free(opaque); ++} ++ ++ObjectProperty * ++object_property_add_str(Object *obj, const char *name, ++ char *(*get)(Object *, Error **), ++ void (*set)(Object *, const char *, Error **)) ++{ ++ StringProperty *prop = g_malloc0(sizeof(*prop)); ++ ++ prop->get = get; ++ prop->set = set; ++ ++ return object_property_add(obj, name, "string", ++ get ? property_get_str : NULL, ++ set ? property_set_str : NULL, ++ property_release_data, ++ prop); ++} ++ ++ObjectProperty * ++object_class_property_add_str(ObjectClass *klass, const char *name, ++ char *(*get)(Object *, Error **), ++ void (*set)(Object *, const char *, ++ Error **)) ++{ ++ StringProperty *prop = g_malloc0(sizeof(*prop)); ++ ++ prop->get = get; ++ prop->set = set; ++ ++ return object_class_property_add(klass, name, "string", ++ get ? property_get_str : NULL, ++ set ? property_set_str : NULL, ++ NULL, ++ prop); ++} ++ ++typedef struct BoolProperty ++{ ++ bool (*get)(Object *, Error **); ++ void (*set)(Object *, bool, Error **); ++} BoolProperty; ++ ++static void property_get_bool(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ BoolProperty *prop = opaque; ++ bool value; ++ Error *err = NULL; ++ ++ value = prop->get(obj, &err); ++ if (err) { ++ error_propagate(errp, err); ++ return; ++ } ++ ++ visit_type_bool(v, name, &value, errp); ++} ++ ++static void property_set_bool(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ BoolProperty *prop = opaque; ++ bool value; ++ ++ if (!visit_type_bool(v, name, &value, errp)) { ++ return; ++ } ++ ++ prop->set(obj, value, errp); ++} ++ ++ObjectProperty * ++object_property_add_bool(Object *obj, const char *name, ++ bool (*get)(Object *, Error **), ++ void (*set)(Object *, bool, Error **)) ++{ ++ BoolProperty *prop = g_malloc0(sizeof(*prop)); ++ ++ prop->get = get; ++ prop->set = set; ++ ++ return object_property_add(obj, name, "bool", ++ get ? property_get_bool : NULL, ++ set ? property_set_bool : NULL, ++ property_release_data, ++ prop); ++} ++ ++ObjectProperty * ++object_class_property_add_bool(ObjectClass *klass, const char *name, ++ bool (*get)(Object *, Error **), ++ void (*set)(Object *, bool, Error **)) ++{ ++ BoolProperty *prop = g_malloc0(sizeof(*prop)); ++ ++ prop->get = get; ++ prop->set = set; ++ ++ return object_class_property_add(klass, name, "bool", ++ get ? property_get_bool : NULL, ++ set ? property_set_bool : NULL, ++ NULL, ++ prop); ++} ++ ++static void property_get_enum(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ EnumProperty *prop = opaque; ++ int value; ++ Error *err = NULL; ++ ++ value = prop->get(obj, &err); ++ if (err) { ++ error_propagate(errp, err); ++ return; ++ } ++ ++ visit_type_enum(v, name, &value, prop->lookup, errp); ++} ++ ++static void property_set_enum(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ EnumProperty *prop = opaque; ++ int value; ++ ++ if (!visit_type_enum(v, name, &value, prop->lookup, errp)) { ++ return; ++ } ++ prop->set(obj, value, errp); ++} ++ ++ObjectProperty * ++object_property_add_enum(Object *obj, const char *name, ++ const char *typename, ++ const QEnumLookup *lookup, ++ int (*get)(Object *, Error **), ++ void (*set)(Object *, int, Error **)) ++{ ++ EnumProperty *prop = g_malloc(sizeof(*prop)); ++ ++ prop->lookup = lookup; ++ prop->get = get; ++ prop->set = set; ++ ++ return object_property_add(obj, name, typename, ++ get ? property_get_enum : NULL, ++ set ? property_set_enum : NULL, ++ property_release_data, ++ prop); ++} ++ ++ObjectProperty * ++object_class_property_add_enum(ObjectClass *klass, const char *name, ++ const char *typename, ++ const QEnumLookup *lookup, ++ int (*get)(Object *, Error **), ++ void (*set)(Object *, int, Error **)) ++{ ++ EnumProperty *prop = g_malloc(sizeof(*prop)); ++ ++ prop->lookup = lookup; ++ prop->get = get; ++ prop->set = set; ++ ++ return object_class_property_add(klass, name, typename, ++ get ? property_get_enum : NULL, ++ set ? property_set_enum : NULL, ++ NULL, ++ prop); ++} ++ ++typedef struct TMProperty { ++ void (*get)(Object *, struct tm *, Error **); ++} TMProperty; ++ ++static void property_get_tm(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ TMProperty *prop = opaque; ++ Error *err = NULL; ++ struct tm value; ++ ++ prop->get(obj, &value, &err); ++ if (err) { ++ error_propagate(errp, err); ++ return; ++ } ++ ++ if (!visit_start_struct(v, name, NULL, 0, errp)) { ++ return; ++ } ++ if (!visit_type_int32(v, "tm_year", &value.tm_year, errp)) { ++ goto out_end; ++ } ++ if (!visit_type_int32(v, "tm_mon", &value.tm_mon, errp)) { ++ goto out_end; ++ } ++ if (!visit_type_int32(v, "tm_mday", &value.tm_mday, errp)) { ++ goto out_end; ++ } ++ if (!visit_type_int32(v, "tm_hour", &value.tm_hour, errp)) { ++ goto out_end; ++ } ++ if (!visit_type_int32(v, "tm_min", &value.tm_min, errp)) { ++ goto out_end; ++ } ++ if (!visit_type_int32(v, "tm_sec", &value.tm_sec, errp)) { ++ goto out_end; ++ } ++ visit_check_struct(v, errp); ++out_end: ++ visit_end_struct(v, NULL); ++} ++ ++ObjectProperty * ++object_property_add_tm(Object *obj, const char *name, ++ void (*get)(Object *, struct tm *, Error **)) ++{ ++ TMProperty *prop = g_malloc0(sizeof(*prop)); ++ ++ prop->get = get; ++ ++ return object_property_add(obj, name, "struct tm", ++ get ? property_get_tm : NULL, NULL, ++ property_release_data, ++ prop); ++} ++ ++ObjectProperty * ++object_class_property_add_tm(ObjectClass *klass, const char *name, ++ void (*get)(Object *, struct tm *, Error **)) ++{ ++ TMProperty *prop = g_malloc0(sizeof(*prop)); ++ ++ prop->get = get; ++ ++ return object_class_property_add(klass, name, "struct tm", ++ get ? property_get_tm : NULL, ++ NULL, NULL, prop); ++} ++ ++static char *object_get_type(Object *obj, Error **errp) ++{ ++ return g_strdup(object_get_typename(obj)); ++} ++ ++static void property_get_uint8_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint8_t value = *(uint8_t *)opaque; ++ visit_type_uint8(v, name, &value, errp); ++} ++ ++static void property_set_uint8_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint8_t *field = opaque; ++ uint8_t value; ++ ++ if (!visit_type_uint8(v, name, &value, errp)) { ++ return; ++ } ++ ++ *field = value; ++} ++ ++static void property_get_uint16_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint16_t value = *(uint16_t *)opaque; ++ visit_type_uint16(v, name, &value, errp); ++} ++ ++static void property_set_uint16_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint16_t *field = opaque; ++ uint16_t value; ++ ++ if (!visit_type_uint16(v, name, &value, errp)) { ++ return; ++ } ++ ++ *field = value; ++} ++ ++static void property_get_uint32_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint32_t value = *(uint32_t *)opaque; ++ visit_type_uint32(v, name, &value, errp); ++} ++ ++static void property_set_uint32_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint32_t *field = opaque; ++ uint32_t value; ++ ++ if (!visit_type_uint32(v, name, &value, errp)) { ++ return; ++ } ++ ++ *field = value; ++} ++ ++static void property_get_uint64_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint64_t value = *(uint64_t *)opaque; ++ visit_type_uint64(v, name, &value, errp); ++} ++ ++static void property_set_uint64_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint64_t *field = opaque; ++ uint64_t value; ++ ++ if (!visit_type_uint64(v, name, &value, errp)) { ++ return; ++ } ++ ++ *field = value; ++} ++ ++ObjectProperty * ++object_property_add_uint8_ptr(Object *obj, const char *name, ++ const uint8_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint8_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint8_ptr; ++ } ++ ++ return object_property_add(obj, name, "uint8", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, ++ const uint8_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint8_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint8_ptr; ++ } ++ ++ return object_class_property_add(klass, name, "uint8", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_property_add_uint16_ptr(Object *obj, const char *name, ++ const uint16_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint16_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint16_ptr; ++ } ++ ++ return object_property_add(obj, name, "uint16", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, ++ const uint16_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint16_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint16_ptr; ++ } ++ ++ return object_class_property_add(klass, name, "uint16", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_property_add_uint32_ptr(Object *obj, const char *name, ++ const uint32_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint32_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint32_ptr; ++ } ++ ++ return object_property_add(obj, name, "uint32", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, ++ const uint32_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint32_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint32_ptr; ++ } ++ ++ return object_class_property_add(klass, name, "uint32", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_property_add_uint64_ptr(Object *obj, const char *name, ++ const uint64_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint64_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint64_ptr; ++ } ++ ++ return object_property_add(obj, name, "uint64", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, ++ const uint64_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint64_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint64_ptr; ++ } ++ ++ return object_class_property_add(klass, name, "uint64", ++ getter, setter, NULL, (void *)v); ++} ++ ++typedef struct { ++ Object *target_obj; ++ char *target_name; ++} AliasProperty; ++ ++static void property_get_alias(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ AliasProperty *prop = opaque; ++ Visitor *alias_v = visitor_forward_field(v, prop->target_name, name); ++ ++ object_property_get(prop->target_obj, prop->target_name, alias_v, errp); ++ visit_free(alias_v); ++} ++ ++static void property_set_alias(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ AliasProperty *prop = opaque; ++ Visitor *alias_v = visitor_forward_field(v, prop->target_name, name); ++ ++ object_property_set(prop->target_obj, prop->target_name, alias_v, errp); ++ visit_free(alias_v); ++} ++ ++static Object *property_resolve_alias(Object *obj, void *opaque, ++ const char *part) ++{ ++ AliasProperty *prop = opaque; ++ ++ return object_resolve_path_component(prop->target_obj, prop->target_name); ++} ++ ++static void property_release_alias(Object *obj, const char *name, void *opaque) ++{ ++ AliasProperty *prop = opaque; ++ ++ g_free(prop->target_name); ++ g_free(prop); ++} ++ ++ObjectProperty * ++object_property_add_alias(Object *obj, const char *name, ++ Object *target_obj, const char *target_name) ++{ ++ AliasProperty *prop; ++ ObjectProperty *op; ++ ObjectProperty *target_prop; ++ g_autofree char *prop_type = NULL; ++ ++ target_prop = object_property_find_err(target_obj, target_name, ++ &error_abort); ++ ++ if (object_property_is_child(target_prop)) { ++ prop_type = g_strdup_printf("link%s", ++ target_prop->type + strlen("child")); ++ } else { ++ prop_type = g_strdup(target_prop->type); ++ } ++ ++ prop = g_malloc(sizeof(*prop)); ++ prop->target_obj = target_obj; ++ prop->target_name = g_strdup(target_name); ++ ++ op = object_property_add(obj, name, prop_type, ++ property_get_alias, ++ property_set_alias, ++ property_release_alias, ++ prop); ++ op->resolve = property_resolve_alias; ++ if (target_prop->defval) { ++ op->defval = qobject_ref(target_prop->defval); ++ } ++ ++ object_property_set_description(obj, op->name, ++ target_prop->description); ++ return op; ++} ++ ++void object_property_set_description(Object *obj, const char *name, ++ const char *description) ++{ ++ ObjectProperty *op; ++ ++ op = object_property_find_err(obj, name, &error_abort); ++ g_free(op->description); ++ op->description = g_strdup(description); ++} ++ ++void object_class_property_set_description(ObjectClass *klass, ++ const char *name, ++ const char *description) ++{ ++ ObjectProperty *op; ++ ++ op = g_hash_table_lookup(klass->properties, name); ++ g_free(op->description); ++ op->description = g_strdup(description); ++} ++ ++static void object_class_init(ObjectClass *klass, void *data) ++{ ++ object_class_property_add_str(klass, "type", object_get_type, ++ NULL); ++} ++ ++static void register_types(void) ++{ ++ static const TypeInfo interface_info = { ++ .name = TYPE_INTERFACE, ++ .class_size = sizeof(InterfaceClass), ++ .abstract = true, ++ }; ++ ++ static const TypeInfo object_info = { ++ .name = TYPE_OBJECT, ++ .instance_size = sizeof(Object), ++ .class_init = object_class_init, ++ .abstract = true, ++ }; ++ ++ type_interface = type_register_internal(&interface_info); ++ type_register_internal(&object_info); ++} ++ ++type_init(register_types) +diff --git a/qcow2/lib/qom/qom-qobject.c b/qcow2/lib/qom/qom-qobject.c +new file mode 100644 +index 00000000..21ce22de +--- /dev/null ++++ b/qcow2/lib/qom/qom-qobject.c +@@ -0,0 +1,45 @@ ++/* ++ * QEMU Object Model - QObject wrappers ++ * ++ * Copyright (C) 2012 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qom/object.h" ++#include "qom/qom-qobject.h" ++#include "qapi/visitor.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qapi/qobject-output-visitor.h" ++ ++bool object_property_set_qobject(Object *obj, ++ const char *name, QObject *value, ++ Error **errp) ++{ ++ Visitor *v; ++ bool ok; ++ ++ v = qobject_input_visitor_new(value); ++ ok = object_property_set(obj, name, v, errp); ++ visit_free(v); ++ return ok; ++} ++ ++QObject *object_property_get_qobject(Object *obj, const char *name, ++ Error **errp) ++{ ++ QObject *ret = NULL; ++ Visitor *v; ++ ++ v = qobject_output_visitor_new(&ret); ++ if (object_property_get(obj, name, v, errp)) { ++ visit_complete(v, &ret); ++ } ++ visit_free(v); ++ return ret; ++} +diff --git a/qcow2/lib/system/.dirstamp b/qcow2/lib/system/.dirstamp +new file mode 100644 +index 00000000..e69de29b +diff --git a/qcow2/lib/system/cpus.c b/qcow2/lib/system/cpus.c +new file mode 100644 +index 00000000..1c818ff6 +--- /dev/null ++++ b/qcow2/lib/system/cpus.c +@@ -0,0 +1,878 @@ ++/* ++ * QEMU System Emulator ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "monitor/monitor.h" ++#include "qemu/coroutine-tls.h" ++#include "qapi/error.h" ++#include "qapi/qapi-commands-machine.h" ++#include "qapi/qapi-commands-misc.h" ++#include "qapi/qapi-events-run-state.h" ++#include "qapi/qmp/qerror.h" ++#include "exec/gdbstub.h" ++#include "sysemu/hw_accel.h" ++#include "exec/cpu-common.h" ++#include "qemu/thread.h" ++#include "qemu/main-loop.h" ++#include "qemu/plugin.h" ++#include "sysemu/cpus.h" ++#include "qemu/guest-random.h" ++#include "hw/nmi.h" ++#include "sysemu/replay.h" ++#include "sysemu/runstate.h" ++#include "sysemu/cpu-timers.h" ++#include "sysemu/whpx.h" ++#include "hw/boards.h" ++#include "hw/hw.h" ++#include "trace.h" ++ ++#ifdef CONFIG_LINUX ++ ++#include ++ ++#ifndef PR_MCE_KILL ++#define PR_MCE_KILL 33 ++#endif ++ ++#ifndef PR_MCE_KILL_SET ++#define PR_MCE_KILL_SET 1 ++#endif ++ ++#ifndef PR_MCE_KILL_EARLY ++#define PR_MCE_KILL_EARLY 1 ++#endif ++ ++#endif /* CONFIG_LINUX */ ++ ++/* The Big QEMU Lock (BQL) */ ++static QemuMutex bql; ++ ++/* ++ * The chosen accelerator is supposed to register this. ++ */ ++static const AccelOpsClass *cpus_accel; ++ ++bool cpu_is_stopped(CPUState *cpu) ++{ ++ return cpu->stopped || !runstate_is_running(); ++} ++ ++bool cpu_work_list_empty(CPUState *cpu) ++{ ++ return QSIMPLEQ_EMPTY_ATOMIC(&cpu->work_list); ++} ++ ++bool cpu_thread_is_idle(CPUState *cpu) ++{ ++ if (cpu->stop || !cpu_work_list_empty(cpu)) { ++ return false; ++ } ++ if (cpu_is_stopped(cpu)) { ++ return true; ++ } ++ if (!cpu->halted || cpu_has_work(cpu)) { ++ return false; ++ } ++ if (cpus_accel->cpu_thread_is_idle) { ++ return cpus_accel->cpu_thread_is_idle(cpu); ++ } ++ return true; ++} ++ ++bool all_cpu_threads_idle(void) ++{ ++ CPUState *cpu; ++ ++ CPU_FOREACH(cpu) { ++ if (!cpu_thread_is_idle(cpu)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++/***********************************************************/ ++void hw_error(const char *fmt, ...) ++{ ++ va_list ap; ++ CPUState *cpu; ++ ++ va_start(ap, fmt); ++ fprintf(stderr, "qemu: hardware error: "); ++ vfprintf(stderr, fmt, ap); ++ fprintf(stderr, "\n"); ++ CPU_FOREACH(cpu) { ++ fprintf(stderr, "CPU #%d:\n", cpu->cpu_index); ++ cpu_dump_state(cpu, stderr, CPU_DUMP_FPU); ++ } ++ va_end(ap); ++ abort(); ++} ++ ++void cpu_synchronize_all_states(void) ++{ ++ CPUState *cpu; ++ ++ CPU_FOREACH(cpu) { ++ cpu_synchronize_state(cpu); ++ } ++} ++ ++void cpu_synchronize_all_post_reset(void) ++{ ++ CPUState *cpu; ++ ++ CPU_FOREACH(cpu) { ++ cpu_synchronize_post_reset(cpu); ++ } ++} ++ ++void cpu_synchronize_all_post_init(void) ++{ ++ CPUState *cpu; ++ ++ CPU_FOREACH(cpu) { ++ cpu_synchronize_post_init(cpu); ++ } ++} ++ ++void cpu_synchronize_all_pre_loadvm(void) ++{ ++ CPUState *cpu; ++ ++ CPU_FOREACH(cpu) { ++ cpu_synchronize_pre_loadvm(cpu); ++ } ++} ++ ++void cpu_synchronize_state(CPUState *cpu) ++{ ++ if (cpus_accel->synchronize_state) { ++ cpus_accel->synchronize_state(cpu); ++ } ++} ++ ++void cpu_synchronize_post_reset(CPUState *cpu) ++{ ++ if (cpus_accel->synchronize_post_reset) { ++ cpus_accel->synchronize_post_reset(cpu); ++ } ++} ++ ++void cpu_synchronize_post_init(CPUState *cpu) ++{ ++ if (cpus_accel->synchronize_post_init) { ++ cpus_accel->synchronize_post_init(cpu); ++ } ++} ++ ++void cpu_synchronize_pre_loadvm(CPUState *cpu) ++{ ++ if (cpus_accel->synchronize_pre_loadvm) { ++ cpus_accel->synchronize_pre_loadvm(cpu); ++ } ++} ++ ++bool cpus_are_resettable(void) ++{ ++ if (cpus_accel->cpus_are_resettable) { ++ return cpus_accel->cpus_are_resettable(); ++ } ++ return true; ++} ++ ++void cpu_exec_reset_hold(CPUState *cpu) ++{ ++ if (cpus_accel->cpu_reset_hold) { ++ cpus_accel->cpu_reset_hold(cpu); ++ } ++} ++ ++int64_t cpus_get_virtual_clock(void) ++{ ++ /* ++ * XXX ++ * ++ * need to check that cpus_accel is not NULL, because qcow2 calls ++ * qemu_get_clock_ns(CLOCK_VIRTUAL) without any accel initialized and ++ * with ticks disabled in some io-tests: ++ * 030 040 041 060 099 120 127 140 156 161 172 181 191 192 195 203 229 249 256 267 ++ * ++ * is this expected? ++ * ++ * XXX ++ */ ++ if (cpus_accel && cpus_accel->get_virtual_clock) { ++ return cpus_accel->get_virtual_clock(); ++ } ++ return cpu_get_clock(); ++} ++ ++/* ++ * Signal the new virtual time to the accelerator. This is only needed ++ * by accelerators that need to track the changes as we warp time. ++ */ ++void cpus_set_virtual_clock(int64_t new_time) ++{ ++ if (cpus_accel && cpus_accel->set_virtual_clock) { ++ cpus_accel->set_virtual_clock(new_time); ++ } ++} ++ ++/* ++ * return the time elapsed in VM between vm_start and vm_stop. Unless ++ * icount is active, cpus_get_elapsed_ticks() uses units of the host CPU cycle ++ * counter. ++ */ ++int64_t cpus_get_elapsed_ticks(void) ++{ ++ if (cpus_accel->get_elapsed_ticks) { ++ return cpus_accel->get_elapsed_ticks(); ++ } ++ return cpu_get_ticks(); ++} ++ ++static void generic_handle_interrupt(CPUState *cpu, int mask) ++{ ++ cpu->interrupt_request |= mask; ++ ++ if (!qemu_cpu_is_self(cpu)) { ++ qemu_cpu_kick(cpu); ++ } ++} ++ ++void cpu_interrupt(CPUState *cpu, int mask) ++{ ++ if (cpus_accel->handle_interrupt) { ++ cpus_accel->handle_interrupt(cpu, mask); ++ } else { ++ generic_handle_interrupt(cpu, mask); ++ } ++} ++ ++/* ++ * True if the vm was previously suspended, and has not been woken or reset. ++ */ ++static int vm_was_suspended; ++ ++void vm_set_suspended(bool suspended) ++{ ++ vm_was_suspended = suspended; ++} ++ ++bool vm_get_suspended(void) ++{ ++ return vm_was_suspended; ++} ++ ++static int do_vm_stop(RunState state, bool send_stop) ++{ ++ int ret = 0; ++ RunState oldstate = runstate_get(); ++ ++ if (runstate_is_live(oldstate)) { ++ vm_was_suspended = (oldstate == RUN_STATE_SUSPENDED); ++ runstate_set(state); ++ cpu_disable_ticks(); ++ if (oldstate == RUN_STATE_RUNNING) { ++ pause_all_vcpus(); ++ } ++ vm_state_notify(0, state); ++ if (send_stop) { ++ qapi_event_send_stop(); ++ } ++ } ++ ++ bdrv_drain_all(); ++ ret = bdrv_flush_all(); ++ trace_vm_stop_flush_all(ret); ++ ++ return ret; ++} ++ ++/* Special vm_stop() variant for terminating the process. Historically clients ++ * did not expect a QMP STOP event and so we need to retain compatibility. ++ */ ++int vm_shutdown(void) ++{ ++ return do_vm_stop(RUN_STATE_SHUTDOWN, false); ++} ++ ++bool cpu_can_run(CPUState *cpu) ++{ ++ if (cpu->stop) { ++ return false; ++ } ++ if (cpu_is_stopped(cpu)) { ++ return false; ++ } ++ return true; ++} ++ ++void cpu_handle_guest_debug(CPUState *cpu) ++{ ++ if (replay_running_debug()) { ++ if (!cpu->singlestep_enabled) { ++ /* ++ * Report about the breakpoint and ++ * make a single step to skip it ++ */ ++ replay_breakpoint(); ++ cpu_single_step(cpu, SSTEP_ENABLE); ++ } else { ++ cpu_single_step(cpu, 0); ++ } ++ } else { ++ gdb_set_stop_cpu(cpu); ++ qemu_system_debug_request(); ++ cpu->stopped = true; ++ } ++} ++ ++#ifdef CONFIG_LINUX ++static void sigbus_reraise(void) ++{ ++ sigset_t set; ++ struct sigaction action; ++ ++ memset(&action, 0, sizeof(action)); ++ action.sa_handler = SIG_DFL; ++ if (!sigaction(SIGBUS, &action, NULL)) { ++ raise(SIGBUS); ++ sigemptyset(&set); ++ sigaddset(&set, SIGBUS); ++ pthread_sigmask(SIG_UNBLOCK, &set, NULL); ++ } ++ perror("Failed to re-raise SIGBUS!"); ++ abort(); ++} ++ ++static void sigbus_handler(int n, siginfo_t *siginfo, void *ctx) ++{ ++ if (siginfo->si_code != BUS_MCEERR_AO && siginfo->si_code != BUS_MCEERR_AR) { ++ sigbus_reraise(); ++ } ++ ++ if (current_cpu) { ++ /* Called asynchronously in VCPU thread. */ ++ if (kvm_on_sigbus_vcpu(current_cpu, siginfo->si_code, siginfo->si_addr)) { ++ sigbus_reraise(); ++ } ++ } else { ++ /* Called synchronously (via signalfd) in main thread. */ ++ if (kvm_on_sigbus(siginfo->si_code, siginfo->si_addr)) { ++ sigbus_reraise(); ++ } ++ } ++} ++ ++static void qemu_init_sigbus(void) ++{ ++ struct sigaction action; ++ ++ /* ++ * ALERT: when modifying this, take care that SIGBUS forwarding in ++ * qemu_prealloc_mem() will continue working as expected. ++ */ ++ memset(&action, 0, sizeof(action)); ++ action.sa_flags = SA_SIGINFO; ++ action.sa_sigaction = sigbus_handler; ++ sigaction(SIGBUS, &action, NULL); ++ ++ prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0); ++} ++#else /* !CONFIG_LINUX */ ++static void qemu_init_sigbus(void) ++{ ++} ++#endif /* !CONFIG_LINUX */ ++ ++static QemuThread io_thread; ++ ++/* cpu creation */ ++static QemuCond qemu_cpu_cond; ++/* system init */ ++static QemuCond qemu_pause_cond; ++ ++void qemu_init_cpu_loop(void) ++{ ++ qemu_init_sigbus(); ++ qemu_cond_init(&qemu_cpu_cond); ++ qemu_cond_init(&qemu_pause_cond); ++ qemu_mutex_init(&bql); ++ ++ qemu_thread_get_self(&io_thread); ++} ++ ++void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data) ++{ ++ do_run_on_cpu(cpu, func, data, &bql); ++} ++ ++static void qemu_cpu_stop(CPUState *cpu, bool exit) ++{ ++ g_assert(qemu_cpu_is_self(cpu)); ++ cpu->stop = false; ++ cpu->stopped = true; ++ if (exit) { ++ cpu_exit(cpu); ++ } ++ qemu_cond_broadcast(&qemu_pause_cond); ++} ++ ++void qemu_wait_io_event_common(CPUState *cpu) ++{ ++ qatomic_set_mb(&cpu->thread_kicked, false); ++ if (cpu->stop) { ++ qemu_cpu_stop(cpu, false); ++ } ++ process_queued_cpu_work(cpu); ++} ++ ++void qemu_wait_io_event(CPUState *cpu) ++{ ++ bool slept = false; ++ ++ while (cpu_thread_is_idle(cpu)) { ++ if (!slept) { ++ slept = true; ++ qemu_plugin_vcpu_idle_cb(cpu); ++ } ++ qemu_cond_wait(cpu->halt_cond, &bql); ++ } ++ if (slept) { ++ qemu_plugin_vcpu_resume_cb(cpu); ++ } ++ ++ qemu_wait_io_event_common(cpu); ++} ++ ++void cpus_kick_thread(CPUState *cpu) ++{ ++ if (cpu->thread_kicked) { ++ return; ++ } ++ cpu->thread_kicked = true; ++ ++#ifndef _WIN32 ++ int err = pthread_kill(cpu->thread->thread, SIG_IPI); ++ if (err && err != ESRCH) { ++ fprintf(stderr, "qemu:%s: %s", __func__, strerror(err)); ++ exit(1); ++ } ++#else ++ qemu_sem_post(&cpu->sem); ++#endif ++} ++ ++void qemu_cpu_kick(CPUState *cpu) ++{ ++ qemu_cond_broadcast(cpu->halt_cond); ++ if (cpus_accel->kick_vcpu_thread) { ++ cpus_accel->kick_vcpu_thread(cpu); ++ } else { /* default */ ++ cpus_kick_thread(cpu); ++ } ++} ++ ++void qemu_cpu_kick_self(void) ++{ ++ assert(current_cpu); ++ cpus_kick_thread(current_cpu); ++} ++ ++bool qemu_cpu_is_self(CPUState *cpu) ++{ ++ return qemu_thread_is_self(cpu->thread); ++} ++ ++bool qemu_in_vcpu_thread(void) ++{ ++ return current_cpu && qemu_cpu_is_self(current_cpu); ++} ++ ++QEMU_DEFINE_STATIC_CO_TLS(bool, bql_locked) ++ ++bool bql_locked(void) ++{ ++ return get_bql_locked(); ++} ++ ++bool qemu_in_main_thread(void) ++{ ++ return bql_locked(); ++} ++ ++/* ++ * The BQL is taken from so many places that it is worth profiling the ++ * callers directly, instead of funneling them all through a single function. ++ */ ++void bql_lock_impl(const char *file, int line) ++{ ++ QemuMutexLockFunc bql_lock_fn = qatomic_read(&bql_mutex_lock_func); ++ ++ g_assert(!bql_locked()); ++ bql_lock_fn(&bql, file, line); ++ set_bql_locked(true); ++} ++ ++void bql_unlock(void) ++{ ++ g_assert(bql_locked()); ++ set_bql_locked(false); ++ qemu_mutex_unlock(&bql); ++} ++ ++void qemu_cond_wait_bql(QemuCond *cond) ++{ ++ qemu_cond_wait(cond, &bql); ++} ++ ++void qemu_cond_timedwait_bql(QemuCond *cond, int ms) ++{ ++ qemu_cond_timedwait(cond, &bql, ms); ++} ++ ++/* signal CPU creation */ ++void cpu_thread_signal_created(CPUState *cpu) ++{ ++ cpu->created = true; ++ qemu_cond_signal(&qemu_cpu_cond); ++} ++ ++/* signal CPU destruction */ ++void cpu_thread_signal_destroyed(CPUState *cpu) ++{ ++ cpu->created = false; ++ qemu_cond_signal(&qemu_cpu_cond); ++} ++ ++void cpu_pause(CPUState *cpu) ++{ ++ if (qemu_cpu_is_self(cpu)) { ++ qemu_cpu_stop(cpu, true); ++ } else { ++ cpu->stop = true; ++ qemu_cpu_kick(cpu); ++ } ++} ++ ++void cpu_resume(CPUState *cpu) ++{ ++ cpu->stop = false; ++ cpu->stopped = false; ++ qemu_cpu_kick(cpu); ++} ++ ++static bool all_vcpus_paused(void) ++{ ++ CPUState *cpu; ++ ++ CPU_FOREACH(cpu) { ++ if (!cpu->stopped) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++void pause_all_vcpus(void) ++{ ++ CPUState *cpu; ++ ++ qemu_clock_enable(QEMU_CLOCK_VIRTUAL, false); ++ CPU_FOREACH(cpu) { ++ cpu_pause(cpu); ++ } ++ ++ /* We need to drop the replay_lock so any vCPU threads woken up ++ * can finish their replay tasks ++ */ ++ replay_mutex_unlock(); ++ ++ while (!all_vcpus_paused()) { ++ qemu_cond_wait(&qemu_pause_cond, &bql); ++ CPU_FOREACH(cpu) { ++ qemu_cpu_kick(cpu); ++ } ++ } ++ ++ bql_unlock(); ++ replay_mutex_lock(); ++ bql_lock(); ++} ++ ++void resume_all_vcpus(void) ++{ ++ CPUState *cpu; ++ ++ if (!runstate_is_running()) { ++ return; ++ } ++ ++ qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true); ++ CPU_FOREACH(cpu) { ++ cpu_resume(cpu); ++ } ++} ++ ++void cpu_remove_sync(CPUState *cpu) ++{ ++ cpu->stop = true; ++ cpu->unplug = true; ++ qemu_cpu_kick(cpu); ++ bql_unlock(); ++ qemu_thread_join(cpu->thread); ++ bql_lock(); ++} ++ ++void cpus_register_accel(const AccelOpsClass *ops) ++{ ++ assert(ops != NULL); ++ assert(ops->create_vcpu_thread != NULL); /* mandatory */ ++ cpus_accel = ops; ++} ++ ++const AccelOpsClass *cpus_get_accel(void) ++{ ++ /* broken if we call this early */ ++ assert(cpus_accel); ++ return cpus_accel; ++} ++ ++void qemu_init_vcpu(CPUState *cpu) ++{ ++ MachineState *ms = MACHINE(qdev_get_machine()); ++ ++ cpu->nr_cores = machine_topo_get_cores_per_socket(ms); ++ cpu->nr_threads = ms->smp.threads; ++ cpu->stopped = true; ++ cpu->random_seed = qemu_guest_random_seed_thread_part1(); ++ ++ if (!cpu->as) { ++ /* If the target cpu hasn't set up any address spaces itself, ++ * give it the default one. ++ */ ++ cpu->num_ases = 1; ++ cpu_address_space_init(cpu, 0, "cpu-memory", cpu->memory); ++ } ++ ++ /* accelerators all implement the AccelOpsClass */ ++ g_assert(cpus_accel != NULL && cpus_accel->create_vcpu_thread != NULL); ++ cpus_accel->create_vcpu_thread(cpu); ++ ++ while (!cpu->created) { ++ qemu_cond_wait(&qemu_cpu_cond, &bql); ++ } ++} ++ ++void cpu_stop_current(void) ++{ ++ if (current_cpu) { ++ current_cpu->stop = true; ++ cpu_exit(current_cpu); ++ } ++} ++ ++int vm_stop(RunState state) ++{ ++ if (qemu_in_vcpu_thread()) { ++ qemu_system_vmstop_request_prepare(); ++ qemu_system_vmstop_request(state); ++ /* ++ * FIXME: should not return to device code in case ++ * vm_stop() has been requested. ++ */ ++ cpu_stop_current(); ++ return 0; ++ } ++ ++ return do_vm_stop(state, true); ++} ++ ++/** ++ * Prepare for (re)starting the VM. ++ * Returns 0 if the vCPUs should be restarted, -1 on an error condition, ++ * and 1 otherwise. ++ */ ++int vm_prepare_start(bool step_pending) ++{ ++ int ret = vm_was_suspended ? 1 : 0; ++ RunState state = vm_was_suspended ? RUN_STATE_SUSPENDED : RUN_STATE_RUNNING; ++ RunState requested; ++ ++ qemu_vmstop_requested(&requested); ++ if (runstate_is_running() && requested == RUN_STATE__MAX) { ++ return -1; ++ } ++ ++ /* Ensure that a STOP/RESUME pair of events is emitted if a ++ * vmstop request was pending. The BLOCK_IO_ERROR event, for ++ * example, according to documentation is always followed by ++ * the STOP event. ++ */ ++ if (runstate_is_running()) { ++ qapi_event_send_stop(); ++ qapi_event_send_resume(); ++ return -1; ++ } ++ ++ /* ++ * WHPX accelerator needs to know whether we are going to step ++ * any CPUs, before starting the first one. ++ */ ++ if (cpus_accel->synchronize_pre_resume) { ++ cpus_accel->synchronize_pre_resume(step_pending); ++ } ++ ++ /* We are sending this now, but the CPUs will be resumed shortly later */ ++ qapi_event_send_resume(); ++ ++ cpu_enable_ticks(); ++ runstate_set(state); ++ vm_state_notify(1, state); ++ vm_was_suspended = false; ++ return ret; ++} ++ ++void vm_start(void) ++{ ++ if (!vm_prepare_start(false)) { ++ resume_all_vcpus(); ++ } ++} ++ ++void vm_resume(RunState state) ++{ ++ if (runstate_is_live(state)) { ++ vm_start(); ++ } else { ++ runstate_set(state); ++ } ++} ++ ++/* does a state transition even if the VM is already stopped, ++ current state is forgotten forever */ ++int vm_stop_force_state(RunState state) ++{ ++ if (runstate_is_live(runstate_get())) { ++ return vm_stop(state); ++ } else { ++ int ret; ++ runstate_set(state); ++ ++ bdrv_drain_all(); ++ /* Make sure to return an error if the flush in a previous vm_stop() ++ * failed. */ ++ ret = bdrv_flush_all(); ++ trace_vm_stop_flush_all(ret); ++ return ret; ++ } ++} ++ ++void qmp_memsave(uint64_t addr, uint64_t size, const char *filename, ++ bool has_cpu, int64_t cpu_index, Error **errp) ++{ ++ FILE *f; ++ uint64_t l; ++ CPUState *cpu; ++ uint8_t buf[1024]; ++ uint64_t orig_addr = addr, orig_size = size; ++ ++ if (!has_cpu) { ++ cpu_index = 0; ++ } ++ ++ cpu = qemu_get_cpu(cpu_index); ++ if (cpu == NULL) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", ++ "a CPU number"); ++ return; ++ } ++ ++ f = fopen(filename, "wb"); ++ if (!f) { ++ error_setg_file_open(errp, errno, filename); ++ return; ++ } ++ ++ while (size != 0) { ++ l = sizeof(buf); ++ if (l > size) ++ l = size; ++ if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) { ++ error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRIu64 ++ " specified", orig_addr, orig_size); ++ goto exit; ++ } ++ if (fwrite(buf, 1, l, f) != l) { ++ error_setg(errp, "writing memory to '%s' failed", ++ filename); ++ goto exit; ++ } ++ addr += l; ++ size -= l; ++ } ++ ++exit: ++ fclose(f); ++} ++ ++void qmp_pmemsave(uint64_t addr, uint64_t size, const char *filename, ++ Error **errp) ++{ ++ FILE *f; ++ uint64_t l; ++ uint8_t buf[1024]; ++ ++ f = fopen(filename, "wb"); ++ if (!f) { ++ error_setg_file_open(errp, errno, filename); ++ return; ++ } ++ ++ while (size != 0) { ++ l = sizeof(buf); ++ if (l > size) ++ l = size; ++ cpu_physical_memory_read(addr, buf, l); ++ if (fwrite(buf, 1, l, f) != l) { ++ error_setg(errp, "writing memory to '%s' failed", ++ filename); ++ goto exit; ++ } ++ addr += l; ++ size -= l; ++ } ++ ++exit: ++ fclose(f); ++} ++ ++void qmp_inject_nmi(Error **errp) ++{ ++ nmi_monitor_handle(monitor_get_cpu_index(monitor_cur()), errp); ++} ++ +diff --git a/qcow2/lib/util/aio-posix.c b/qcow2/lib/util/aio-posix.c +new file mode 100644 +index 00000000..266c9dd3 +--- /dev/null ++++ b/qcow2/lib/util/aio-posix.c +@@ -0,0 +1,789 @@ ++/* ++ * QEMU aio implementation ++ * ++ * Copyright IBM, Corp. 2008 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/block.h" ++#include "block/thread-pool.h" ++#include "qemu/main-loop.h" ++#include "qemu/rcu.h" ++#include "qemu/rcu_queue.h" ++#include "qemu/sockets.h" ++#include "qemu/cutils.h" ++#include "trace.h" ++#include "aio-posix.h" ++ ++/* Stop userspace polling on a handler if it isn't active for some time */ ++#define POLL_IDLE_INTERVAL_NS (7 * NANOSECONDS_PER_SECOND) ++ ++bool aio_poll_disabled(AioContext *ctx) ++{ ++ return qatomic_read(&ctx->poll_disable_cnt); ++} ++ ++void aio_add_ready_handler(AioHandlerList *ready_list, ++ AioHandler *node, ++ int revents) ++{ ++ QLIST_SAFE_REMOVE(node, node_ready); /* remove from nested parent's list */ ++ node->pfd.revents = revents; ++ QLIST_INSERT_HEAD(ready_list, node, node_ready); ++} ++ ++static void aio_add_poll_ready_handler(AioHandlerList *ready_list, ++ AioHandler *node) ++{ ++ QLIST_SAFE_REMOVE(node, node_ready); /* remove from nested parent's list */ ++ node->poll_ready = true; ++ QLIST_INSERT_HEAD(ready_list, node, node_ready); ++} ++ ++static AioHandler *find_aio_handler(AioContext *ctx, int fd) ++{ ++ AioHandler *node; ++ ++ QLIST_FOREACH(node, &ctx->aio_handlers, node) { ++ if (node->pfd.fd == fd) { ++ if (!QLIST_IS_INSERTED(node, node_deleted)) { ++ return node; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++static bool aio_remove_fd_handler(AioContext *ctx, AioHandler *node) ++{ ++ /* If the GSource is in the process of being destroyed then ++ * g_source_remove_poll() causes an assertion failure. Skip ++ * removal in that case, because glib cleans up its state during ++ * destruction anyway. ++ */ ++ if (!g_source_is_destroyed(&ctx->source)) { ++ g_source_remove_poll(&ctx->source, &node->pfd); ++ } ++ ++ node->pfd.revents = 0; ++ node->poll_ready = false; ++ ++ /* If the fd monitor has already marked it deleted, leave it alone */ ++ if (QLIST_IS_INSERTED(node, node_deleted)) { ++ return false; ++ } ++ ++ /* If a read is in progress, just mark the node as deleted */ ++ if (qemu_lockcnt_count(&ctx->list_lock)) { ++ QLIST_INSERT_HEAD_RCU(&ctx->deleted_aio_handlers, node, node_deleted); ++ return false; ++ } ++ /* Otherwise, delete it for real. We can't just mark it as ++ * deleted because deleted nodes are only cleaned up while ++ * no one is walking the handlers list. ++ */ ++ QLIST_SAFE_REMOVE(node, node_poll); ++ QLIST_REMOVE(node, node); ++ return true; ++} ++ ++void aio_set_fd_handler(AioContext *ctx, ++ int fd, ++ IOHandler *io_read, ++ IOHandler *io_write, ++ AioPollFn *io_poll, ++ IOHandler *io_poll_ready, ++ void *opaque) ++{ ++ AioHandler *node; ++ AioHandler *new_node = NULL; ++ bool is_new = false; ++ bool deleted = false; ++ int poll_disable_change; ++ ++ if (io_poll && !io_poll_ready) { ++ io_poll = NULL; /* polling only makes sense if there is a handler */ ++ } ++ ++ qemu_lockcnt_lock(&ctx->list_lock); ++ ++ node = find_aio_handler(ctx, fd); ++ ++ /* Are we deleting the fd handler? */ ++ if (!io_read && !io_write && !io_poll) { ++ if (node == NULL) { ++ qemu_lockcnt_unlock(&ctx->list_lock); ++ return; ++ } ++ /* Clean events in order to unregister fd from the ctx epoll. */ ++ node->pfd.events = 0; ++ ++ poll_disable_change = -!node->io_poll; ++ } else { ++ poll_disable_change = !io_poll - (node && !node->io_poll); ++ if (node == NULL) { ++ is_new = true; ++ } ++ /* Alloc and insert if it's not already there */ ++ new_node = g_new0(AioHandler, 1); ++ ++ /* Update handler with latest information */ ++ new_node->io_read = io_read; ++ new_node->io_write = io_write; ++ new_node->io_poll = io_poll; ++ new_node->io_poll_ready = io_poll_ready; ++ new_node->opaque = opaque; ++ ++ if (is_new) { ++ new_node->pfd.fd = fd; ++ } else { ++ new_node->pfd = node->pfd; ++ } ++ g_source_add_poll(&ctx->source, &new_node->pfd); ++ ++ new_node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP | G_IO_ERR : 0); ++ new_node->pfd.events |= (io_write ? G_IO_OUT | G_IO_ERR : 0); ++ ++ QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, new_node, node); ++ } ++ ++ /* No need to order poll_disable_cnt writes against other updates; ++ * the counter is only used to avoid wasting time and latency on ++ * iterated polling when the system call will be ultimately necessary. ++ * Changing handlers is a rare event, and a little wasted polling until ++ * the aio_notify below is not an issue. ++ */ ++ qatomic_set(&ctx->poll_disable_cnt, ++ qatomic_read(&ctx->poll_disable_cnt) + poll_disable_change); ++ ++ ctx->fdmon_ops->update(ctx, node, new_node); ++ if (node) { ++ deleted = aio_remove_fd_handler(ctx, node); ++ } ++ qemu_lockcnt_unlock(&ctx->list_lock); ++ aio_notify(ctx); ++ ++ if (deleted) { ++ g_free(node); ++ } ++} ++ ++static void aio_set_fd_poll(AioContext *ctx, int fd, ++ IOHandler *io_poll_begin, ++ IOHandler *io_poll_end) ++{ ++ AioHandler *node = find_aio_handler(ctx, fd); ++ ++ if (!node) { ++ return; ++ } ++ ++ node->io_poll_begin = io_poll_begin; ++ node->io_poll_end = io_poll_end; ++} ++ ++void aio_set_event_notifier(AioContext *ctx, ++ EventNotifier *notifier, ++ EventNotifierHandler *io_read, ++ AioPollFn *io_poll, ++ EventNotifierHandler *io_poll_ready) ++{ ++ aio_set_fd_handler(ctx, event_notifier_get_fd(notifier), ++ (IOHandler *)io_read, NULL, io_poll, ++ (IOHandler *)io_poll_ready, notifier); ++} ++ ++void aio_set_event_notifier_poll(AioContext *ctx, ++ EventNotifier *notifier, ++ EventNotifierHandler *io_poll_begin, ++ EventNotifierHandler *io_poll_end) ++{ ++ aio_set_fd_poll(ctx, event_notifier_get_fd(notifier), ++ (IOHandler *)io_poll_begin, ++ (IOHandler *)io_poll_end); ++} ++ ++static bool poll_set_started(AioContext *ctx, AioHandlerList *ready_list, ++ bool started) ++{ ++ AioHandler *node; ++ bool progress = false; ++ ++ if (started == ctx->poll_started) { ++ return false; ++ } ++ ++ ctx->poll_started = started; ++ ++ qemu_lockcnt_inc(&ctx->list_lock); ++ QLIST_FOREACH(node, &ctx->poll_aio_handlers, node_poll) { ++ IOHandler *fn; ++ ++ if (QLIST_IS_INSERTED(node, node_deleted)) { ++ continue; ++ } ++ ++ if (started) { ++ fn = node->io_poll_begin; ++ } else { ++ fn = node->io_poll_end; ++ } ++ ++ if (fn) { ++ fn(node->opaque); ++ } ++ ++ /* Poll one last time in case ->io_poll_end() raced with the event */ ++ if (!started && node->io_poll(node->opaque)) { ++ aio_add_poll_ready_handler(ready_list, node); ++ progress = true; ++ } ++ } ++ qemu_lockcnt_dec(&ctx->list_lock); ++ ++ return progress; ++} ++ ++ ++bool aio_prepare(AioContext *ctx) ++{ ++ AioHandlerList ready_list = QLIST_HEAD_INITIALIZER(ready_list); ++ ++ /* Poll mode cannot be used with glib's event loop, disable it. */ ++ poll_set_started(ctx, &ready_list, false); ++ /* TODO what to do with this list? */ ++ ++ return false; ++} ++ ++bool aio_pending(AioContext *ctx) ++{ ++ AioHandler *node; ++ bool result = false; ++ ++ /* ++ * We have to walk very carefully in case aio_set_fd_handler is ++ * called while we're walking. ++ */ ++ qemu_lockcnt_inc(&ctx->list_lock); ++ ++ QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { ++ int revents; ++ ++ /* TODO should this check poll ready? */ ++ revents = node->pfd.revents & node->pfd.events; ++ if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) { ++ result = true; ++ break; ++ } ++ if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write) { ++ result = true; ++ break; ++ } ++ } ++ qemu_lockcnt_dec(&ctx->list_lock); ++ ++ return result; ++} ++ ++static void aio_free_deleted_handlers(AioContext *ctx) ++{ ++ AioHandler *node; ++ ++ if (QLIST_EMPTY_RCU(&ctx->deleted_aio_handlers)) { ++ return; ++ } ++ if (!qemu_lockcnt_dec_if_lock(&ctx->list_lock)) { ++ return; /* we are nested, let the parent do the freeing */ ++ } ++ ++ while ((node = QLIST_FIRST_RCU(&ctx->deleted_aio_handlers))) { ++ QLIST_REMOVE(node, node); ++ QLIST_REMOVE(node, node_deleted); ++ QLIST_SAFE_REMOVE(node, node_poll); ++ g_free(node); ++ } ++ ++ qemu_lockcnt_inc_and_unlock(&ctx->list_lock); ++} ++ ++static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node) ++{ ++ bool progress = false; ++ bool poll_ready; ++ int revents; ++ ++ revents = node->pfd.revents & node->pfd.events; ++ node->pfd.revents = 0; ++ ++ poll_ready = node->poll_ready; ++ node->poll_ready = false; ++ ++ /* ++ * Start polling AioHandlers when they become ready because activity is ++ * likely to continue. Note that starvation is theoretically possible when ++ * fdmon_supports_polling(), but only until the fd fires for the first ++ * time. ++ */ ++ if (!QLIST_IS_INSERTED(node, node_deleted) && ++ !QLIST_IS_INSERTED(node, node_poll) && ++ node->io_poll) { ++ trace_poll_add(ctx, node, node->pfd.fd, revents); ++ if (ctx->poll_started && node->io_poll_begin) { ++ node->io_poll_begin(node->opaque); ++ } ++ QLIST_INSERT_HEAD(&ctx->poll_aio_handlers, node, node_poll); ++ } ++ if (!QLIST_IS_INSERTED(node, node_deleted) && ++ poll_ready && revents == 0 && node->io_poll_ready) { ++ /* ++ * Remove temporarily to avoid infinite loops when ->io_poll_ready() ++ * calls aio_poll() before clearing the condition that made the poll ++ * handler become ready. ++ */ ++ QLIST_SAFE_REMOVE(node, node_poll); ++ ++ node->io_poll_ready(node->opaque); ++ ++ if (!QLIST_IS_INSERTED(node, node_poll)) { ++ QLIST_INSERT_HEAD(&ctx->poll_aio_handlers, node, node_poll); ++ } ++ ++ /* ++ * Return early since revents was zero. aio_notify() does not count as ++ * progress. ++ */ ++ return node->opaque != &ctx->notifier; ++ } ++ ++ if (!QLIST_IS_INSERTED(node, node_deleted) && ++ (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) && ++ node->io_read) { ++ node->io_read(node->opaque); ++ ++ /* aio_notify() does not count as progress */ ++ if (node->opaque != &ctx->notifier) { ++ progress = true; ++ } ++ } ++ if (!QLIST_IS_INSERTED(node, node_deleted) && ++ (revents & (G_IO_OUT | G_IO_ERR)) && ++ node->io_write) { ++ node->io_write(node->opaque); ++ progress = true; ++ } ++ ++ return progress; ++} ++ ++/* ++ * If we have a list of ready handlers then this is more efficient than ++ * scanning all handlers with aio_dispatch_handlers(). ++ */ ++static bool aio_dispatch_ready_handlers(AioContext *ctx, ++ AioHandlerList *ready_list) ++{ ++ bool progress = false; ++ AioHandler *node; ++ ++ while ((node = QLIST_FIRST(ready_list))) { ++ QLIST_REMOVE(node, node_ready); ++ progress = aio_dispatch_handler(ctx, node) || progress; ++ } ++ ++ return progress; ++} ++ ++/* Slower than aio_dispatch_ready_handlers() but only used via glib */ ++static bool aio_dispatch_handlers(AioContext *ctx) ++{ ++ AioHandler *node, *tmp; ++ bool progress = false; ++ ++ QLIST_FOREACH_SAFE_RCU(node, &ctx->aio_handlers, node, tmp) { ++ progress = aio_dispatch_handler(ctx, node) || progress; ++ } ++ ++ return progress; ++} ++ ++void aio_dispatch(AioContext *ctx) ++{ ++ qemu_lockcnt_inc(&ctx->list_lock); ++ aio_bh_poll(ctx); ++ aio_dispatch_handlers(ctx); ++ aio_free_deleted_handlers(ctx); ++ qemu_lockcnt_dec(&ctx->list_lock); ++ ++ timerlistgroup_run_timers(&ctx->tlg); ++} ++ ++static bool run_poll_handlers_once(AioContext *ctx, ++ AioHandlerList *ready_list, ++ int64_t now, ++ int64_t *timeout) ++{ ++ bool progress = false; ++ AioHandler *node; ++ AioHandler *tmp; ++ ++ QLIST_FOREACH_SAFE(node, &ctx->poll_aio_handlers, node_poll, tmp) { ++ if (node->io_poll(node->opaque)) { ++ aio_add_poll_ready_handler(ready_list, node); ++ ++ node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS; ++ ++ /* ++ * Polling was successful, exit try_poll_mode immediately ++ * to adjust the next polling time. ++ */ ++ *timeout = 0; ++ if (node->opaque != &ctx->notifier) { ++ progress = true; ++ } ++ } ++ ++ /* Caller handles freeing deleted nodes. Don't do it here. */ ++ } ++ ++ return progress; ++} ++ ++static bool fdmon_supports_polling(AioContext *ctx) ++{ ++ return ctx->fdmon_ops->need_wait != aio_poll_disabled; ++} ++ ++static bool remove_idle_poll_handlers(AioContext *ctx, ++ AioHandlerList *ready_list, ++ int64_t now) ++{ ++ AioHandler *node; ++ AioHandler *tmp; ++ bool progress = false; ++ ++ /* ++ * File descriptor monitoring implementations without userspace polling ++ * support suffer from starvation when a subset of handlers is polled ++ * because fds will not be processed in a timely fashion. Don't remove ++ * idle poll handlers. ++ */ ++ if (!fdmon_supports_polling(ctx)) { ++ return false; ++ } ++ ++ QLIST_FOREACH_SAFE(node, &ctx->poll_aio_handlers, node_poll, tmp) { ++ if (node->poll_idle_timeout == 0LL) { ++ node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS; ++ } else if (now >= node->poll_idle_timeout) { ++ trace_poll_remove(ctx, node, node->pfd.fd); ++ node->poll_idle_timeout = 0LL; ++ QLIST_SAFE_REMOVE(node, node_poll); ++ if (ctx->poll_started && node->io_poll_end) { ++ node->io_poll_end(node->opaque); ++ ++ /* ++ * Final poll in case ->io_poll_end() races with an event. ++ * Nevermind about re-adding the handler in the rare case where ++ * this causes progress. ++ */ ++ if (node->io_poll(node->opaque)) { ++ aio_add_poll_ready_handler(ready_list, node); ++ progress = true; ++ } ++ } ++ } ++ } ++ ++ return progress; ++} ++ ++/* run_poll_handlers: ++ * @ctx: the AioContext ++ * @ready_list: the list to place ready handlers on ++ * @max_ns: maximum time to poll for, in nanoseconds ++ * ++ * Polls for a given time. ++ * ++ * Note that the caller must have incremented ctx->list_lock. ++ * ++ * Returns: true if progress was made, false otherwise ++ */ ++static bool run_poll_handlers(AioContext *ctx, AioHandlerList *ready_list, ++ int64_t max_ns, int64_t *timeout) ++{ ++ bool progress; ++ int64_t start_time, elapsed_time; ++ ++ assert(qemu_lockcnt_count(&ctx->list_lock) > 0); ++ ++ trace_run_poll_handlers_begin(ctx, max_ns, *timeout); ++ ++ /* ++ * Optimization: ->io_poll() handlers often contain RCU read critical ++ * sections and we therefore see many rcu_read_lock() -> rcu_read_unlock() ++ * -> rcu_read_lock() -> ... sequences with expensive memory ++ * synchronization primitives. Make the entire polling loop an RCU ++ * critical section because nested rcu_read_lock()/rcu_read_unlock() calls ++ * are cheap. ++ */ ++ RCU_READ_LOCK_GUARD(); ++ ++ start_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); ++ do { ++ progress = run_poll_handlers_once(ctx, ready_list, ++ start_time, timeout); ++ elapsed_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start_time; ++ max_ns = qemu_soonest_timeout(*timeout, max_ns); ++ assert(!(max_ns && progress)); ++ } while (elapsed_time < max_ns && !ctx->fdmon_ops->need_wait(ctx)); ++ ++ if (remove_idle_poll_handlers(ctx, ready_list, ++ start_time + elapsed_time)) { ++ *timeout = 0; ++ progress = true; ++ } ++ ++ /* If time has passed with no successful polling, adjust *timeout to ++ * keep the same ending time. ++ */ ++ if (*timeout != -1) { ++ *timeout -= MIN(*timeout, elapsed_time); ++ } ++ ++ trace_run_poll_handlers_end(ctx, progress, *timeout); ++ return progress; ++} ++ ++/* try_poll_mode: ++ * @ctx: the AioContext ++ * @ready_list: list to add handlers that need to be run ++ * @timeout: timeout for blocking wait, computed by the caller and updated if ++ * polling succeeds. ++ * ++ * Note that the caller must have incremented ctx->list_lock. ++ * ++ * Returns: true if progress was made, false otherwise ++ */ ++static bool try_poll_mode(AioContext *ctx, AioHandlerList *ready_list, ++ int64_t *timeout) ++{ ++ int64_t max_ns; ++ ++ if (QLIST_EMPTY_RCU(&ctx->poll_aio_handlers)) { ++ return false; ++ } ++ ++ max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns); ++ if (max_ns && !ctx->fdmon_ops->need_wait(ctx)) { ++ /* ++ * Enable poll mode. It pairs with the poll_set_started() in ++ * aio_poll() which disables poll mode. ++ */ ++ poll_set_started(ctx, ready_list, true); ++ ++ if (run_poll_handlers(ctx, ready_list, max_ns, timeout)) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++bool aio_poll(AioContext *ctx, bool blocking) ++{ ++ AioHandlerList ready_list = QLIST_HEAD_INITIALIZER(ready_list); ++ bool progress; ++ bool use_notify_me; ++ int64_t timeout; ++ int64_t start = 0; ++ ++ /* ++ * There cannot be two concurrent aio_poll calls for the same AioContext (or ++ * an aio_poll concurrent with a GSource prepare/check/dispatch callback). ++ * We rely on this below to avoid slow locked accesses to ctx->notify_me. ++ * ++ * aio_poll() may only be called in the AioContext's thread. iohandler_ctx ++ * is special in that it runs in the main thread, but that thread's context ++ * is qemu_aio_context. ++ */ ++ assert(in_aio_context_home_thread(ctx == iohandler_get_aio_context() ? ++ qemu_get_aio_context() : ctx)); ++ ++ qemu_lockcnt_inc(&ctx->list_lock); ++ ++ if (ctx->poll_max_ns) { ++ start = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); ++ } ++ ++ timeout = blocking ? aio_compute_timeout(ctx) : 0; ++ progress = try_poll_mode(ctx, &ready_list, &timeout); ++ assert(!(timeout && progress)); ++ ++ /* ++ * aio_notify can avoid the expensive event_notifier_set if ++ * everything (file descriptors, bottom halves, timers) will ++ * be re-evaluated before the next blocking poll(). This is ++ * already true when aio_poll is called with blocking == false; ++ * if blocking == true, it is only true after poll() returns, ++ * so disable the optimization now. ++ */ ++ use_notify_me = timeout != 0; ++ if (use_notify_me) { ++ qatomic_set(&ctx->notify_me, qatomic_read(&ctx->notify_me) + 2); ++ /* ++ * Write ctx->notify_me before reading ctx->notified. Pairs with ++ * smp_mb in aio_notify(). ++ */ ++ smp_mb(); ++ ++ /* Don't block if aio_notify() was called */ ++ if (qatomic_read(&ctx->notified)) { ++ timeout = 0; ++ } ++ } ++ ++ /* If polling is allowed, non-blocking aio_poll does not need the ++ * system call---a single round of run_poll_handlers_once suffices. ++ */ ++ if (timeout || ctx->fdmon_ops->need_wait(ctx)) { ++ /* ++ * Disable poll mode. poll mode should be disabled before the call ++ * of ctx->fdmon_ops->wait() so that guest's notification can wake ++ * up IO threads when some work becomes pending. It is essential to ++ * avoid hangs or unnecessary latency. ++ */ ++ if (poll_set_started(ctx, &ready_list, false)) { ++ timeout = 0; ++ progress = true; ++ } ++ ++ ctx->fdmon_ops->wait(ctx, &ready_list, timeout); ++ } ++ ++ if (use_notify_me) { ++ /* Finish the poll before clearing the flag. */ ++ qatomic_store_release(&ctx->notify_me, ++ qatomic_read(&ctx->notify_me) - 2); ++ } ++ ++ aio_notify_accept(ctx); ++ ++ /* Adjust polling time */ ++ if (ctx->poll_max_ns) { ++ int64_t block_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start; ++ ++ if (block_ns <= ctx->poll_ns) { ++ /* This is the sweet spot, no adjustment needed */ ++ } else if (block_ns > ctx->poll_max_ns) { ++ /* We'd have to poll for too long, poll less */ ++ int64_t old = ctx->poll_ns; ++ ++ if (ctx->poll_shrink) { ++ ctx->poll_ns /= ctx->poll_shrink; ++ } else { ++ ctx->poll_ns = 0; ++ } ++ ++ trace_poll_shrink(ctx, old, ctx->poll_ns); ++ } else if (ctx->poll_ns < ctx->poll_max_ns && ++ block_ns < ctx->poll_max_ns) { ++ /* There is room to grow, poll longer */ ++ int64_t old = ctx->poll_ns; ++ int64_t grow = ctx->poll_grow; ++ ++ if (grow == 0) { ++ grow = 2; ++ } ++ ++ if (ctx->poll_ns) { ++ ctx->poll_ns *= grow; ++ } else { ++ ctx->poll_ns = 4000; /* start polling at 4 microseconds */ ++ } ++ ++ if (ctx->poll_ns > ctx->poll_max_ns) { ++ ctx->poll_ns = ctx->poll_max_ns; ++ } ++ ++ trace_poll_grow(ctx, old, ctx->poll_ns); ++ } ++ } ++ ++ progress |= aio_bh_poll(ctx); ++ progress |= aio_dispatch_ready_handlers(ctx, &ready_list); ++ ++ aio_free_deleted_handlers(ctx); ++ ++ qemu_lockcnt_dec(&ctx->list_lock); ++ ++ progress |= timerlistgroup_run_timers(&ctx->tlg); ++ ++ return progress; ++} ++ ++void aio_context_setup(AioContext *ctx) ++{ ++ ctx->fdmon_ops = &fdmon_poll_ops; ++ ctx->epollfd = -1; ++ ++ /* Use the fastest fd monitoring implementation if available */ ++ if (fdmon_io_uring_setup(ctx)) { ++ return; ++ } ++ ++ fdmon_epoll_setup(ctx); ++} ++ ++void aio_context_destroy(AioContext *ctx) ++{ ++ fdmon_io_uring_destroy(ctx); ++ fdmon_epoll_disable(ctx); ++ aio_free_deleted_handlers(ctx); ++} ++ ++void aio_context_use_g_source(AioContext *ctx) ++{ ++ /* ++ * Disable io_uring when the glib main loop is used because it doesn't ++ * support mixed glib/aio_poll() usage. It relies on aio_poll() being ++ * called regularly so that changes to the monitored file descriptors are ++ * submitted, otherwise a list of pending fd handlers builds up. ++ */ ++ fdmon_io_uring_destroy(ctx); ++ aio_free_deleted_handlers(ctx); ++} ++ ++void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, ++ int64_t grow, int64_t shrink, Error **errp) ++{ ++ /* No thread synchronization here, it doesn't matter if an incorrect value ++ * is used once. ++ */ ++ ctx->poll_max_ns = max_ns; ++ ctx->poll_ns = 0; ++ ctx->poll_grow = grow; ++ ctx->poll_shrink = shrink; ++ ++ aio_notify(ctx); ++} ++ ++void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch) ++{ ++ /* ++ * No thread synchronization here, it doesn't matter if an incorrect value ++ * is used once. ++ */ ++ ctx->aio_max_batch = max_batch; ++ ++ aio_notify(ctx); ++} +diff --git a/qcow2/lib/util/aio-posix.h b/qcow2/lib/util/aio-posix.h +new file mode 100644 +index 00000000..4264c518 +--- /dev/null ++++ b/qcow2/lib/util/aio-posix.h +@@ -0,0 +1,82 @@ ++/* ++ * AioContext POSIX event loop implementation internal APIs ++ * ++ * Copyright IBM, Corp. 2008 ++ * Copyright Red Hat, Inc. 2020 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#ifndef AIO_POSIX_H ++#define AIO_POSIX_H ++ ++#include "block/aio.h" ++ ++struct AioHandler { ++ GPollFD pfd; ++ IOHandler *io_read; ++ IOHandler *io_write; ++ AioPollFn *io_poll; ++ IOHandler *io_poll_ready; ++ IOHandler *io_poll_begin; ++ IOHandler *io_poll_end; ++ void *opaque; ++ QLIST_ENTRY(AioHandler) node; ++ QLIST_ENTRY(AioHandler) node_ready; /* only used during aio_poll() */ ++ QLIST_ENTRY(AioHandler) node_deleted; ++ QLIST_ENTRY(AioHandler) node_poll; ++#ifdef CONFIG_LINUX_IO_URING ++ QSLIST_ENTRY(AioHandler) node_submitted; ++ unsigned flags; /* see fdmon-io_uring.c */ ++#endif ++ int64_t poll_idle_timeout; /* when to stop userspace polling */ ++ bool poll_ready; /* has polling detected an event? */ ++}; ++ ++/* Add a handler to a ready list */ ++void aio_add_ready_handler(AioHandlerList *ready_list, AioHandler *node, ++ int revents); ++ ++extern const FDMonOps fdmon_poll_ops; ++ ++#ifdef CONFIG_EPOLL_CREATE1 ++bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd); ++void fdmon_epoll_setup(AioContext *ctx); ++void fdmon_epoll_disable(AioContext *ctx); ++#else ++static inline bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd) ++{ ++ return false; ++} ++ ++static inline void fdmon_epoll_setup(AioContext *ctx) ++{ ++} ++ ++static inline void fdmon_epoll_disable(AioContext *ctx) ++{ ++} ++#endif /* !CONFIG_EPOLL_CREATE1 */ ++ ++#ifdef CONFIG_LINUX_IO_URING ++bool fdmon_io_uring_setup(AioContext *ctx); ++void fdmon_io_uring_destroy(AioContext *ctx); ++#else ++static inline bool fdmon_io_uring_setup(AioContext *ctx) ++{ ++ return false; ++} ++ ++static inline void fdmon_io_uring_destroy(AioContext *ctx) ++{ ++} ++#endif /* !CONFIG_LINUX_IO_URING */ ++ ++#endif /* AIO_POSIX_H */ +diff --git a/qcow2/lib/util/aio-wait.c b/qcow2/lib/util/aio-wait.c +new file mode 100644 +index 00000000..b5336cf5 +--- /dev/null ++++ b/qcow2/lib/util/aio-wait.c +@@ -0,0 +1,86 @@ ++/* ++ * AioContext wait support ++ * ++ * Copyright (C) 2018 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/main-loop.h" ++#include "block/aio-wait.h" ++ ++AioWait global_aio_wait; ++ ++static void dummy_bh_cb(void *opaque) ++{ ++ /* The point is to make AIO_WAIT_WHILE()'s aio_poll() return */ ++} ++ ++void aio_wait_kick(void) ++{ ++ /* ++ * Paired with smp_mb in AIO_WAIT_WHILE. Here we have: ++ * write(condition); ++ * aio_wait_kick() { ++ * smp_mb(); ++ * read(num_waiters); ++ * } ++ * ++ * And in AIO_WAIT_WHILE: ++ * write(num_waiters); ++ * smp_mb(); ++ * read(condition); ++ */ ++ smp_mb(); ++ ++ if (qatomic_read(&global_aio_wait.num_waiters)) { ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL); ++ } ++} ++ ++typedef struct { ++ bool done; ++ QEMUBHFunc *cb; ++ void *opaque; ++} AioWaitBHData; ++ ++/* Context: BH in IOThread */ ++static void aio_wait_bh(void *opaque) ++{ ++ AioWaitBHData *data = opaque; ++ ++ data->cb(data->opaque); ++ ++ data->done = true; ++ aio_wait_kick(); ++} ++ ++void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque) ++{ ++ AioWaitBHData data = { ++ .cb = cb, ++ .opaque = opaque, ++ }; ++ ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ ++ aio_bh_schedule_oneshot(ctx, aio_wait_bh, &data); ++ AIO_WAIT_WHILE_UNLOCKED(NULL, !data.done); ++} +diff --git a/qcow2/lib/util/aiocb.c b/qcow2/lib/util/aiocb.c +new file mode 100644 +index 00000000..5aef3a06 +--- /dev/null ++++ b/qcow2/lib/util/aiocb.c +@@ -0,0 +1,55 @@ ++/* ++ * BlockAIOCB allocation ++ * ++ * Copyright (c) 2003-2017 Fabrice Bellard and other QEMU contributors ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/aio.h" ++ ++void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ BlockAIOCB *acb; ++ ++ acb = g_malloc(aiocb_info->aiocb_size); ++ acb->aiocb_info = aiocb_info; ++ acb->bs = bs; ++ acb->cb = cb; ++ acb->opaque = opaque; ++ acb->refcnt = 1; ++ return acb; ++} ++ ++void qemu_aio_ref(void *p) ++{ ++ BlockAIOCB *acb = p; ++ acb->refcnt++; ++} ++ ++void qemu_aio_unref(void *p) ++{ ++ BlockAIOCB *acb = p; ++ assert(acb->refcnt > 0); ++ if (--acb->refcnt == 0) { ++ g_free(acb); ++ } ++} +diff --git a/qcow2/lib/util/async.c b/qcow2/lib/util/async.c +new file mode 100644 +index 00000000..3e3e4fc7 +--- /dev/null ++++ b/qcow2/lib/util/async.c +@@ -0,0 +1,760 @@ ++/* ++ * Data plane event loop ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2009-2017 QEMU contributors ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "block/aio.h" ++#include "block/thread-pool.h" ++#include "block/graph-lock.h" ++#include "qemu/main-loop.h" ++#include "qemu/atomic.h" ++#include "qemu/rcu_queue.h" ++#include "block/raw-aio.h" ++#include "qemu/coroutine_int.h" ++#include "qemu/coroutine-tls.h" ++#include "sysemu/cpu-timers.h" ++#include "trace.h" ++ ++/***********************************************************/ ++/* bottom halves (can be seen as timers which expire ASAP) */ ++ ++/* QEMUBH::flags values */ ++enum { ++ /* Already enqueued and waiting for aio_bh_poll() */ ++ BH_PENDING = (1 << 0), ++ ++ /* Invoke the callback */ ++ BH_SCHEDULED = (1 << 1), ++ ++ /* Delete without invoking callback */ ++ BH_DELETED = (1 << 2), ++ ++ /* Delete after invoking callback */ ++ BH_ONESHOT = (1 << 3), ++ ++ /* Schedule periodically when the event loop is idle */ ++ BH_IDLE = (1 << 4), ++}; ++ ++struct QEMUBH { ++ AioContext *ctx; ++ const char *name; ++ QEMUBHFunc *cb; ++ void *opaque; ++ QSLIST_ENTRY(QEMUBH) next; ++ unsigned flags; ++ MemReentrancyGuard *reentrancy_guard; ++}; ++ ++/* Called concurrently from any thread */ ++static void aio_bh_enqueue(QEMUBH *bh, unsigned new_flags) ++{ ++ AioContext *ctx = bh->ctx; ++ unsigned old_flags; ++ ++ /* ++ * Synchronizes with atomic_fetch_and() in aio_bh_dequeue(), ensuring that ++ * insertion starts after BH_PENDING is set. ++ */ ++ old_flags = qatomic_fetch_or(&bh->flags, BH_PENDING | new_flags); ++ ++ if (!(old_flags & BH_PENDING)) { ++ /* ++ * At this point the bottom half becomes visible to aio_bh_poll(). ++ * This insertion thus synchronizes with QSLIST_MOVE_ATOMIC in ++ * aio_bh_poll(), ensuring that: ++ * 1. any writes needed by the callback are visible from the callback ++ * after aio_bh_dequeue() returns bh. ++ * 2. ctx is loaded before the callback has a chance to execute and bh ++ * could be freed. ++ */ ++ QSLIST_INSERT_HEAD_ATOMIC(&ctx->bh_list, bh, next); ++ } ++ ++ aio_notify(ctx); ++ if (unlikely(icount_enabled())) { ++ /* ++ * Workaround for record/replay. ++ * vCPU execution should be suspended when new BH is set. ++ * This is needed to avoid guest timeouts caused ++ * by the long cycles of the execution. ++ */ ++ icount_notify_exit(); ++ } ++} ++ ++/* Only called from aio_bh_poll() and aio_ctx_finalize() */ ++static QEMUBH *aio_bh_dequeue(BHList *head, unsigned *flags) ++{ ++ QEMUBH *bh = QSLIST_FIRST_RCU(head); ++ ++ if (!bh) { ++ return NULL; ++ } ++ ++ QSLIST_REMOVE_HEAD(head, next); ++ ++ /* ++ * Synchronizes with qatomic_fetch_or() in aio_bh_enqueue(), ensuring that ++ * the removal finishes before BH_PENDING is reset. ++ */ ++ *flags = qatomic_fetch_and(&bh->flags, ++ ~(BH_PENDING | BH_SCHEDULED | BH_IDLE)); ++ return bh; ++} ++ ++void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, ++ void *opaque, const char *name) ++{ ++ QEMUBH *bh; ++ bh = g_new(QEMUBH, 1); ++ *bh = (QEMUBH){ ++ .ctx = ctx, ++ .cb = cb, ++ .opaque = opaque, ++ .name = name, ++ }; ++ aio_bh_enqueue(bh, BH_SCHEDULED | BH_ONESHOT); ++} ++ ++QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, ++ const char *name, MemReentrancyGuard *reentrancy_guard) ++{ ++ QEMUBH *bh; ++ bh = g_new(QEMUBH, 1); ++ *bh = (QEMUBH){ ++ .ctx = ctx, ++ .cb = cb, ++ .opaque = opaque, ++ .name = name, ++ .reentrancy_guard = reentrancy_guard, ++ }; ++ return bh; ++} ++ ++void aio_bh_call(QEMUBH *bh) ++{ ++ bool last_engaged_in_io = false; ++ ++ /* Make a copy of the guard-pointer as cb may free the bh */ ++ MemReentrancyGuard *reentrancy_guard = bh->reentrancy_guard; ++ if (reentrancy_guard) { ++ last_engaged_in_io = reentrancy_guard->engaged_in_io; ++ if (reentrancy_guard->engaged_in_io) { ++ trace_reentrant_aio(bh->ctx, bh->name); ++ } ++ reentrancy_guard->engaged_in_io = true; ++ } ++ ++ bh->cb(bh->opaque); ++ ++ if (reentrancy_guard) { ++ reentrancy_guard->engaged_in_io = last_engaged_in_io; ++ } ++} ++ ++/* Multiple occurrences of aio_bh_poll cannot be called concurrently. */ ++int aio_bh_poll(AioContext *ctx) ++{ ++ BHListSlice slice; ++ BHListSlice *s; ++ int ret = 0; ++ ++ /* Synchronizes with QSLIST_INSERT_HEAD_ATOMIC in aio_bh_enqueue(). */ ++ QSLIST_MOVE_ATOMIC(&slice.bh_list, &ctx->bh_list); ++ ++ /* ++ * GCC13 [-Werror=dangling-pointer=] complains that the local variable ++ * 'slice' is being stored in the global 'ctx->bh_slice_list' but the ++ * list is emptied before this function returns. ++ */ ++#if !defined(__clang__) ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wpragmas" ++#pragma GCC diagnostic ignored "-Wdangling-pointer=" ++#endif ++ QSIMPLEQ_INSERT_TAIL(&ctx->bh_slice_list, &slice, next); ++#if !defined(__clang__) ++#pragma GCC diagnostic pop ++#endif ++ ++ while ((s = QSIMPLEQ_FIRST(&ctx->bh_slice_list))) { ++ QEMUBH *bh; ++ unsigned flags; ++ ++ bh = aio_bh_dequeue(&s->bh_list, &flags); ++ if (!bh) { ++ QSIMPLEQ_REMOVE_HEAD(&ctx->bh_slice_list, next); ++ continue; ++ } ++ ++ if ((flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { ++ /* Idle BHs don't count as progress */ ++ if (!(flags & BH_IDLE)) { ++ ret = 1; ++ } ++ aio_bh_call(bh); ++ } ++ if (flags & (BH_DELETED | BH_ONESHOT)) { ++ g_free(bh); ++ } ++ } ++ ++ return ret; ++} ++ ++void qemu_bh_schedule_idle(QEMUBH *bh) ++{ ++ aio_bh_enqueue(bh, BH_SCHEDULED | BH_IDLE); ++} ++ ++void qemu_bh_schedule(QEMUBH *bh) ++{ ++ aio_bh_enqueue(bh, BH_SCHEDULED); ++} ++ ++/* This func is async. ++ */ ++void qemu_bh_cancel(QEMUBH *bh) ++{ ++ qatomic_and(&bh->flags, ~BH_SCHEDULED); ++} ++ ++/* This func is async.The bottom half will do the delete action at the finial ++ * end. ++ */ ++void qemu_bh_delete(QEMUBH *bh) ++{ ++ aio_bh_enqueue(bh, BH_DELETED); ++} ++ ++static int64_t aio_compute_bh_timeout(BHList *head, int timeout) ++{ ++ QEMUBH *bh; ++ ++ QSLIST_FOREACH_RCU(bh, head, next) { ++ if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { ++ if (bh->flags & BH_IDLE) { ++ /* idle bottom halves will be polled at least ++ * every 10ms */ ++ timeout = 10000000; ++ } else { ++ /* non-idle bottom halves will be executed ++ * immediately */ ++ return 0; ++ } ++ } ++ } ++ ++ return timeout; ++} ++ ++int64_t ++aio_compute_timeout(AioContext *ctx) ++{ ++ BHListSlice *s; ++ int64_t deadline; ++ int timeout = -1; ++ ++ timeout = aio_compute_bh_timeout(&ctx->bh_list, timeout); ++ if (timeout == 0) { ++ return 0; ++ } ++ ++ QSIMPLEQ_FOREACH(s, &ctx->bh_slice_list, next) { ++ timeout = aio_compute_bh_timeout(&s->bh_list, timeout); ++ if (timeout == 0) { ++ return 0; ++ } ++ } ++ ++ deadline = timerlistgroup_deadline_ns(&ctx->tlg); ++ if (deadline == 0) { ++ return 0; ++ } else { ++ return qemu_soonest_timeout(timeout, deadline); ++ } ++} ++ ++static gboolean ++aio_ctx_prepare(GSource *source, gint *timeout) ++{ ++ AioContext *ctx = (AioContext *) source; ++ ++ qatomic_set(&ctx->notify_me, qatomic_read(&ctx->notify_me) | 1); ++ ++ /* ++ * Write ctx->notify_me before computing the timeout ++ * (reading bottom half flags, etc.). Pairs with ++ * smp_mb in aio_notify(). ++ */ ++ smp_mb(); ++ ++ /* We assume there is no timeout already supplied */ ++ *timeout = qemu_timeout_ns_to_ms(aio_compute_timeout(ctx)); ++ ++ if (aio_prepare(ctx)) { ++ *timeout = 0; ++ } ++ ++ return *timeout == 0; ++} ++ ++static gboolean ++aio_ctx_check(GSource *source) ++{ ++ AioContext *ctx = (AioContext *) source; ++ QEMUBH *bh; ++ BHListSlice *s; ++ ++ /* Finish computing the timeout before clearing the flag. */ ++ qatomic_store_release(&ctx->notify_me, qatomic_read(&ctx->notify_me) & ~1); ++ aio_notify_accept(ctx); ++ ++ QSLIST_FOREACH_RCU(bh, &ctx->bh_list, next) { ++ if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { ++ return true; ++ } ++ } ++ ++ QSIMPLEQ_FOREACH(s, &ctx->bh_slice_list, next) { ++ QSLIST_FOREACH_RCU(bh, &s->bh_list, next) { ++ if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { ++ return true; ++ } ++ } ++ } ++ return aio_pending(ctx) || (timerlistgroup_deadline_ns(&ctx->tlg) == 0); ++} ++ ++static gboolean ++aio_ctx_dispatch(GSource *source, ++ GSourceFunc callback, ++ gpointer user_data) ++{ ++ AioContext *ctx = (AioContext *) source; ++ ++ assert(callback == NULL); ++ aio_dispatch(ctx); ++ return true; ++} ++ ++static void ++aio_ctx_finalize(GSource *source) ++{ ++ AioContext *ctx = (AioContext *) source; ++ QEMUBH *bh; ++ unsigned flags; ++ ++ thread_pool_free(ctx->thread_pool); ++ ++#ifdef CONFIG_LINUX_AIO ++ if (ctx->linux_aio) { ++ laio_detach_aio_context(ctx->linux_aio, ctx); ++ laio_cleanup(ctx->linux_aio); ++ ctx->linux_aio = NULL; ++ } ++#endif ++ ++#ifdef CONFIG_LINUX_IO_URING ++ if (ctx->linux_io_uring) { ++ luring_detach_aio_context(ctx->linux_io_uring, ctx); ++ luring_cleanup(ctx->linux_io_uring); ++ ctx->linux_io_uring = NULL; ++ } ++#endif ++ ++ assert(QSLIST_EMPTY(&ctx->scheduled_coroutines)); ++ qemu_bh_delete(ctx->co_schedule_bh); ++ ++ /* There must be no aio_bh_poll() calls going on */ ++ assert(QSIMPLEQ_EMPTY(&ctx->bh_slice_list)); ++ ++ while ((bh = aio_bh_dequeue(&ctx->bh_list, &flags))) { ++ /* ++ * qemu_bh_delete() must have been called on BHs in this AioContext. In ++ * many cases memory leaks, hangs, or inconsistent state occur when a ++ * BH is leaked because something still expects it to run. ++ * ++ * If you hit this, fix the lifecycle of the BH so that ++ * qemu_bh_delete() and any associated cleanup is called before the ++ * AioContext is finalized. ++ */ ++ if (unlikely(!(flags & BH_DELETED))) { ++ fprintf(stderr, "%s: BH '%s' leaked, aborting...\n", ++ __func__, bh->name); ++ abort(); ++ } ++ ++ g_free(bh); ++ } ++ ++ aio_set_event_notifier(ctx, &ctx->notifier, NULL, NULL, NULL); ++ event_notifier_cleanup(&ctx->notifier); ++ qemu_rec_mutex_destroy(&ctx->lock); ++ qemu_lockcnt_destroy(&ctx->list_lock); ++ timerlistgroup_deinit(&ctx->tlg); ++ unregister_aiocontext(ctx); ++ aio_context_destroy(ctx); ++} ++ ++static GSourceFuncs aio_source_funcs = { ++ aio_ctx_prepare, ++ aio_ctx_check, ++ aio_ctx_dispatch, ++ aio_ctx_finalize ++}; ++ ++GSource *aio_get_g_source(AioContext *ctx) ++{ ++ aio_context_use_g_source(ctx); ++ g_source_ref(&ctx->source); ++ return &ctx->source; ++} ++ ++ThreadPool *aio_get_thread_pool(AioContext *ctx) ++{ ++ if (!ctx->thread_pool) { ++ ctx->thread_pool = thread_pool_new(ctx); ++ } ++ return ctx->thread_pool; ++} ++ ++#ifdef CONFIG_LINUX_AIO ++LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp) ++{ ++ if (!ctx->linux_aio) { ++ ctx->linux_aio = laio_init(errp); ++ if (ctx->linux_aio) { ++ laio_attach_aio_context(ctx->linux_aio, ctx); ++ } ++ } ++ return ctx->linux_aio; ++} ++ ++LinuxAioState *aio_get_linux_aio(AioContext *ctx) ++{ ++ assert(ctx->linux_aio); ++ return ctx->linux_aio; ++} ++#endif ++ ++#ifdef CONFIG_LINUX_IO_URING ++LuringState *aio_setup_linux_io_uring(AioContext *ctx, Error **errp) ++{ ++ if (ctx->linux_io_uring) { ++ return ctx->linux_io_uring; ++ } ++ ++ ctx->linux_io_uring = luring_init(errp); ++ if (!ctx->linux_io_uring) { ++ return NULL; ++ } ++ ++ luring_attach_aio_context(ctx->linux_io_uring, ctx); ++ return ctx->linux_io_uring; ++} ++ ++LuringState *aio_get_linux_io_uring(AioContext *ctx) ++{ ++ assert(ctx->linux_io_uring); ++ return ctx->linux_io_uring; ++} ++#endif ++ ++void aio_notify(AioContext *ctx) ++{ ++ /* ++ * Write e.g. ctx->bh_list before writing ctx->notified. Pairs with ++ * smp_mb() in aio_notify_accept(). ++ */ ++ smp_wmb(); ++ qatomic_set(&ctx->notified, true); ++ ++ /* ++ * Write ctx->notified (and also ctx->bh_list) before reading ctx->notify_me. ++ * Pairs with smp_mb() in aio_ctx_prepare or aio_poll. ++ */ ++ smp_mb(); ++ if (qatomic_read(&ctx->notify_me)) { ++ event_notifier_set(&ctx->notifier); ++ } ++} ++ ++void aio_notify_accept(AioContext *ctx) ++{ ++ qatomic_set(&ctx->notified, false); ++ ++ /* ++ * Order reads of ctx->notified (in aio_context_notifier_poll()) and the ++ * above clearing of ctx->notified before reads of e.g. bh->flags. Pairs ++ * with smp_wmb() in aio_notify. ++ */ ++ smp_mb(); ++} ++ ++static void aio_timerlist_notify(void *opaque, QEMUClockType type) ++{ ++ aio_notify(opaque); ++} ++ ++static void aio_context_notifier_cb(EventNotifier *e) ++{ ++ AioContext *ctx = container_of(e, AioContext, notifier); ++ ++ event_notifier_test_and_clear(&ctx->notifier); ++} ++ ++/* Returns true if aio_notify() was called (e.g. a BH was scheduled) */ ++static bool aio_context_notifier_poll(void *opaque) ++{ ++ EventNotifier *e = opaque; ++ AioContext *ctx = container_of(e, AioContext, notifier); ++ ++ /* ++ * No need for load-acquire because we just want to kick the ++ * event loop. aio_notify_accept() takes care of synchronizing ++ * the event loop with the producers. ++ */ ++ return qatomic_read(&ctx->notified); ++} ++ ++static void aio_context_notifier_poll_ready(EventNotifier *e) ++{ ++ /* Do nothing, we just wanted to kick the event loop */ ++} ++ ++static void co_schedule_bh_cb(void *opaque) ++{ ++ AioContext *ctx = opaque; ++ QSLIST_HEAD(, Coroutine) straight, reversed; ++ ++ QSLIST_MOVE_ATOMIC(&reversed, &ctx->scheduled_coroutines); ++ QSLIST_INIT(&straight); ++ ++ while (!QSLIST_EMPTY(&reversed)) { ++ Coroutine *co = QSLIST_FIRST(&reversed); ++ QSLIST_REMOVE_HEAD(&reversed, co_scheduled_next); ++ QSLIST_INSERT_HEAD(&straight, co, co_scheduled_next); ++ } ++ ++ while (!QSLIST_EMPTY(&straight)) { ++ Coroutine *co = QSLIST_FIRST(&straight); ++ QSLIST_REMOVE_HEAD(&straight, co_scheduled_next); ++ trace_aio_co_schedule_bh_cb(ctx, co); ++ ++ /* Protected by write barrier in qemu_aio_coroutine_enter */ ++ qatomic_set(&co->scheduled, NULL); ++ qemu_aio_coroutine_enter(ctx, co); ++ } ++} ++ ++AioContext *aio_context_new(Error **errp) ++{ ++ int ret; ++ AioContext *ctx; ++ ++ ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext)); ++ QSLIST_INIT(&ctx->bh_list); ++ QSIMPLEQ_INIT(&ctx->bh_slice_list); ++ aio_context_setup(ctx); ++ ++ ret = event_notifier_init(&ctx->notifier, false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to initialize event notifier"); ++ goto fail; ++ } ++ g_source_set_can_recurse(&ctx->source, true); ++ qemu_lockcnt_init(&ctx->list_lock); ++ ++ ctx->co_schedule_bh = aio_bh_new(ctx, co_schedule_bh_cb, ctx); ++ QSLIST_INIT(&ctx->scheduled_coroutines); ++ ++ aio_set_event_notifier(ctx, &ctx->notifier, ++ aio_context_notifier_cb, ++ aio_context_notifier_poll, ++ aio_context_notifier_poll_ready); ++#ifdef CONFIG_LINUX_AIO ++ ctx->linux_aio = NULL; ++#endif ++ ++#ifdef CONFIG_LINUX_IO_URING ++ ctx->linux_io_uring = NULL; ++#endif ++ ++ ctx->thread_pool = NULL; ++ qemu_rec_mutex_init(&ctx->lock); ++ timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx); ++ ++ ctx->poll_ns = 0; ++ ctx->poll_max_ns = 0; ++ ctx->poll_grow = 0; ++ ctx->poll_shrink = 0; ++ ++ ctx->aio_max_batch = 0; ++ ++ ctx->thread_pool_min = 0; ++ ctx->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT; ++ ++ register_aiocontext(ctx); ++ ++ return ctx; ++fail: ++ g_source_destroy(&ctx->source); ++ return NULL; ++} ++ ++void aio_co_schedule(AioContext *ctx, Coroutine *co) ++{ ++ trace_aio_co_schedule(ctx, co); ++ const char *scheduled = qatomic_cmpxchg(&co->scheduled, NULL, ++ __func__); ++ ++ if (scheduled) { ++ fprintf(stderr, ++ "%s: Co-routine was already scheduled in '%s'\n", ++ __func__, scheduled); ++ abort(); ++ } ++ ++ /* The coroutine might run and release the last ctx reference before we ++ * invoke qemu_bh_schedule(). Take a reference to keep ctx alive until ++ * we're done. ++ */ ++ aio_context_ref(ctx); ++ ++ QSLIST_INSERT_HEAD_ATOMIC(&ctx->scheduled_coroutines, ++ co, co_scheduled_next); ++ qemu_bh_schedule(ctx->co_schedule_bh); ++ ++ aio_context_unref(ctx); ++} ++ ++typedef struct AioCoRescheduleSelf { ++ Coroutine *co; ++ AioContext *new_ctx; ++} AioCoRescheduleSelf; ++ ++static void aio_co_reschedule_self_bh(void *opaque) ++{ ++ AioCoRescheduleSelf *data = opaque; ++ aio_co_schedule(data->new_ctx, data->co); ++} ++ ++void coroutine_fn aio_co_reschedule_self(AioContext *new_ctx) ++{ ++ AioContext *old_ctx = qemu_get_current_aio_context(); ++ ++ if (old_ctx != new_ctx) { ++ AioCoRescheduleSelf data = { ++ .co = qemu_coroutine_self(), ++ .new_ctx = new_ctx, ++ }; ++ /* ++ * We can't directly schedule the coroutine in the target context ++ * because this would be racy: The other thread could try to enter the ++ * coroutine before it has yielded in this one. ++ */ ++ aio_bh_schedule_oneshot(old_ctx, aio_co_reschedule_self_bh, &data); ++ qemu_coroutine_yield(); ++ } ++} ++ ++void aio_co_wake(Coroutine *co) ++{ ++ AioContext *ctx; ++ ++ /* Read coroutine before co->ctx. Matches smp_wmb in ++ * qemu_coroutine_enter. ++ */ ++ smp_read_barrier_depends(); ++ ctx = qatomic_read(&co->ctx); ++ ++ aio_co_enter(ctx, co); ++} ++ ++void aio_co_enter(AioContext *ctx, Coroutine *co) ++{ ++ if (ctx != qemu_get_current_aio_context()) { ++ aio_co_schedule(ctx, co); ++ return; ++ } ++ ++ if (qemu_in_coroutine()) { ++ Coroutine *self = qemu_coroutine_self(); ++ assert(self != co); ++ QSIMPLEQ_INSERT_TAIL(&self->co_queue_wakeup, co, co_queue_next); ++ } else { ++ qemu_aio_coroutine_enter(ctx, co); ++ } ++} ++ ++void aio_context_ref(AioContext *ctx) ++{ ++ g_source_ref(&ctx->source); ++} ++ ++void aio_context_unref(AioContext *ctx) ++{ ++ g_source_unref(&ctx->source); ++} ++ ++QEMU_DEFINE_STATIC_CO_TLS(AioContext *, my_aiocontext) ++ ++AioContext *qemu_get_current_aio_context(void) ++{ ++ AioContext *ctx = get_my_aiocontext(); ++ if (ctx) { ++ return ctx; ++ } ++ if (bql_locked()) { ++ /* Possibly in a vCPU thread. */ ++ return qemu_get_aio_context(); ++ } ++ return NULL; ++} ++ ++void qemu_set_current_aio_context(AioContext *ctx) ++{ ++ assert(!get_my_aiocontext()); ++ set_my_aiocontext(ctx); ++} ++ ++void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min, ++ int64_t max, Error **errp) ++{ ++ ++ if (min > max || max <= 0 || min < 0 || min > INT_MAX || max > INT_MAX) { ++ error_setg(errp, "bad thread-pool-min/thread-pool-max values"); ++ return; ++ } ++ ++ ctx->thread_pool_min = min; ++ ctx->thread_pool_max = max; ++ ++ if (ctx->thread_pool) { ++ thread_pool_update_params(ctx->thread_pool, ctx); ++ } ++} +diff --git a/qcow2/lib/util/bitmap.c b/qcow2/lib/util/bitmap.c +new file mode 100644 +index 00000000..8d12e90a +--- /dev/null ++++ b/qcow2/lib/util/bitmap.c +@@ -0,0 +1,534 @@ ++/* ++ * Bitmap Module ++ * ++ * Stolen from linux/src/lib/bitmap.c ++ * ++ * Copyright (C) 2010 Corentin Chary ++ * ++ * This source code is licensed under the GNU General Public License, ++ * Version 2. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/bitops.h" ++#include "qemu/bitmap.h" ++#include "qemu/atomic.h" ++ ++/* ++ * bitmaps provide an array of bits, implemented using an ++ * array of unsigned longs. The number of valid bits in a ++ * given bitmap does _not_ need to be an exact multiple of ++ * BITS_PER_LONG. ++ * ++ * The possible unused bits in the last, partially used word ++ * of a bitmap are 'don't care'. The implementation makes ++ * no particular effort to keep them zero. It ensures that ++ * their value will not affect the results of any operation. ++ * The bitmap operations that return Boolean (bitmap_empty, ++ * for example) or scalar (bitmap_weight, for example) results ++ * carefully filter out these unused bits from impacting their ++ * results. ++ * ++ * These operations actually hold to a slightly stronger rule: ++ * if you don't input any bitmaps to these ops that have some ++ * unused bits set, then they won't output any set unused bits ++ * in output bitmaps. ++ * ++ * The byte ordering of bitmaps is more natural on little ++ * endian architectures. ++ */ ++ ++int slow_bitmap_empty(const unsigned long *bitmap, long bits) ++{ ++ long k, lim = bits/BITS_PER_LONG; ++ ++ for (k = 0; k < lim; ++k) { ++ if (bitmap[k]) { ++ return 0; ++ } ++ } ++ if (bits % BITS_PER_LONG) { ++ if (bitmap[k] & BITMAP_LAST_WORD_MASK(bits)) { ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++int slow_bitmap_full(const unsigned long *bitmap, long bits) ++{ ++ long k, lim = bits/BITS_PER_LONG; ++ ++ for (k = 0; k < lim; ++k) { ++ if (~bitmap[k]) { ++ return 0; ++ } ++ } ++ ++ if (bits % BITS_PER_LONG) { ++ if (~bitmap[k] & BITMAP_LAST_WORD_MASK(bits)) { ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++int slow_bitmap_equal(const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits) ++{ ++ long k, lim = bits/BITS_PER_LONG; ++ ++ for (k = 0; k < lim; ++k) { ++ if (bitmap1[k] != bitmap2[k]) { ++ return 0; ++ } ++ } ++ ++ if (bits % BITS_PER_LONG) { ++ if ((bitmap1[k] ^ bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits)) { ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++void slow_bitmap_complement(unsigned long *dst, const unsigned long *src, ++ long bits) ++{ ++ long k, lim = bits/BITS_PER_LONG; ++ ++ for (k = 0; k < lim; ++k) { ++ dst[k] = ~src[k]; ++ } ++ ++ if (bits % BITS_PER_LONG) { ++ dst[k] = ~src[k] & BITMAP_LAST_WORD_MASK(bits); ++ } ++} ++ ++int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits) ++{ ++ long k; ++ long nr = BITS_TO_LONGS(bits); ++ unsigned long result = 0; ++ ++ for (k = 0; k < nr; k++) { ++ result |= (dst[k] = bitmap1[k] & bitmap2[k]); ++ } ++ return result != 0; ++} ++ ++void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits) ++{ ++ long k; ++ long nr = BITS_TO_LONGS(bits); ++ ++ for (k = 0; k < nr; k++) { ++ dst[k] = bitmap1[k] | bitmap2[k]; ++ } ++} ++ ++void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits) ++{ ++ long k; ++ long nr = BITS_TO_LONGS(bits); ++ ++ for (k = 0; k < nr; k++) { ++ dst[k] = bitmap1[k] ^ bitmap2[k]; ++ } ++} ++ ++int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits) ++{ ++ long k; ++ long nr = BITS_TO_LONGS(bits); ++ unsigned long result = 0; ++ ++ for (k = 0; k < nr; k++) { ++ result |= (dst[k] = bitmap1[k] & ~bitmap2[k]); ++ } ++ return result != 0; ++} ++ ++void bitmap_set(unsigned long *map, long start, long nr) ++{ ++ unsigned long *p = map + BIT_WORD(start); ++ const long size = start + nr; ++ int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG); ++ unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start); ++ ++ assert(start >= 0 && nr >= 0); ++ ++ while (nr - bits_to_set >= 0) { ++ *p |= mask_to_set; ++ nr -= bits_to_set; ++ bits_to_set = BITS_PER_LONG; ++ mask_to_set = ~0UL; ++ p++; ++ } ++ if (nr) { ++ mask_to_set &= BITMAP_LAST_WORD_MASK(size); ++ *p |= mask_to_set; ++ } ++} ++ ++void bitmap_set_atomic(unsigned long *map, long start, long nr) ++{ ++ unsigned long *p = map + BIT_WORD(start); ++ const long size = start + nr; ++ int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG); ++ unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start); ++ ++ assert(start >= 0 && nr >= 0); ++ ++ /* First word */ ++ if (nr - bits_to_set > 0) { ++ qatomic_or(p, mask_to_set); ++ nr -= bits_to_set; ++ bits_to_set = BITS_PER_LONG; ++ mask_to_set = ~0UL; ++ p++; ++ } ++ ++ /* Full words */ ++ if (bits_to_set == BITS_PER_LONG) { ++ while (nr >= BITS_PER_LONG) { ++ *p = ~0UL; ++ nr -= BITS_PER_LONG; ++ p++; ++ } ++ } ++ ++ /* Last word */ ++ if (nr) { ++ mask_to_set &= BITMAP_LAST_WORD_MASK(size); ++ qatomic_or(p, mask_to_set); ++ } else { ++ /* If we avoided the full barrier in qatomic_or(), issue a ++ * barrier to account for the assignments in the while loop. ++ */ ++ smp_mb(); ++ } ++} ++ ++void bitmap_clear(unsigned long *map, long start, long nr) ++{ ++ unsigned long *p = map + BIT_WORD(start); ++ const long size = start + nr; ++ int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); ++ unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); ++ ++ assert(start >= 0 && nr >= 0); ++ ++ while (nr - bits_to_clear >= 0) { ++ *p &= ~mask_to_clear; ++ nr -= bits_to_clear; ++ bits_to_clear = BITS_PER_LONG; ++ mask_to_clear = ~0UL; ++ p++; ++ } ++ if (nr) { ++ mask_to_clear &= BITMAP_LAST_WORD_MASK(size); ++ *p &= ~mask_to_clear; ++ } ++} ++ ++bool bitmap_test_and_clear(unsigned long *map, long start, long nr) ++{ ++ unsigned long *p = map + BIT_WORD(start); ++ const long size = start + nr; ++ int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); ++ unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); ++ bool dirty = false; ++ ++ assert(start >= 0 && nr >= 0); ++ ++ /* First word */ ++ if (nr - bits_to_clear > 0) { ++ if ((*p) & mask_to_clear) { ++ dirty = true; ++ } ++ *p &= ~mask_to_clear; ++ nr -= bits_to_clear; ++ bits_to_clear = BITS_PER_LONG; ++ p++; ++ } ++ ++ /* Full words */ ++ if (bits_to_clear == BITS_PER_LONG) { ++ while (nr >= BITS_PER_LONG) { ++ if (*p) { ++ dirty = true; ++ *p = 0; ++ } ++ nr -= BITS_PER_LONG; ++ p++; ++ } ++ } ++ ++ /* Last word */ ++ if (nr) { ++ mask_to_clear &= BITMAP_LAST_WORD_MASK(size); ++ if ((*p) & mask_to_clear) { ++ dirty = true; ++ } ++ *p &= ~mask_to_clear; ++ } ++ ++ return dirty; ++} ++ ++bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr) ++{ ++ unsigned long *p = map + BIT_WORD(start); ++ const long size = start + nr; ++ int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); ++ unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); ++ unsigned long dirty = 0; ++ unsigned long old_bits; ++ ++ assert(start >= 0 && nr >= 0); ++ ++ /* First word */ ++ if (nr - bits_to_clear > 0) { ++ old_bits = qatomic_fetch_and(p, ~mask_to_clear); ++ dirty |= old_bits & mask_to_clear; ++ nr -= bits_to_clear; ++ bits_to_clear = BITS_PER_LONG; ++ mask_to_clear = ~0UL; ++ p++; ++ } ++ ++ /* Full words */ ++ if (bits_to_clear == BITS_PER_LONG) { ++ while (nr >= BITS_PER_LONG) { ++ if (*p) { ++ old_bits = qatomic_xchg(p, 0); ++ dirty |= old_bits; ++ } ++ nr -= BITS_PER_LONG; ++ p++; ++ } ++ } ++ ++ /* Last word */ ++ if (nr) { ++ mask_to_clear &= BITMAP_LAST_WORD_MASK(size); ++ old_bits = qatomic_fetch_and(p, ~mask_to_clear); ++ dirty |= old_bits & mask_to_clear; ++ } else { ++ if (!dirty) { ++ smp_mb(); ++ } ++ } ++ ++ return dirty != 0; ++} ++ ++void bitmap_copy_and_clear_atomic(unsigned long *dst, unsigned long *src, ++ long nr) ++{ ++ while (nr > 0) { ++ *dst = qatomic_xchg(src, 0); ++ dst++; ++ src++; ++ nr -= BITS_PER_LONG; ++ } ++} ++ ++#define ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) ++ ++/** ++ * bitmap_find_next_zero_area - find a contiguous aligned zero area ++ * @map: The address to base the search on ++ * @size: The bitmap size in bits ++ * @start: The bitnumber to start searching at ++ * @nr: The number of zeroed bits we're looking for ++ * @align_mask: Alignment mask for zero area ++ * ++ * The @align_mask should be one less than a power of 2; the effect is that ++ * the bit offset of all zero areas this function finds is multiples of that ++ * power of 2. A @align_mask of 0 means no alignment is required. ++ */ ++unsigned long bitmap_find_next_zero_area(unsigned long *map, ++ unsigned long size, ++ unsigned long start, ++ unsigned long nr, ++ unsigned long align_mask) ++{ ++ unsigned long index, end, i; ++again: ++ index = find_next_zero_bit(map, size, start); ++ ++ /* Align allocation */ ++ index = ALIGN_MASK(index, align_mask); ++ ++ end = index + nr; ++ if (end > size) { ++ return end; ++ } ++ i = find_next_bit(map, end, index); ++ if (i < end) { ++ start = i + 1; ++ goto again; ++ } ++ return index; ++} ++ ++int slow_bitmap_intersects(const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits) ++{ ++ long k, lim = bits/BITS_PER_LONG; ++ ++ for (k = 0; k < lim; ++k) { ++ if (bitmap1[k] & bitmap2[k]) { ++ return 1; ++ } ++ } ++ ++ if (bits % BITS_PER_LONG) { ++ if ((bitmap1[k] & bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits)) { ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++long slow_bitmap_count_one(const unsigned long *bitmap, long nbits) ++{ ++ long k, lim = nbits / BITS_PER_LONG, result = 0; ++ ++ for (k = 0; k < lim; k++) { ++ result += ctpopl(bitmap[k]); ++ } ++ ++ if (nbits % BITS_PER_LONG) { ++ result += ctpopl(bitmap[k] & BITMAP_LAST_WORD_MASK(nbits)); ++ } ++ ++ return result; ++} ++ ++static void bitmap_to_from_le(unsigned long *dst, ++ const unsigned long *src, long nbits) ++{ ++ long len = BITS_TO_LONGS(nbits); ++ ++#if HOST_BIG_ENDIAN ++ long index; ++ ++ for (index = 0; index < len; index++) { ++# if HOST_LONG_BITS == 64 ++ dst[index] = bswap64(src[index]); ++# else ++ dst[index] = bswap32(src[index]); ++# endif ++ } ++#else ++ memcpy(dst, src, len * sizeof(unsigned long)); ++#endif ++} ++ ++void bitmap_from_le(unsigned long *dst, const unsigned long *src, ++ long nbits) ++{ ++ bitmap_to_from_le(dst, src, nbits); ++} ++ ++void bitmap_to_le(unsigned long *dst, const unsigned long *src, ++ long nbits) ++{ ++ bitmap_to_from_le(dst, src, nbits); ++} ++ ++/* ++ * Copy "src" bitmap with a positive offset and put it into the "dst" ++ * bitmap. The caller needs to make sure the bitmap size of "src" ++ * is bigger than (shift + nbits). ++ */ ++void bitmap_copy_with_src_offset(unsigned long *dst, const unsigned long *src, ++ unsigned long shift, unsigned long nbits) ++{ ++ unsigned long left_mask, right_mask, last_mask; ++ ++ /* Proper shift src pointer to the first word to copy from */ ++ src += BIT_WORD(shift); ++ shift %= BITS_PER_LONG; ++ ++ if (!shift) { ++ /* Fast path */ ++ bitmap_copy(dst, src, nbits); ++ return; ++ } ++ ++ right_mask = (1ul << shift) - 1; ++ left_mask = ~right_mask; ++ ++ while (nbits >= BITS_PER_LONG) { ++ *dst = (*src & left_mask) >> shift; ++ *dst |= (src[1] & right_mask) << (BITS_PER_LONG - shift); ++ dst++; ++ src++; ++ nbits -= BITS_PER_LONG; ++ } ++ ++ if (nbits > BITS_PER_LONG - shift) { ++ *dst = (*src & left_mask) >> shift; ++ nbits -= BITS_PER_LONG - shift; ++ last_mask = (1ul << nbits) - 1; ++ *dst |= (src[1] & last_mask) << (BITS_PER_LONG - shift); ++ } else if (nbits) { ++ last_mask = (1ul << nbits) - 1; ++ *dst = (*src >> shift) & last_mask; ++ } ++} ++ ++/* ++ * Copy "src" bitmap into the "dst" bitmap with an offset in the ++ * "dst". The caller needs to make sure the bitmap size of "dst" is ++ * bigger than (shift + nbits). ++ */ ++void bitmap_copy_with_dst_offset(unsigned long *dst, const unsigned long *src, ++ unsigned long shift, unsigned long nbits) ++{ ++ unsigned long left_mask, right_mask, last_mask; ++ ++ /* Proper shift dst pointer to the first word to copy from */ ++ dst += BIT_WORD(shift); ++ shift %= BITS_PER_LONG; ++ ++ if (!shift) { ++ /* Fast path */ ++ bitmap_copy(dst, src, nbits); ++ return; ++ } ++ ++ right_mask = (1ul << (BITS_PER_LONG - shift)) - 1; ++ left_mask = ~right_mask; ++ ++ *dst &= (1ul << shift) - 1; ++ while (nbits >= BITS_PER_LONG) { ++ *dst |= (*src & right_mask) << shift; ++ dst[1] = (*src & left_mask) >> (BITS_PER_LONG - shift); ++ dst++; ++ src++; ++ nbits -= BITS_PER_LONG; ++ } ++ ++ if (nbits > BITS_PER_LONG - shift) { ++ *dst |= (*src & right_mask) << shift; ++ nbits -= BITS_PER_LONG - shift; ++ last_mask = ((1ul << nbits) - 1) << (BITS_PER_LONG - shift); ++ dst[1] = (*src & last_mask) >> (BITS_PER_LONG - shift); ++ } else if (nbits) { ++ last_mask = (1ul << nbits) - 1; ++ *dst |= (*src & last_mask) << shift; ++ } ++} +diff --git a/qcow2/lib/util/bitops.c b/qcow2/lib/util/bitops.c +new file mode 100644 +index 00000000..4b647b3e +--- /dev/null ++++ b/qcow2/lib/util/bitops.c +@@ -0,0 +1,157 @@ ++/* ++ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. ++ * Written by David Howells (dhowells@redhat.com) ++ * Copyright (C) 2008 IBM Corporation ++ * Written by Rusty Russell ++ * (Inspired by David Howell's find_next_bit implementation) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/bitops.h" ++ ++/* ++ * Find the next set bit in a memory region. ++ */ ++unsigned long find_next_bit(const unsigned long *addr, unsigned long size, ++ unsigned long offset) ++{ ++ const unsigned long *p = addr + BIT_WORD(offset); ++ unsigned long result = offset & ~(BITS_PER_LONG-1); ++ unsigned long tmp; ++ ++ if (offset >= size) { ++ return size; ++ } ++ size -= result; ++ offset %= BITS_PER_LONG; ++ if (offset) { ++ tmp = *(p++); ++ tmp &= (~0UL << offset); ++ if (size < BITS_PER_LONG) { ++ goto found_first; ++ } ++ if (tmp) { ++ goto found_middle; ++ } ++ size -= BITS_PER_LONG; ++ result += BITS_PER_LONG; ++ } ++ while (size >= 4*BITS_PER_LONG) { ++ unsigned long d1, d2, d3; ++ tmp = *p; ++ d1 = *(p+1); ++ d2 = *(p+2); ++ d3 = *(p+3); ++ if (tmp) { ++ goto found_middle; ++ } ++ if (d1 | d2 | d3) { ++ break; ++ } ++ p += 4; ++ result += 4*BITS_PER_LONG; ++ size -= 4*BITS_PER_LONG; ++ } ++ while (size >= BITS_PER_LONG) { ++ if ((tmp = *(p++))) { ++ goto found_middle; ++ } ++ result += BITS_PER_LONG; ++ size -= BITS_PER_LONG; ++ } ++ if (!size) { ++ return result; ++ } ++ tmp = *p; ++ ++found_first: ++ tmp &= (~0UL >> (BITS_PER_LONG - size)); ++ if (tmp == 0UL) { /* Are any bits set? */ ++ return result + size; /* Nope. */ ++ } ++found_middle: ++ return result + ctzl(tmp); ++} ++ ++/* ++ * This implementation of find_{first,next}_zero_bit was stolen from ++ * Linus' asm-alpha/bitops.h. ++ */ ++unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, ++ unsigned long offset) ++{ ++ const unsigned long *p = addr + BIT_WORD(offset); ++ unsigned long result = offset & ~(BITS_PER_LONG-1); ++ unsigned long tmp; ++ ++ if (offset >= size) { ++ return size; ++ } ++ size -= result; ++ offset %= BITS_PER_LONG; ++ if (offset) { ++ tmp = *(p++); ++ tmp |= ~0UL >> (BITS_PER_LONG - offset); ++ if (size < BITS_PER_LONG) { ++ goto found_first; ++ } ++ if (~tmp) { ++ goto found_middle; ++ } ++ size -= BITS_PER_LONG; ++ result += BITS_PER_LONG; ++ } ++ while (size & ~(BITS_PER_LONG-1)) { ++ if (~(tmp = *(p++))) { ++ goto found_middle; ++ } ++ result += BITS_PER_LONG; ++ size -= BITS_PER_LONG; ++ } ++ if (!size) { ++ return result; ++ } ++ tmp = *p; ++ ++found_first: ++ tmp |= ~0UL << size; ++ if (tmp == ~0UL) { /* Are any bits zero? */ ++ return result + size; /* Nope. */ ++ } ++found_middle: ++ return result + ctzl(~tmp); ++} ++ ++unsigned long find_last_bit(const unsigned long *addr, unsigned long size) ++{ ++ unsigned long words; ++ unsigned long tmp; ++ ++ /* Start at final word. */ ++ words = size / BITS_PER_LONG; ++ ++ /* Partial final word? */ ++ if (size & (BITS_PER_LONG-1)) { ++ tmp = (addr[words] & (~0UL >> (BITS_PER_LONG ++ - (size & (BITS_PER_LONG-1))))); ++ if (tmp) { ++ goto found; ++ } ++ } ++ ++ while (words) { ++ tmp = addr[--words]; ++ if (tmp) { ++ found: ++ return words * BITS_PER_LONG + BITS_PER_LONG - 1 - clzl(tmp); ++ } ++ } ++ ++ /* Not found */ ++ return size; ++} +diff --git a/qcow2/lib/util/bufferiszero.c b/qcow2/lib/util/bufferiszero.c +new file mode 100644 +index 00000000..522146da +--- /dev/null ++++ b/qcow2/lib/util/bufferiszero.c +@@ -0,0 +1,126 @@ ++/* ++ * Simple C functions to supplement the C library ++ * ++ * Copyright (c) 2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++#include "qemu/bswap.h" ++#include "host/cpuinfo.h" ++ ++typedef bool (*biz_accel_fn)(const void *, size_t); ++ ++static bool buffer_is_zero_int_lt256(const void *buf, size_t len) ++{ ++ uint64_t t; ++ const uint64_t *p, *e; ++ ++ /* ++ * Use unaligned memory access functions to handle ++ * the beginning and end of the buffer. ++ */ ++ if (unlikely(len <= 8)) { ++ return (ldl_he_p(buf) | ldl_he_p(buf + len - 4)) == 0; ++ } ++ ++ t = ldq_he_p(buf) | ldq_he_p(buf + len - 8); ++ p = QEMU_ALIGN_PTR_DOWN(buf + 8, 8); ++ e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 8); ++ ++ /* Read 0 to 31 aligned words from the middle. */ ++ while (p < e) { ++ t |= *p++; ++ } ++ return t == 0; ++} ++ ++static bool buffer_is_zero_int_ge256(const void *buf, size_t len) ++{ ++ /* ++ * Use unaligned memory access functions to handle ++ * the beginning and end of the buffer. ++ */ ++ uint64_t t = ldq_he_p(buf) | ldq_he_p(buf + len - 8); ++ const uint64_t *p = QEMU_ALIGN_PTR_DOWN(buf + 8, 8); ++ const uint64_t *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 8); ++ ++ /* Collect a partial block at the tail end. */ ++ t |= e[-7] | e[-6] | e[-5] | e[-4] | e[-3] | e[-2] | e[-1]; ++ ++ /* ++ * Loop over 64 byte blocks. ++ * With the head and tail removed, e - p >= 30, ++ * so the loop must iterate at least 3 times. ++ */ ++ do { ++ if (t) { ++ return false; ++ } ++ t = p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7]; ++ p += 8; ++ } while (p < e - 7); ++ ++ return t == 0; ++} ++ ++#include "host/bufferiszero.c.inc" ++ ++static biz_accel_fn buffer_is_zero_accel; ++static unsigned accel_index; ++ ++bool buffer_is_zero_ool(const void *buf, size_t len) ++{ ++ if (unlikely(len == 0)) { ++ return true; ++ } ++ if (!buffer_is_zero_sample3(buf, len)) { ++ return false; ++ } ++ /* All bytes are covered for any len <= 3. */ ++ if (unlikely(len <= 3)) { ++ return true; ++ } ++ ++ if (likely(len >= 256)) { ++ return buffer_is_zero_accel(buf, len); ++ } ++ return buffer_is_zero_int_lt256(buf, len); ++} ++ ++bool buffer_is_zero_ge256(const void *buf, size_t len) ++{ ++ return buffer_is_zero_accel(buf, len); ++} ++ ++bool test_buffer_is_zero_next_accel(void) ++{ ++ if (accel_index != 0) { ++ buffer_is_zero_accel = accel_table[--accel_index]; ++ return true; ++ } ++ return false; ++} ++ ++static void __attribute__((constructor)) init_accel(void) ++{ ++ accel_index = best_accel(); ++ buffer_is_zero_accel = accel_table[accel_index]; ++} +diff --git a/qcow2/lib/util/compatfd.c b/qcow2/lib/util/compatfd.c +new file mode 100644 +index 00000000..147e39e2 +--- /dev/null ++++ b/qcow2/lib/util/compatfd.c +@@ -0,0 +1,89 @@ ++/* ++ * signalfd/eventfd compatibility ++ * ++ * Copyright IBM, Corp. 2008 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/thread.h" ++ ++#if defined(CONFIG_SIGNALFD) ++#include ++#endif ++ ++struct sigfd_compat_info { ++ sigset_t mask; ++ int fd; ++}; ++ ++static void *sigwait_compat(void *opaque) ++{ ++ struct sigfd_compat_info *info = opaque; ++ ++ while (1) { ++ int sig; ++ int err; ++ ++ err = sigwait(&info->mask, &sig); ++ if (err != 0) { ++ if (errno == EINTR) { ++ continue; ++ } else { ++ return NULL; ++ } ++ } else { ++ struct qemu_signalfd_siginfo buffer; ++ memset(&buffer, 0, sizeof(buffer)); ++ buffer.ssi_signo = sig; ++ ++ if (qemu_write_full(info->fd, &buffer, sizeof(buffer)) != sizeof(buffer)) { ++ return NULL; ++ } ++ } ++ } ++} ++ ++static int qemu_signalfd_compat(const sigset_t *mask) ++{ ++ struct sigfd_compat_info *info; ++ QemuThread thread; ++ int fds[2]; ++ ++ info = g_malloc(sizeof(*info)); ++ ++ if (!g_unix_open_pipe(fds, FD_CLOEXEC, NULL)) { ++ g_free(info); ++ return -1; ++ } ++ ++ memcpy(&info->mask, mask, sizeof(*mask)); ++ info->fd = fds[1]; ++ ++ qemu_thread_create(&thread, "signalfd_compat", sigwait_compat, info, ++ QEMU_THREAD_DETACHED); ++ ++ return fds[0]; ++} ++ ++int qemu_signalfd(const sigset_t *mask) ++{ ++#if defined(CONFIG_SIGNALFD) ++ int ret; ++ ++ ret = signalfd(-1, mask, SFD_CLOEXEC); ++ if (ret != -1) { ++ return ret; ++ } ++#endif ++ ++ return qemu_signalfd_compat(mask); ++} +diff --git a/qcow2/lib/util/coroutine-ucontext.c b/qcow2/lib/util/coroutine-ucontext.c +new file mode 100644 +index 00000000..8ef603d0 +--- /dev/null ++++ b/qcow2/lib/util/coroutine-ucontext.c +@@ -0,0 +1,359 @@ ++/* ++ * ucontext coroutine initialization code ++ * ++ * Copyright (C) 2006 Anthony Liguori ++ * Copyright (C) 2011 Kevin Wolf ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.0 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ */ ++ ++/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */ ++#undef _FORTIFY_SOURCE ++#define _FORTIFY_SOURCE 0 ++ ++#include "qemu/osdep.h" ++#include ++#include "qemu/coroutine_int.h" ++#include "qemu/coroutine-tls.h" ++ ++#ifdef CONFIG_VALGRIND_H ++#include ++#endif ++ ++#ifdef QEMU_SANITIZE_ADDRESS ++#ifdef CONFIG_ASAN_IFACE_FIBER ++#define CONFIG_ASAN 1 ++#include ++#endif ++#endif ++ ++#ifdef CONFIG_TSAN ++#include ++#endif ++ ++typedef struct { ++ Coroutine base; ++ void *stack; ++ size_t stack_size; ++#ifdef CONFIG_SAFESTACK ++ /* Need an unsafe stack for each coroutine */ ++ void *unsafe_stack; ++ size_t unsafe_stack_size; ++#endif ++ sigjmp_buf env; ++ ++#ifdef CONFIG_TSAN ++ void *tsan_co_fiber; ++ void *tsan_caller_fiber; ++#endif ++ ++#ifdef CONFIG_VALGRIND_H ++ unsigned int valgrind_stack_id; ++#endif ++ ++} CoroutineUContext; ++ ++/** ++ * Per-thread coroutine bookkeeping ++ */ ++QEMU_DEFINE_STATIC_CO_TLS(Coroutine *, current); ++QEMU_DEFINE_STATIC_CO_TLS(CoroutineUContext, leader); ++ ++/* ++ * va_args to makecontext() must be type 'int', so passing ++ * the pointer we need may require several int args. This ++ * union is a quick hack to let us do that ++ */ ++union cc_arg { ++ void *p; ++ int i[2]; ++}; ++ ++/* ++ * QEMU_ALWAYS_INLINE only does so if __OPTIMIZE__, so we cannot use it. ++ * always_inline is required to avoid TSan runtime fatal errors. ++ */ ++static inline __attribute__((always_inline)) ++void on_new_fiber(CoroutineUContext *co) ++{ ++#ifdef CONFIG_TSAN ++ co->tsan_co_fiber = __tsan_create_fiber(0); /* flags: sync on switch */ ++ co->tsan_caller_fiber = __tsan_get_current_fiber(); ++#endif ++} ++ ++/* always_inline is required to avoid TSan runtime fatal errors. */ ++static inline __attribute__((always_inline)) ++void finish_switch_fiber(void *fake_stack_save) ++{ ++#ifdef CONFIG_ASAN ++ CoroutineUContext *leaderp = get_ptr_leader(); ++ const void *bottom_old; ++ size_t size_old; ++ ++ __sanitizer_finish_switch_fiber(fake_stack_save, &bottom_old, &size_old); ++ ++ if (!leaderp->stack) { ++ leaderp->stack = (void *)bottom_old; ++ leaderp->stack_size = size_old; ++ } ++#endif ++#ifdef CONFIG_TSAN ++ if (fake_stack_save) { ++ __tsan_release(fake_stack_save); ++ __tsan_switch_to_fiber(fake_stack_save, 0); /* 0=synchronize */ ++ } ++#endif ++} ++ ++/* always_inline is required to avoid TSan runtime fatal errors. */ ++static inline __attribute__((always_inline)) ++void start_switch_fiber_asan(void **fake_stack_save, ++ const void *bottom, size_t size) ++{ ++#ifdef CONFIG_ASAN ++ __sanitizer_start_switch_fiber(fake_stack_save, bottom, size); ++#endif ++} ++ ++/* always_inline is required to avoid TSan runtime fatal errors. */ ++static inline __attribute__((always_inline)) ++void start_switch_fiber_tsan(void **fake_stack_save, ++ CoroutineUContext *co, ++ bool caller) ++{ ++#ifdef CONFIG_TSAN ++ void *new_fiber = caller ? ++ co->tsan_caller_fiber : ++ co->tsan_co_fiber; ++ void *curr_fiber = __tsan_get_current_fiber(); ++ __tsan_acquire(curr_fiber); ++ ++ *fake_stack_save = curr_fiber; ++ __tsan_switch_to_fiber(new_fiber, 0); /* 0=synchronize */ ++#endif ++} ++ ++static void coroutine_trampoline(int i0, int i1) ++{ ++ union cc_arg arg; ++ CoroutineUContext *self; ++ Coroutine *co; ++ void *fake_stack_save = NULL; ++ ++ finish_switch_fiber(NULL); ++ ++ arg.i[0] = i0; ++ arg.i[1] = i1; ++ self = arg.p; ++ co = &self->base; ++ ++ /* Initialize longjmp environment and switch back the caller */ ++ if (!sigsetjmp(self->env, 0)) { ++ CoroutineUContext *leaderp = get_ptr_leader(); ++ ++ start_switch_fiber_asan(&fake_stack_save, ++ leaderp->stack, leaderp->stack_size); ++ start_switch_fiber_tsan(&fake_stack_save, self, true); /* true=caller */ ++ siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); ++ } ++ ++ finish_switch_fiber(fake_stack_save); ++ ++ while (true) { ++ co->entry(co->entry_arg); ++ qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); ++ } ++} ++ ++Coroutine *qemu_coroutine_new(void) ++{ ++ CoroutineUContext *co; ++ ucontext_t old_uc, uc; ++ sigjmp_buf old_env; ++ union cc_arg arg = {0}; ++ void *fake_stack_save = NULL; ++ ++ /* The ucontext functions preserve signal masks which incurs a ++ * system call overhead. sigsetjmp(buf, 0)/siglongjmp() does not ++ * preserve signal masks but only works on the current stack. ++ * Since we need a way to create and switch to a new stack, use ++ * the ucontext functions for that but sigsetjmp()/siglongjmp() for ++ * everything else. ++ */ ++ ++ if (getcontext(&uc) == -1) { ++ abort(); ++ } ++ ++ co = g_malloc0(sizeof(*co)); ++ co->stack_size = COROUTINE_STACK_SIZE; ++ co->stack = qemu_alloc_stack(&co->stack_size); ++#ifdef CONFIG_SAFESTACK ++ co->unsafe_stack_size = COROUTINE_STACK_SIZE; ++ co->unsafe_stack = qemu_alloc_stack(&co->unsafe_stack_size); ++#endif ++ co->base.entry_arg = &old_env; /* stash away our jmp_buf */ ++ ++ uc.uc_link = &old_uc; ++ uc.uc_stack.ss_sp = co->stack; ++ uc.uc_stack.ss_size = co->stack_size; ++ uc.uc_stack.ss_flags = 0; ++ ++#ifdef CONFIG_VALGRIND_H ++ co->valgrind_stack_id = ++ VALGRIND_STACK_REGISTER(co->stack, co->stack + co->stack_size); ++#endif ++ ++ arg.p = co; ++ ++ on_new_fiber(co); ++ makecontext(&uc, (void (*)(void))coroutine_trampoline, ++ 2, arg.i[0], arg.i[1]); ++ ++ /* swapcontext() in, siglongjmp() back out */ ++ if (!sigsetjmp(old_env, 0)) { ++ start_switch_fiber_asan(&fake_stack_save, co->stack, co->stack_size); ++ start_switch_fiber_tsan(&fake_stack_save, ++ co, false); /* false=not caller */ ++ ++#ifdef CONFIG_SAFESTACK ++ /* ++ * Before we swap the context, set the new unsafe stack ++ * The unsafe stack grows just like the normal stack, so start from ++ * the last usable location of the memory area. ++ * NOTE: we don't have to re-set the usp afterwards because we are ++ * coming back to this context through a siglongjmp. ++ * The compiler already wrapped the corresponding sigsetjmp call with ++ * code that saves the usp on the (safe) stack before the call, and ++ * restores it right after (which is where we return with siglongjmp). ++ */ ++ void *usp = co->unsafe_stack + co->unsafe_stack_size; ++ __safestack_unsafe_stack_ptr = usp; ++#endif ++ ++ swapcontext(&old_uc, &uc); ++ } ++ ++ finish_switch_fiber(fake_stack_save); ++ ++ return &co->base; ++} ++ ++#ifdef CONFIG_VALGRIND_H ++/* Work around an unused variable in the valgrind.h macro... */ ++#if !defined(__clang__) ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wunused-but-set-variable" ++#endif ++static inline void valgrind_stack_deregister(CoroutineUContext *co) ++{ ++ VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id); ++} ++#if !defined(__clang__) ++#pragma GCC diagnostic pop ++#endif ++#endif ++ ++#if defined(CONFIG_ASAN) && defined(CONFIG_COROUTINE_POOL) ++static void coroutine_fn terminate_asan(void *opaque) ++{ ++ CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, opaque); ++ ++ set_current(opaque); ++ start_switch_fiber_asan(NULL, to->stack, to->stack_size); ++ G_STATIC_ASSERT(!IS_ENABLED(CONFIG_TSAN)); ++ siglongjmp(to->env, COROUTINE_ENTER); ++} ++#endif ++ ++void qemu_coroutine_delete(Coroutine *co_) ++{ ++ CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); ++ ++#if defined(CONFIG_ASAN) && defined(CONFIG_COROUTINE_POOL) ++ co_->entry_arg = qemu_coroutine_self(); ++ co_->entry = terminate_asan; ++ qemu_coroutine_switch(co_->entry_arg, co_, COROUTINE_ENTER); ++#endif ++ ++#ifdef CONFIG_VALGRIND_H ++ valgrind_stack_deregister(co); ++#endif ++ ++ qemu_free_stack(co->stack, co->stack_size); ++#ifdef CONFIG_SAFESTACK ++ qemu_free_stack(co->unsafe_stack, co->unsafe_stack_size); ++#endif ++ g_free(co); ++} ++ ++/* This function is marked noinline to prevent GCC from inlining it ++ * into coroutine_trampoline(). If we allow it to do that then it ++ * hoists the code to get the address of the TLS variable "current" ++ * out of the while() loop. This is an invalid transformation because ++ * the sigsetjmp() call may be called when running thread A but ++ * return in thread B, and so we might be in a different thread ++ * context each time round the loop. ++ */ ++CoroutineAction __attribute__((noinline)) ++qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, ++ CoroutineAction action) ++{ ++ CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_); ++ CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_); ++ int ret; ++ void *fake_stack_save = NULL; ++ ++ set_current(to_); ++ ++ ret = sigsetjmp(from->env, 0); ++ if (ret == 0) { ++ start_switch_fiber_asan(IS_ENABLED(CONFIG_COROUTINE_POOL) || ++ action != COROUTINE_TERMINATE ? ++ &fake_stack_save : NULL, ++ to->stack, to->stack_size); ++ start_switch_fiber_tsan(&fake_stack_save, ++ to, false); /* false=not caller */ ++ siglongjmp(to->env, action); ++ } ++ ++ finish_switch_fiber(fake_stack_save); ++ ++ return ret; ++} ++ ++Coroutine *qemu_coroutine_self(void) ++{ ++ Coroutine *self = get_current(); ++ CoroutineUContext *leaderp = get_ptr_leader(); ++ ++ if (!self) { ++ self = &leaderp->base; ++ set_current(self); ++ } ++#ifdef CONFIG_TSAN ++ if (!leaderp->tsan_co_fiber) { ++ leaderp->tsan_co_fiber = __tsan_get_current_fiber(); ++ } ++#endif ++ return self; ++} ++ ++bool qemu_in_coroutine(void) ++{ ++ Coroutine *self = get_current(); ++ ++ return self && self->caller; ++} +diff --git a/qcow2/lib/util/cutils.c b/qcow2/lib/util/cutils.c +new file mode 100644 +index 00000000..42364039 +--- /dev/null ++++ b/qcow2/lib/util/cutils.c +@@ -0,0 +1,1218 @@ ++/* ++ * Simple C functions to supplement the C library ++ * ++ * Copyright (c) 2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/host-utils.h" ++#include ++ ++#ifdef __FreeBSD__ ++#include ++#include ++#endif ++ ++#ifdef __NetBSD__ ++#include ++#endif ++ ++#ifdef __HAIKU__ ++#include ++#endif ++ ++#ifdef __APPLE__ ++#include ++#endif ++ ++#ifdef G_OS_WIN32 ++#include ++#include ++#endif ++ ++#include "qemu/ctype.h" ++#include "qemu/cutils.h" ++#include "qemu/error-report.h" ++ ++void strpadcpy(char *buf, int buf_size, const char *str, char pad) ++{ ++ int len = qemu_strnlen(str, buf_size); ++ memcpy(buf, str, len); ++ memset(buf + len, pad, buf_size - len); ++} ++ ++void pstrcpy(char *buf, int buf_size, const char *str) ++{ ++ int c; ++ char *q = buf; ++ ++ if (buf_size <= 0) ++ return; ++ ++ for(;;) { ++ c = *str++; ++ if (c == 0 || q >= buf + buf_size - 1) ++ break; ++ *q++ = c; ++ } ++ *q = '\0'; ++} ++ ++/* strcat and truncate. */ ++char *pstrcat(char *buf, int buf_size, const char *s) ++{ ++ int len; ++ len = strlen(buf); ++ if (len < buf_size) ++ pstrcpy(buf + len, buf_size - len, s); ++ return buf; ++} ++ ++int strstart(const char *str, const char *val, const char **ptr) ++{ ++ const char *p, *q; ++ p = str; ++ q = val; ++ while (*q != '\0') { ++ if (*p != *q) ++ return 0; ++ p++; ++ q++; ++ } ++ if (ptr) ++ *ptr = p; ++ return 1; ++} ++ ++int stristart(const char *str, const char *val, const char **ptr) ++{ ++ const char *p, *q; ++ p = str; ++ q = val; ++ while (*q != '\0') { ++ if (qemu_toupper(*p) != qemu_toupper(*q)) ++ return 0; ++ p++; ++ q++; ++ } ++ if (ptr) ++ *ptr = p; ++ return 1; ++} ++ ++/* XXX: use host strnlen if available ? */ ++int qemu_strnlen(const char *s, int max_len) ++{ ++ int i; ++ ++ for(i = 0; i < max_len; i++) { ++ if (s[i] == '\0') { ++ break; ++ } ++ } ++ return i; ++} ++ ++char *qemu_strsep(char **input, const char *delim) ++{ ++ char *result = *input; ++ if (result != NULL) { ++ char *p; ++ ++ for (p = result; *p != '\0'; p++) { ++ if (strchr(delim, *p)) { ++ break; ++ } ++ } ++ if (*p == '\0') { ++ *input = NULL; ++ } else { ++ *p = '\0'; ++ *input = p + 1; ++ } ++ } ++ return result; ++} ++ ++time_t mktimegm(struct tm *tm) ++{ ++ time_t t; ++ int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday; ++ if (m < 3) { ++ m += 12; ++ y--; ++ } ++ t = 86400ULL * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + ++ y / 400 - 719469); ++ t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec; ++ return t; ++} ++ ++static int64_t suffix_mul(char suffix, int64_t unit) ++{ ++ switch (qemu_toupper(suffix)) { ++ case 'B': ++ return 1; ++ case 'K': ++ return unit; ++ case 'M': ++ return unit * unit; ++ case 'G': ++ return unit * unit * unit; ++ case 'T': ++ return unit * unit * unit * unit; ++ case 'P': ++ return unit * unit * unit * unit * unit; ++ case 'E': ++ return unit * unit * unit * unit * unit * unit; ++ } ++ return -1; ++} ++ ++/* ++ * Convert size string to bytes. ++ * ++ * The size parsing supports the following syntaxes ++ * - 12345 - decimal, scale determined by @default_suffix and @unit ++ * - 12345{bBkKmMgGtTpPeE} - decimal, scale determined by suffix and @unit ++ * - 12345.678{kKmMgGtTpPeE} - decimal, scale determined by suffix, and ++ * fractional portion is truncated to byte, either side of . may be empty ++ * - 0x7fEE - hexadecimal, unit determined by @default_suffix ++ * ++ * The following are intentionally not supported ++ * - hex with scaling suffix, such as 0x20M or 0x1p3 (both fail with ++ * -EINVAL), while 0x1b is 27 (not 1 with byte scale) ++ * - octal, such as 08 (parsed as decimal instead) ++ * - binary, such as 0b1000 (parsed as 0b with trailing garbage "1000") ++ * - fractional hex, such as 0x1.8 (parsed as 0 with trailing garbage "x1.8") ++ * - negative values, including -0 (fail with -ERANGE) ++ * - floating point exponents, such as 1e3 (parsed as 1e with trailing ++ * garbage "3") or 0x1p3 (rejected as hex with scaling suffix) ++ * - non-finite values, such as inf or NaN (fail with -EINVAL) ++ * ++ * The end pointer will be returned in *end, if not NULL. If there is ++ * no fraction, the input can be decimal or hexadecimal; if there is a ++ * non-zero fraction, then the input must be decimal and there must be ++ * a suffix (possibly by @default_suffix) larger than Byte, and the ++ * fractional portion may suffer from precision loss or rounding. The ++ * input must be positive. ++ * ++ * Return -ERANGE on overflow (with *@end advanced), and -EINVAL on ++ * other error (with *@end at @nptr). Unlike strtoull, *@result is ++ * set to 0 on all errors, as returning UINT64_MAX on overflow is less ++ * likely to be usable as a size. ++ */ ++static int do_strtosz(const char *nptr, const char **end, ++ const char default_suffix, int64_t unit, ++ uint64_t *result) ++{ ++ int retval; ++ const char *endptr; ++ unsigned char c; ++ uint64_t val = 0, valf = 0; ++ int64_t mul; ++ ++ /* Parse integral portion as decimal. */ ++ retval = parse_uint(nptr, &endptr, 10, &val); ++ if (retval == -ERANGE || !nptr) { ++ goto out; ++ } ++ if (retval == 0 && val == 0 && (*endptr == 'x' || *endptr == 'X')) { ++ /* Input looks like hex; reparse, and insist on no fraction or suffix. */ ++ retval = qemu_strtou64(nptr, &endptr, 16, &val); ++ if (retval) { ++ goto out; ++ } ++ if (*endptr == '.' || suffix_mul(*endptr, unit) > 0) { ++ endptr = nptr; ++ retval = -EINVAL; ++ goto out; ++ } ++ } else if (*endptr == '.' || (endptr == nptr && strchr(nptr, '.'))) { ++ /* ++ * Input looks like a fraction. Make sure even 1.k works ++ * without fractional digits. strtod tries to treat 'e' as an ++ * exponent, but we want to treat it as a scaling suffix; ++ * doing this requires modifying a copy of the fraction. ++ */ ++ double fraction = 0.0; ++ ++ if (retval == 0 && *endptr == '.' && !isdigit(endptr[1])) { ++ /* If we got here, we parsed at least one digit already. */ ++ endptr++; ++ } else { ++ char *e; ++ const char *tail; ++ g_autofree char *copy = g_strdup(endptr); ++ ++ e = strchr(copy, 'e'); ++ if (e) { ++ *e = '\0'; ++ } ++ e = strchr(copy, 'E'); ++ if (e) { ++ *e = '\0'; ++ } ++ /* ++ * If this is a floating point, we are guaranteed that '.' ++ * appears before any possible digits in copy. If it is ++ * not a floating point, strtod will fail. Either way, ++ * there is now no exponent in copy, so if it parses, we ++ * know 0.0 <= abs(result) <= 1.0 (after rounding), and ++ * ERANGE is only possible on underflow which is okay. ++ */ ++ retval = qemu_strtod_finite(copy, &tail, &fraction); ++ endptr += tail - copy; ++ if (signbit(fraction)) { ++ retval = -ERANGE; ++ goto out; ++ } ++ } ++ ++ /* Extract into a 64-bit fixed-point fraction. */ ++ if (fraction == 1.0) { ++ if (val == UINT64_MAX) { ++ retval = -ERANGE; ++ goto out; ++ } ++ val++; ++ } else if (retval == -ERANGE) { ++ /* See comments above about underflow */ ++ valf = 1; ++ retval = 0; ++ } else { ++ /* We want non-zero valf for any non-zero fraction */ ++ valf = (uint64_t)(fraction * 0x1p64); ++ if (valf == 0 && fraction > 0.0) { ++ valf = 1; ++ } ++ } ++ } ++ if (retval) { ++ goto out; ++ } ++ c = *endptr; ++ mul = suffix_mul(c, unit); ++ if (mul > 0) { ++ endptr++; ++ } else { ++ mul = suffix_mul(default_suffix, unit); ++ assert(mul > 0); ++ } ++ if (mul == 1) { ++ /* When a fraction is present, a scale is required. */ ++ if (valf != 0) { ++ endptr = nptr; ++ retval = -EINVAL; ++ goto out; ++ } ++ } else { ++ uint64_t valh, tmp; ++ ++ /* Compute exact result: 64.64 x 64.0 -> 128.64 fixed point */ ++ mulu64(&val, &valh, val, mul); ++ mulu64(&valf, &tmp, valf, mul); ++ val += tmp; ++ valh += val < tmp; ++ ++ /* Round 0.5 upward. */ ++ tmp = valf >> 63; ++ val += tmp; ++ valh += val < tmp; ++ ++ /* Report overflow. */ ++ if (valh != 0) { ++ retval = -ERANGE; ++ goto out; ++ } ++ } ++ ++ retval = 0; ++ ++out: ++ if (end) { ++ *end = endptr; ++ } else if (nptr && *endptr) { ++ retval = -EINVAL; ++ } ++ if (retval == 0) { ++ *result = val; ++ } else { ++ *result = 0; ++ if (end && retval == -EINVAL) { ++ *end = nptr; ++ } ++ } ++ ++ return retval; ++} ++ ++int qemu_strtosz(const char *nptr, const char **end, uint64_t *result) ++{ ++ return do_strtosz(nptr, end, 'B', 1024, result); ++} ++ ++int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result) ++{ ++ return do_strtosz(nptr, end, 'M', 1024, result); ++} ++ ++int qemu_strtosz_metric(const char *nptr, const char **end, uint64_t *result) ++{ ++ return do_strtosz(nptr, end, 'B', 1000, result); ++} ++ ++/** ++ * Helper function for error checking after strtol() and the like ++ */ ++static int check_strtox_error(const char *nptr, char *ep, ++ const char **endptr, bool check_zero, ++ int libc_errno) ++{ ++ assert(ep >= nptr); ++ ++ /* Windows has a bug in that it fails to parse 0 from "0x" in base 16 */ ++ if (check_zero && ep == nptr && libc_errno == 0) { ++ char *tmp; ++ ++ errno = 0; ++ if (strtol(nptr, &tmp, 10) == 0 && errno == 0 && ++ (*tmp == 'x' || *tmp == 'X')) { ++ ep = tmp; ++ } ++ } ++ ++ if (endptr) { ++ *endptr = ep; ++ } ++ ++ /* Turn "no conversion" into an error */ ++ if (libc_errno == 0 && ep == nptr) { ++ return -EINVAL; ++ } ++ ++ /* Fail when we're expected to consume the string, but didn't */ ++ if (!endptr && *ep) { ++ return -EINVAL; ++ } ++ ++ return -libc_errno; ++} ++ ++/** ++ * Convert string @nptr to an integer, and store it in @result. ++ * ++ * This is a wrapper around strtol() that is harder to misuse. ++ * Semantics of @nptr, @endptr, @base match strtol() with differences ++ * noted below. ++ * ++ * @nptr may be null, and no conversion is performed then. ++ * ++ * If no conversion is performed, store @nptr in *@endptr, 0 in ++ * @result, and return -EINVAL. ++ * ++ * If @endptr is null, and the string isn't fully converted, return ++ * -EINVAL with @result set to the parsed value. This is the case ++ * when the pointer that would be stored in a non-null @endptr points ++ * to a character other than '\0'. ++ * ++ * If the conversion overflows @result, store INT_MAX in @result, ++ * and return -ERANGE. ++ * ++ * If the conversion underflows @result, store INT_MIN in @result, ++ * and return -ERANGE. ++ * ++ * Else store the converted value in @result, and return zero. ++ * ++ * This matches the behavior of strtol() on 32-bit platforms, even on ++ * platforms where long is 64-bits. ++ */ ++int qemu_strtoi(const char *nptr, const char **endptr, int base, ++ int *result) ++{ ++ char *ep; ++ long long lresult; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!nptr) { ++ *result = 0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ errno = 0; ++ lresult = strtoll(nptr, &ep, base); ++ if (lresult < INT_MIN) { ++ *result = INT_MIN; ++ errno = ERANGE; ++ } else if (lresult > INT_MAX) { ++ *result = INT_MAX; ++ errno = ERANGE; ++ } else { ++ *result = lresult; ++ } ++ return check_strtox_error(nptr, ep, endptr, lresult == 0, errno); ++} ++ ++/** ++ * Convert string @nptr to an unsigned integer, and store it in @result. ++ * ++ * This is a wrapper around strtoul() that is harder to misuse. ++ * Semantics of @nptr, @endptr, @base match strtoul() with differences ++ * noted below. ++ * ++ * @nptr may be null, and no conversion is performed then. ++ * ++ * If no conversion is performed, store @nptr in *@endptr, 0 in ++ * @result, and return -EINVAL. ++ * ++ * If @endptr is null, and the string isn't fully converted, return ++ * -EINVAL with @result set to the parsed value. This is the case ++ * when the pointer that would be stored in a non-null @endptr points ++ * to a character other than '\0'. ++ * ++ * If the conversion overflows @result, store UINT_MAX in @result, ++ * and return -ERANGE. ++ * ++ * Else store the converted value in @result, and return zero. ++ * ++ * Note that a number with a leading minus sign gets converted without ++ * the minus sign, checked for overflow (see above), then negated (in ++ * @result's type). This matches the behavior of strtoul() on 32-bit ++ * platforms, even on platforms where long is 64-bits. ++ */ ++int qemu_strtoui(const char *nptr, const char **endptr, int base, ++ unsigned int *result) ++{ ++ char *ep; ++ unsigned long long lresult; ++ bool neg; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!nptr) { ++ *result = 0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ errno = 0; ++ lresult = strtoull(nptr, &ep, base); ++ ++ /* Windows returns 1 for negative out-of-range values. */ ++ if (errno == ERANGE) { ++ *result = -1; ++ } else { ++ /* ++ * Note that platforms with 32-bit strtoul only accept input ++ * in the range [-4294967295, 4294967295]; but we used 64-bit ++ * strtoull which wraps -18446744073709551615 to 1 instead of ++ * declaring overflow. So we must check if '-' was parsed, ++ * and if so, undo the negation before doing our bounds check. ++ */ ++ neg = memchr(nptr, '-', ep - nptr) != NULL; ++ if (neg) { ++ lresult = -lresult; ++ } ++ if (lresult > UINT_MAX) { ++ *result = UINT_MAX; ++ errno = ERANGE; ++ } else { ++ *result = neg ? -lresult : lresult; ++ } ++ } ++ return check_strtox_error(nptr, ep, endptr, lresult == 0, errno); ++} ++ ++/** ++ * Convert string @nptr to a long integer, and store it in @result. ++ * ++ * This is a wrapper around strtol() that is harder to misuse. ++ * Semantics of @nptr, @endptr, @base match strtol() with differences ++ * noted below. ++ * ++ * @nptr may be null, and no conversion is performed then. ++ * ++ * If no conversion is performed, store @nptr in *@endptr, 0 in ++ * @result, and return -EINVAL. ++ * ++ * If @endptr is null, and the string isn't fully converted, return ++ * -EINVAL with @result set to the parsed value. This is the case ++ * when the pointer that would be stored in a non-null @endptr points ++ * to a character other than '\0'. ++ * ++ * If the conversion overflows @result, store LONG_MAX in @result, ++ * and return -ERANGE. ++ * ++ * If the conversion underflows @result, store LONG_MIN in @result, ++ * and return -ERANGE. ++ * ++ * Else store the converted value in @result, and return zero. ++ */ ++int qemu_strtol(const char *nptr, const char **endptr, int base, ++ long *result) ++{ ++ char *ep; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!nptr) { ++ *result = 0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ errno = 0; ++ *result = strtol(nptr, &ep, base); ++ return check_strtox_error(nptr, ep, endptr, *result == 0, errno); ++} ++ ++/** ++ * Convert string @nptr to an unsigned long, and store it in @result. ++ * ++ * This is a wrapper around strtoul() that is harder to misuse. ++ * Semantics of @nptr, @endptr, @base match strtoul() with differences ++ * noted below. ++ * ++ * @nptr may be null, and no conversion is performed then. ++ * ++ * If no conversion is performed, store @nptr in *@endptr, 0 in ++ * @result, and return -EINVAL. ++ * ++ * If @endptr is null, and the string isn't fully converted, return ++ * -EINVAL with @result set to the parsed value. This is the case ++ * when the pointer that would be stored in a non-null @endptr points ++ * to a character other than '\0'. ++ * ++ * If the conversion overflows @result, store ULONG_MAX in @result, ++ * and return -ERANGE. ++ * ++ * Else store the converted value in @result, and return zero. ++ * ++ * Note that a number with a leading minus sign gets converted without ++ * the minus sign, checked for overflow (see above), then negated (in ++ * @result's type). This is exactly how strtoul() works. ++ */ ++int qemu_strtoul(const char *nptr, const char **endptr, int base, ++ unsigned long *result) ++{ ++ char *ep; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!nptr) { ++ *result = 0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ errno = 0; ++ *result = strtoul(nptr, &ep, base); ++ /* Windows returns 1 for negative out-of-range values. */ ++ if (errno == ERANGE) { ++ *result = -1; ++ } ++ return check_strtox_error(nptr, ep, endptr, *result == 0, errno); ++} ++ ++/** ++ * Convert string @nptr to an int64_t. ++ * ++ * Works like qemu_strtol(), except it stores INT64_MAX on overflow, ++ * and INT64_MIN on underflow. ++ */ ++int qemu_strtoi64(const char *nptr, const char **endptr, int base, ++ int64_t *result) ++{ ++ char *ep; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!nptr) { ++ *result = 0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ /* This assumes int64_t is long long TODO relax */ ++ QEMU_BUILD_BUG_ON(sizeof(int64_t) != sizeof(long long)); ++ errno = 0; ++ *result = strtoll(nptr, &ep, base); ++ return check_strtox_error(nptr, ep, endptr, *result == 0, errno); ++} ++ ++/** ++ * Convert string @nptr to an uint64_t. ++ * ++ * Works like qemu_strtoul(), except it stores UINT64_MAX on overflow. ++ * (If you want to prohibit negative numbers that wrap around to ++ * positive, use parse_uint()). ++ */ ++int qemu_strtou64(const char *nptr, const char **endptr, int base, ++ uint64_t *result) ++{ ++ char *ep; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!nptr) { ++ *result = 0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ /* This assumes uint64_t is unsigned long long TODO relax */ ++ QEMU_BUILD_BUG_ON(sizeof(uint64_t) != sizeof(unsigned long long)); ++ errno = 0; ++ *result = strtoull(nptr, &ep, base); ++ /* Windows returns 1 for negative out-of-range values. */ ++ if (errno == ERANGE) { ++ *result = -1; ++ } ++ return check_strtox_error(nptr, ep, endptr, *result == 0, errno); ++} ++ ++/** ++ * Convert string @nptr to a double. ++ * ++ * This is a wrapper around strtod() that is harder to misuse. ++ * Semantics of @nptr and @endptr match strtod() with differences ++ * noted below. ++ * ++ * @nptr may be null, and no conversion is performed then. ++ * ++ * If no conversion is performed, store @nptr in *@endptr, +0.0 in ++ * @result, and return -EINVAL. ++ * ++ * If @endptr is null, and the string isn't fully converted, return ++ * -EINVAL with @result set to the parsed value. This is the case ++ * when the pointer that would be stored in a non-null @endptr points ++ * to a character other than '\0'. ++ * ++ * If the conversion overflows, store +/-HUGE_VAL in @result, depending ++ * on the sign, and return -ERANGE. ++ * ++ * If the conversion underflows, store +/-0.0 in @result, depending on the ++ * sign, and return -ERANGE. ++ * ++ * Else store the converted value in @result, and return zero. ++ */ ++int qemu_strtod(const char *nptr, const char **endptr, double *result) ++{ ++ char *ep; ++ ++ if (!nptr) { ++ *result = 0.0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ errno = 0; ++ *result = strtod(nptr, &ep); ++ return check_strtox_error(nptr, ep, endptr, false, errno); ++} ++ ++/** ++ * Convert string @nptr to a finite double. ++ * ++ * Works like qemu_strtod(), except that "NaN", "inf", and strings ++ * that cause ERANGE overflow errors are rejected with -EINVAL as if ++ * no conversion is performed, storing 0.0 into @result regardless of ++ * any sign. -ERANGE failures for underflow still preserve the parsed ++ * sign. ++ */ ++int qemu_strtod_finite(const char *nptr, const char **endptr, double *result) ++{ ++ const char *tmp; ++ int ret; ++ ++ ret = qemu_strtod(nptr, &tmp, result); ++ if (!isfinite(*result)) { ++ if (endptr) { ++ *endptr = nptr; ++ } ++ *result = 0.0; ++ ret = -EINVAL; ++ } else if (endptr) { ++ *endptr = tmp; ++ } else if (*tmp) { ++ ret = -EINVAL; ++ } ++ return ret; ++} ++ ++/** ++ * Searches for the first occurrence of 'c' in 's', and returns a pointer ++ * to the trailing null byte if none was found. ++ */ ++#ifndef HAVE_STRCHRNUL ++const char *qemu_strchrnul(const char *s, int c) ++{ ++ const char *e = strchr(s, c); ++ if (!e) { ++ e = s + strlen(s); ++ } ++ return e; ++} ++#endif ++ ++/** ++ * parse_uint: ++ * ++ * @s: String to parse ++ * @endptr: Destination for pointer to first character not consumed ++ * @base: integer base, between 2 and 36 inclusive, or 0 ++ * @value: Destination for parsed integer value ++ * ++ * Parse unsigned integer ++ * ++ * Parsed syntax is like strtoull()'s: arbitrary whitespace, a single optional ++ * '+' or '-', an optional "0x" if @base is 0 or 16, one or more digits. ++ * ++ * If @s is null, or @s doesn't start with an integer in the syntax ++ * above, set *@value to 0, *@endptr to @s, and return -EINVAL. ++ * ++ * Set *@endptr to point right beyond the parsed integer (even if the integer ++ * overflows or is negative, all digits will be parsed and *@endptr will ++ * point right beyond them). If @endptr is %NULL, any trailing character ++ * instead causes a result of -EINVAL with *@value of 0. ++ * ++ * If the integer is negative, set *@value to 0, and return -ERANGE. ++ * (If you want to allow negative numbers that wrap around within ++ * bounds, use qemu_strtou64()). ++ * ++ * If the integer overflows unsigned long long, set *@value to ++ * ULLONG_MAX, and return -ERANGE. ++ * ++ * Else, set *@value to the parsed integer, and return 0. ++ */ ++int parse_uint(const char *s, const char **endptr, int base, uint64_t *value) ++{ ++ int r = 0; ++ char *endp = (char *)s; ++ unsigned long long val = 0; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!s) { ++ r = -EINVAL; ++ goto out; ++ } ++ ++ errno = 0; ++ val = strtoull(s, &endp, base); ++ if (errno) { ++ r = -errno; ++ goto out; ++ } ++ ++ if (endp == s) { ++ r = -EINVAL; ++ goto out; ++ } ++ ++ /* make sure we reject negative numbers: */ ++ while (qemu_isspace(*s)) { ++ s++; ++ } ++ if (*s == '-') { ++ val = 0; ++ r = -ERANGE; ++ goto out; ++ } ++ ++out: ++ *value = val; ++ if (endptr) { ++ *endptr = endp; ++ } else if (s && *endp) { ++ r = -EINVAL; ++ *value = 0; ++ } ++ return r; ++} ++ ++/** ++ * parse_uint_full: ++ * ++ * @s: String to parse ++ * @base: integer base, between 2 and 36 inclusive, or 0 ++ * @value: Destination for parsed integer value ++ * ++ * Parse unsigned integer from entire string, rejecting any trailing slop. ++ * ++ * Shorthand for parse_uint(s, NULL, base, value). ++ */ ++int parse_uint_full(const char *s, int base, uint64_t *value) ++{ ++ return parse_uint(s, NULL, base, value); ++} ++ ++int qemu_parse_fd(const char *param) ++{ ++ long fd; ++ char *endptr; ++ ++ errno = 0; ++ fd = strtol(param, &endptr, 10); ++ if (param == endptr /* no conversion performed */ || ++ errno != 0 /* not representable as long; possibly others */ || ++ *endptr != '\0' /* final string not empty */ || ++ fd < 0 /* invalid as file descriptor */ || ++ fd > INT_MAX /* not representable as int */) { ++ return -1; ++ } ++ return fd; ++} ++ ++/* ++ * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128) ++ * Input is limited to 14-bit numbers ++ */ ++int uleb128_encode_small(uint8_t *out, uint32_t n) ++{ ++ g_assert(n <= 0x3fff); ++ if (n < 0x80) { ++ *out = n; ++ return 1; ++ } else { ++ *out++ = (n & 0x7f) | 0x80; ++ *out = n >> 7; ++ return 2; ++ } ++} ++ ++int uleb128_decode_small(const uint8_t *in, uint32_t *n) ++{ ++ if (!(*in & 0x80)) { ++ *n = *in; ++ return 1; ++ } else { ++ *n = *in++ & 0x7f; ++ /* we exceed 14 bit number */ ++ if (*in & 0x80) { ++ return -1; ++ } ++ *n |= *in << 7; ++ return 2; ++ } ++} ++ ++/* ++ * helper to parse debug environment variables ++ */ ++int parse_debug_env(const char *name, int max, int initial) ++{ ++ char *debug_env = getenv(name); ++ char *inv = NULL; ++ long debug; ++ ++ if (!debug_env) { ++ return initial; ++ } ++ errno = 0; ++ debug = strtol(debug_env, &inv, 10); ++ if (inv == debug_env) { ++ return initial; ++ } ++ if (debug < 0 || debug > max || errno != 0) { ++ warn_report("%s not in [0, %d]", name, max); ++ return initial; ++ } ++ return debug; ++} ++ ++const char *si_prefix(unsigned int exp10) ++{ ++ static const char *prefixes[] = { ++ "a", "f", "p", "n", "u", "m", "", "K", "M", "G", "T", "P", "E" ++ }; ++ ++ exp10 += 18; ++ assert(exp10 % 3 == 0 && exp10 / 3 < ARRAY_SIZE(prefixes)); ++ return prefixes[exp10 / 3]; ++} ++ ++const char *iec_binary_prefix(unsigned int exp2) ++{ ++ static const char *prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; ++ ++ assert(exp2 % 10 == 0 && exp2 / 10 < ARRAY_SIZE(prefixes)); ++ return prefixes[exp2 / 10]; ++} ++ ++/* ++ * Return human readable string for size @val. ++ * @val can be anything that uint64_t allows (no more than "16 EiB"). ++ * Use IEC binary units like KiB, MiB, and so forth. ++ * Caller is responsible for passing it to g_free(). ++ */ ++char *size_to_str(uint64_t val) ++{ ++ uint64_t div; ++ int i; ++ ++ /* ++ * The exponent (returned in i) minus one gives us ++ * floor(log2(val * 1024 / 1000). The correction makes us ++ * switch to the higher power when the integer part is >= 1000. ++ * (see e41b509d68afb1f for more info) ++ */ ++ frexp(val / (1000.0 / 1024.0), &i); ++ i = (i - 1) / 10 * 10; ++ div = 1ULL << i; ++ ++ return g_strdup_printf("%0.3g %sB", (double)val / div, iec_binary_prefix(i)); ++} ++ ++char *freq_to_str(uint64_t freq_hz) ++{ ++ double freq = freq_hz; ++ size_t exp10 = 0; ++ ++ while (freq >= 1000.0) { ++ freq /= 1000.0; ++ exp10 += 3; ++ } ++ ++ return g_strdup_printf("%0.3g %sHz", freq, si_prefix(exp10)); ++} ++ ++int qemu_pstrcmp0(const char **str1, const char **str2) ++{ ++ return g_strcmp0(*str1, *str2); ++} ++ ++static inline bool starts_with_prefix(const char *dir) ++{ ++ size_t prefix_len = strlen(CONFIG_PREFIX); ++ /* ++ * dir[prefix_len] is only accessed if the length of dir is ++ * >= prefix_len, so no out of bounds access is possible. ++ */ ++#pragma GCC diagnostic push ++#if !defined(__clang__) || __has_warning("-Warray-bounds=") ++#pragma GCC diagnostic ignored "-Warray-bounds=" ++#endif ++ return !memcmp(dir, CONFIG_PREFIX, prefix_len) && ++ (!dir[prefix_len] || G_IS_DIR_SEPARATOR(dir[prefix_len])); ++#pragma GCC diagnostic pop ++} ++ ++/* Return the next path component in dir, and store its length in *p_len. */ ++static inline const char *next_component(const char *dir, int *p_len) ++{ ++ int len; ++ while ((*dir && G_IS_DIR_SEPARATOR(*dir)) || ++ (*dir == '.' && (G_IS_DIR_SEPARATOR(dir[1]) || dir[1] == '\0'))) { ++ dir++; ++ } ++ len = 0; ++ while (dir[len] && !G_IS_DIR_SEPARATOR(dir[len])) { ++ len++; ++ } ++ *p_len = len; ++ return dir; ++} ++ ++static const char *exec_dir; ++ ++void qemu_init_exec_dir(const char *argv0) ++{ ++#ifdef G_OS_WIN32 ++ char *p; ++ char buf[MAX_PATH]; ++ DWORD len; ++ ++ if (exec_dir) { ++ return; ++ } ++ ++ len = GetModuleFileName(NULL, buf, sizeof(buf) - 1); ++ if (len == 0) { ++ return; ++ } ++ ++ buf[len] = 0; ++ p = buf + len - 1; ++ while (p != buf && *p != '\\') { ++ p--; ++ } ++ *p = 0; ++ if (access(buf, R_OK) == 0) { ++ exec_dir = g_strdup(buf); ++ } else { ++ exec_dir = CONFIG_BINDIR; ++ } ++#else ++ char *p = NULL; ++ char buf[PATH_MAX]; ++ ++ if (exec_dir) { ++ return; ++ } ++ ++#if defined(__linux__) ++ { ++ int len; ++ len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); ++ if (len > 0) { ++ buf[len] = 0; ++ p = buf; ++ } ++ } ++#elif defined(__FreeBSD__) \ ++ || (defined(__NetBSD__) && defined(KERN_PROC_PATHNAME)) ++ { ++#if defined(__FreeBSD__) ++ static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; ++#else ++ static int mib[4] = {CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME}; ++#endif ++ size_t len = sizeof(buf) - 1; ++ ++ *buf = '\0'; ++ if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) && ++ *buf) { ++ buf[sizeof(buf) - 1] = '\0'; ++ p = buf; ++ } ++ } ++#elif defined(__APPLE__) ++ { ++ char fpath[PATH_MAX]; ++ uint32_t len = sizeof(fpath); ++ if (_NSGetExecutablePath(fpath, &len) == 0) { ++ p = realpath(fpath, buf); ++ if (!p) { ++ return; ++ } ++ } ++ } ++#elif defined(__HAIKU__) ++ { ++ image_info ii; ++ int32_t c = 0; ++ ++ *buf = '\0'; ++ while (get_next_image_info(0, &c, &ii) == B_OK) { ++ if (ii.type == B_APP_IMAGE) { ++ strncpy(buf, ii.name, sizeof(buf)); ++ buf[sizeof(buf) - 1] = 0; ++ p = buf; ++ break; ++ } ++ } ++ } ++#endif ++ /* If we don't have any way of figuring out the actual executable ++ location then try argv[0]. */ ++ if (!p && argv0) { ++ p = realpath(argv0, buf); ++ } ++ if (p) { ++ exec_dir = g_path_get_dirname(p); ++ } else { ++ exec_dir = CONFIG_BINDIR; ++ } ++#endif ++} ++ ++const char *qemu_get_exec_dir(void) ++{ ++ return exec_dir; ++} ++ ++char *get_relocated_path(const char *dir) ++{ ++ size_t prefix_len = strlen(CONFIG_PREFIX); ++ const char *bindir = CONFIG_BINDIR; ++ GString *result; ++ int len_dir, len_bindir; ++ ++ /* Fail if qemu_init_exec_dir was not called. */ ++ assert(exec_dir[0]); ++ ++ result = g_string_new(exec_dir); ++ g_string_append(result, "/qemu-bundle"); ++ if (access(result->str, R_OK) == 0) { ++#ifdef G_OS_WIN32 ++ const char *src = dir; ++ size_t size = mbsrtowcs(NULL, &src, 0, &(mbstate_t){0}) + 1; ++ PWSTR wdir = g_new(WCHAR, size); ++ mbsrtowcs(wdir, &src, size, &(mbstate_t){0}); ++ ++ PCWSTR wdir_skipped_root; ++ if (PathCchSkipRoot(wdir, &wdir_skipped_root) == S_OK) { ++ size = wcsrtombs(NULL, &wdir_skipped_root, 0, &(mbstate_t){0}); ++ char *cursor = result->str + result->len; ++ g_string_set_size(result, result->len + size); ++ wcsrtombs(cursor, &wdir_skipped_root, size + 1, &(mbstate_t){0}); ++ } else { ++ g_string_append(result, dir); ++ } ++ ++ g_free(wdir); ++#else ++ g_string_append(result, dir); ++#endif ++ goto out; ++ } ++ ++ if (IS_ENABLED(CONFIG_RELOCATABLE) && ++ starts_with_prefix(dir) && starts_with_prefix(bindir)) { ++ g_string_assign(result, exec_dir); ++ ++ /* Advance over common components. */ ++ len_dir = len_bindir = prefix_len; ++ do { ++ dir += len_dir; ++ bindir += len_bindir; ++ dir = next_component(dir, &len_dir); ++ bindir = next_component(bindir, &len_bindir); ++ } while (len_dir && len_dir == len_bindir && !memcmp(dir, bindir, len_dir)); ++ ++ /* Ascend from bindir to the common prefix with dir. */ ++ while (len_bindir) { ++ bindir += len_bindir; ++ g_string_append(result, "/.."); ++ bindir = next_component(bindir, &len_bindir); ++ } ++ ++ if (*dir) { ++ assert(G_IS_DIR_SEPARATOR(dir[-1])); ++ g_string_append(result, dir - 1); ++ } ++ goto out; ++ } ++ ++ g_string_assign(result, dir); ++out: ++ return g_string_free(result, false); ++} +diff --git a/qcow2/lib/util/defer-call.c b/qcow2/lib/util/defer-call.c +new file mode 100644 +index 00000000..037dc0ab +--- /dev/null ++++ b/qcow2/lib/util/defer-call.c +@@ -0,0 +1,156 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Deferred calls ++ * ++ * Copyright Red Hat. ++ * ++ * This API defers a function call within a defer_call_begin()/defer_call_end() ++ * section, allowing multiple calls to batch up. This is a performance ++ * optimization that is used in the block layer to submit several I/O requests ++ * at once instead of individually: ++ * ++ * defer_call_begin(); <-- start of section ++ * ... ++ * defer_call(my_func, my_obj); <-- deferred my_func(my_obj) call ++ * defer_call(my_func, my_obj); <-- another ++ * defer_call(my_func, my_obj); <-- another ++ * ... ++ * defer_call_end(); <-- end of section, my_func(my_obj) is called once ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/coroutine-tls.h" ++#include "qemu/notify.h" ++#include "qemu/thread.h" ++#include "qemu/defer-call.h" ++ ++/* A function call that has been deferred until defer_call_end() */ ++typedef struct { ++ void (*fn)(void *); ++ void *opaque; ++} DeferredCall; ++ ++/* Per-thread state */ ++typedef struct { ++ unsigned nesting_level; ++ GArray *deferred_call_array; ++} DeferCallThreadState; ++ ++/* Use get_ptr_defer_call_thread_state() to fetch this thread-local value */ ++QEMU_DEFINE_STATIC_CO_TLS(DeferCallThreadState, defer_call_thread_state); ++ ++/* Called at thread cleanup time */ ++static void defer_call_atexit(Notifier *n, void *value) ++{ ++ DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); ++ g_array_free(thread_state->deferred_call_array, TRUE); ++} ++ ++/* This won't involve coroutines, so use __thread */ ++static __thread Notifier defer_call_atexit_notifier; ++ ++/** ++ * defer_call: ++ * @fn: a function pointer to be invoked ++ * @opaque: a user-defined argument to @fn() ++ * ++ * Call @fn(@opaque) immediately if not within a ++ * defer_call_begin()/defer_call_end() section. ++ * ++ * Otherwise defer the call until the end of the outermost ++ * defer_call_begin()/defer_call_end() section in this thread. If the same ++ * @fn/@opaque pair has already been deferred, it will only be called once upon ++ * defer_call_end() so that accumulated calls are batched into a single call. ++ * ++ * The caller must ensure that @opaque is not freed before @fn() is invoked. ++ */ ++void defer_call(void (*fn)(void *), void *opaque) ++{ ++ DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); ++ ++ /* Call immediately if we're not deferring calls */ ++ if (thread_state->nesting_level == 0) { ++ fn(opaque); ++ return; ++ } ++ ++ GArray *array = thread_state->deferred_call_array; ++ if (!array) { ++ array = g_array_new(FALSE, FALSE, sizeof(DeferredCall)); ++ thread_state->deferred_call_array = array; ++ defer_call_atexit_notifier.notify = defer_call_atexit; ++ qemu_thread_atexit_add(&defer_call_atexit_notifier); ++ } ++ ++ DeferredCall *fns = (DeferredCall *)array->data; ++ DeferredCall new_fn = { ++ .fn = fn, ++ .opaque = opaque, ++ }; ++ ++ /* ++ * There won't be many, so do a linear search. If this becomes a bottleneck ++ * then a binary search (glib 2.62+) or different data structure could be ++ * used. ++ */ ++ for (guint i = 0; i < array->len; i++) { ++ if (memcmp(&fns[i], &new_fn, sizeof(new_fn)) == 0) { ++ return; /* already exists */ ++ } ++ } ++ ++ g_array_append_val(array, new_fn); ++} ++ ++/** ++ * defer_call_begin: Defer defer_call() functions until defer_call_end() ++ * ++ * defer_call_begin() and defer_call_end() are thread-local operations. The ++ * caller must ensure that each defer_call_begin() has a matching ++ * defer_call_end() in the same thread. ++ * ++ * Nesting is supported. defer_call() functions are only called at the ++ * outermost defer_call_end(). ++ */ ++void defer_call_begin(void) ++{ ++ DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); ++ ++ assert(thread_state->nesting_level < UINT32_MAX); ++ ++ thread_state->nesting_level++; ++} ++ ++/** ++ * defer_call_end: Run any pending defer_call() functions ++ * ++ * There must have been a matching defer_call_begin() call in the same thread ++ * prior to this defer_call_end() call. ++ */ ++void defer_call_end(void) ++{ ++ DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); ++ ++ assert(thread_state->nesting_level > 0); ++ ++ if (--thread_state->nesting_level > 0) { ++ return; ++ } ++ ++ GArray *array = thread_state->deferred_call_array; ++ if (!array) { ++ return; ++ } ++ ++ DeferredCall *fns = (DeferredCall *)array->data; ++ ++ for (guint i = 0; i < array->len; i++) { ++ fns[i].fn(fns[i].opaque); ++ } ++ ++ /* ++ * This resets the array without freeing memory so that appending is cheap ++ * in the future. ++ */ ++ g_array_set_size(array, 0); ++} +diff --git a/qcow2/lib/util/error-report.c b/qcow2/lib/util/error-report.c +new file mode 100644 +index 00000000..1b17c11d +--- /dev/null ++++ b/qcow2/lib/util/error-report.c +@@ -0,0 +1,394 @@ ++/* ++ * Error reporting ++ * ++ * Copyright (C) 2010 Red Hat Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "monitor/monitor.h" ++#include "qemu/error-report.h" ++ ++/* ++ * @report_type is the type of message: error, warning or ++ * informational. ++ */ ++typedef enum { ++ REPORT_TYPE_ERROR, ++ REPORT_TYPE_WARNING, ++ REPORT_TYPE_INFO, ++} report_type; ++ ++/* Prepend timestamp to messages */ ++bool message_with_timestamp; ++bool error_with_guestname; ++const char *error_guest_name; ++ ++int error_printf(const char *fmt, ...) ++{ ++ va_list ap; ++ int ret; ++ ++ va_start(ap, fmt); ++ ret = error_vprintf(fmt, ap); ++ va_end(ap); ++ return ret; ++} ++ ++static Location std_loc = { ++ .kind = LOC_NONE ++}; ++static Location *cur_loc = &std_loc; ++ ++/* ++ * Push location saved in LOC onto the location stack, return it. ++ * The top of that stack is the current location. ++ * Needs a matching loc_pop(). ++ */ ++Location *loc_push_restore(Location *loc) ++{ ++ assert(!loc->prev); ++ loc->prev = cur_loc; ++ cur_loc = loc; ++ return loc; ++} ++ ++/* ++ * Initialize *LOC to "nowhere", push it onto the location stack. ++ * The top of that stack is the current location. ++ * Needs a matching loc_pop(). ++ * Return LOC. ++ */ ++Location *loc_push_none(Location *loc) ++{ ++ loc->kind = LOC_NONE; ++ loc->prev = NULL; ++ return loc_push_restore(loc); ++} ++ ++/* ++ * Pop the location stack. ++ * LOC must be the current location, i.e. the top of the stack. ++ */ ++Location *loc_pop(Location *loc) ++{ ++ assert(cur_loc == loc && loc->prev); ++ cur_loc = loc->prev; ++ loc->prev = NULL; ++ return loc; ++} ++ ++/* ++ * Save the current location in LOC, return LOC. ++ */ ++Location *loc_save(Location *loc) ++{ ++ *loc = *cur_loc; ++ loc->prev = NULL; ++ return loc; ++} ++ ++/* ++ * Change the current location to the one saved in LOC. ++ */ ++void loc_restore(Location *loc) ++{ ++ Location *prev = cur_loc->prev; ++ assert(!loc->prev); ++ *cur_loc = *loc; ++ cur_loc->prev = prev; ++} ++ ++/* ++ * Change the current location to "nowhere in particular". ++ */ ++void loc_set_none(void) ++{ ++ cur_loc->kind = LOC_NONE; ++} ++ ++/* ++ * Change the current location to argument ARGV[IDX..IDX+CNT-1]. ++ */ ++void loc_set_cmdline(char **argv, int idx, int cnt) ++{ ++ cur_loc->kind = LOC_CMDLINE; ++ cur_loc->num = cnt; ++ cur_loc->ptr = argv + idx; ++} ++ ++/* ++ * Change the current location to file FNAME, line LNO. ++ */ ++void loc_set_file(const char *fname, int lno) ++{ ++ assert (fname || cur_loc->kind == LOC_FILE); ++ cur_loc->kind = LOC_FILE; ++ cur_loc->num = lno; ++ if (fname) { ++ cur_loc->ptr = fname; ++ } ++} ++ ++/* ++ * Print current location to current monitor if we have one, else to stderr. ++ */ ++static void print_loc(void) ++{ ++ const char *sep = ""; ++ int i; ++ const char *const *argp; ++ ++ if (!monitor_cur() && g_get_prgname()) { ++ error_printf("%s:", g_get_prgname()); ++ sep = " "; ++ } ++ switch (cur_loc->kind) { ++ case LOC_CMDLINE: ++ argp = cur_loc->ptr; ++ for (i = 0; i < cur_loc->num; i++) { ++ error_printf("%s%s", sep, argp[i]); ++ sep = " "; ++ } ++ error_printf(": "); ++ break; ++ case LOC_FILE: ++ error_printf("%s:", (const char *)cur_loc->ptr); ++ if (cur_loc->num) { ++ error_printf("%d:", cur_loc->num); ++ } ++ error_printf(" "); ++ break; ++ default: ++ error_printf("%s", sep); ++ } ++} ++ ++static char * ++real_time_iso8601(void) ++{ ++ g_autoptr(GDateTime) dt = g_date_time_new_now_utc(); ++ return g_date_time_format_iso8601(dt); ++} ++ ++/* ++ * Print a message to current monitor if we have one, else to stderr. ++ * @report_type is the type of message: error, warning or informational. ++ * Format arguments like vsprintf(). The resulting message should be ++ * a single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ */ ++G_GNUC_PRINTF(2, 0) ++static void vreport(report_type type, const char *fmt, va_list ap) ++{ ++ gchar *timestr; ++ ++ if (message_with_timestamp && !monitor_cur()) { ++ timestr = real_time_iso8601(); ++ error_printf("%s ", timestr); ++ g_free(timestr); ++ } ++ ++ /* Only prepend guest name if -msg guest-name and -name guest=... are set */ ++ if (error_with_guestname && error_guest_name && !monitor_cur()) { ++ error_printf("%s ", error_guest_name); ++ } ++ ++ print_loc(); ++ ++ switch (type) { ++ case REPORT_TYPE_ERROR: ++ break; ++ case REPORT_TYPE_WARNING: ++ error_printf("warning: "); ++ break; ++ case REPORT_TYPE_INFO: ++ error_printf("info: "); ++ break; ++ } ++ ++ error_vprintf(fmt, ap); ++ error_printf("\n"); ++} ++ ++/* ++ * Print an error message to current monitor if we have one, else to stderr. ++ * Format arguments like vsprintf(). The resulting message should be ++ * a single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ * It's wrong to call this in a QMP monitor. Use error_setg() there. ++ */ ++void error_vreport(const char *fmt, va_list ap) ++{ ++ vreport(REPORT_TYPE_ERROR, fmt, ap); ++} ++ ++/* ++ * Print a warning message to current monitor if we have one, else to stderr. ++ * Format arguments like vsprintf(). The resulting message should be ++ * a single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ */ ++void warn_vreport(const char *fmt, va_list ap) ++{ ++ vreport(REPORT_TYPE_WARNING, fmt, ap); ++} ++ ++/* ++ * Print an information message to current monitor if we have one, else to ++ * stderr. ++ * Format arguments like vsprintf(). The resulting message should be ++ * a single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ */ ++void info_vreport(const char *fmt, va_list ap) ++{ ++ vreport(REPORT_TYPE_INFO, fmt, ap); ++} ++ ++/* ++ * Print an error message to current monitor if we have one, else to stderr. ++ * Format arguments like sprintf(). The resulting message should be ++ * a single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ * It's wrong to call this in a QMP monitor. Use error_setg() there. ++ */ ++void error_report(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ vreport(REPORT_TYPE_ERROR, fmt, ap); ++ va_end(ap); ++} ++ ++/* ++ * Print a warning message to current monitor if we have one, else to stderr. ++ * Format arguments like sprintf(). The resulting message should be a ++ * single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ */ ++void warn_report(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ vreport(REPORT_TYPE_WARNING, fmt, ap); ++ va_end(ap); ++} ++ ++/* ++ * Print an information message to current monitor if we have one, else to ++ * stderr. ++ * Format arguments like sprintf(). The resulting message should be a ++ * single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ */ ++void info_report(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ vreport(REPORT_TYPE_INFO, fmt, ap); ++ va_end(ap); ++} ++ ++/* ++ * Like error_report(), except print just once. ++ * If *printed is false, print the message, and flip *printed to true. ++ * Return whether the message was printed. ++ */ ++bool error_report_once_cond(bool *printed, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ assert(printed); ++ if (*printed) { ++ return false; ++ } ++ *printed = true; ++ va_start(ap, fmt); ++ vreport(REPORT_TYPE_ERROR, fmt, ap); ++ va_end(ap); ++ return true; ++} ++ ++/* ++ * Like warn_report(), except print just once. ++ * If *printed is false, print the message, and flip *printed to true. ++ * Return whether the message was printed. ++ */ ++bool warn_report_once_cond(bool *printed, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ assert(printed); ++ if (*printed) { ++ return false; ++ } ++ *printed = true; ++ va_start(ap, fmt); ++ vreport(REPORT_TYPE_WARNING, fmt, ap); ++ va_end(ap); ++ return true; ++} ++ ++static char *qemu_glog_domains; ++ ++static void qemu_log_func(const gchar *log_domain, ++ GLogLevelFlags log_level, ++ const gchar *message, ++ gpointer user_data) ++{ ++ switch (log_level & G_LOG_LEVEL_MASK) { ++ case G_LOG_LEVEL_DEBUG: ++ case G_LOG_LEVEL_INFO: ++ /* ++ * Use same G_MESSAGES_DEBUG logic as glib to enable/disable debug ++ * messages ++ */ ++ if (qemu_glog_domains == NULL) { ++ break; ++ } ++ if (strcmp(qemu_glog_domains, "all") != 0 && ++ (log_domain == NULL || !strstr(qemu_glog_domains, log_domain))) { ++ break; ++ } ++ /* Fall through */ ++ case G_LOG_LEVEL_MESSAGE: ++ info_report("%s%s%s", ++ log_domain ?: "", log_domain ? ": " : "", message); ++ ++ break; ++ case G_LOG_LEVEL_WARNING: ++ warn_report("%s%s%s", ++ log_domain ?: "", log_domain ? ": " : "", message); ++ break; ++ case G_LOG_LEVEL_CRITICAL: ++ case G_LOG_LEVEL_ERROR: ++ error_report("%s%s%s", ++ log_domain ?: "", log_domain ? ": " : "", message); ++ break; ++ } ++} ++ ++void error_init(const char *argv0) ++{ ++ const char *p = strrchr(argv0, '/'); ++ ++ /* Set the program name for error_print_loc(). */ ++ g_set_prgname(p ? p + 1 : argv0); ++ ++ /* ++ * This sets up glib logging so libraries using it also print their logs ++ * through error_report(), warn_report(), info_report(). ++ */ ++ g_log_set_default_handler(qemu_log_func, NULL); ++ g_warn_if_fail(qemu_glog_domains == NULL); ++ qemu_glog_domains = g_strdup(g_getenv("G_MESSAGES_DEBUG")); ++} +diff --git a/qcow2/lib/util/error.c b/qcow2/lib/util/error.c +new file mode 100644 +index 00000000..e5e24720 +--- /dev/null ++++ b/qcow2/lib/util/error.c +@@ -0,0 +1,308 @@ ++/* ++ * QEMU Error Objects ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2011-2015 Red Hat, Inc. ++ * ++ * Authors: ++ * Anthony Liguori ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2. See ++ * the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/error-report.h" ++ ++struct Error ++{ ++ char *msg; ++ ErrorClass err_class; ++ const char *src, *func; ++ int line; ++ GString *hint; ++}; ++ ++Error *error_abort; ++Error *error_fatal; ++Error *error_warn; ++ ++static void error_handle(Error **errp, Error *err) ++{ ++ if (errp == &error_abort) { ++ fprintf(stderr, "Unexpected error in %s() at %s:%d:\n", ++ err->func, err->src, err->line); ++ error_report("%s", error_get_pretty(err)); ++ if (err->hint) { ++ error_printf("%s", err->hint->str); ++ } ++ abort(); ++ } ++ if (errp == &error_fatal) { ++ error_report_err(err); ++ exit(1); ++ } ++ if (errp == &error_warn) { ++ warn_report_err(err); ++ } else if (errp && !*errp) { ++ *errp = err; ++ } else { ++ error_free(err); ++ } ++} ++ ++G_GNUC_PRINTF(6, 0) ++static void error_setv(Error **errp, ++ const char *src, int line, const char *func, ++ ErrorClass err_class, const char *fmt, va_list ap, ++ const char *suffix) ++{ ++ Error *err; ++ int saved_errno = errno; ++ ++ if (errp == NULL) { ++ return; ++ } ++ assert(*errp == NULL); ++ ++ err = g_malloc0(sizeof(*err)); ++ err->msg = g_strdup_vprintf(fmt, ap); ++ if (suffix) { ++ char *msg = err->msg; ++ err->msg = g_strdup_printf("%s: %s", msg, suffix); ++ g_free(msg); ++ } ++ err->err_class = err_class; ++ err->src = src; ++ err->line = line; ++ err->func = func; ++ ++ error_handle(errp, err); ++ ++ errno = saved_errno; ++} ++ ++void error_set_internal(Error **errp, ++ const char *src, int line, const char *func, ++ ErrorClass err_class, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ error_setv(errp, src, line, func, err_class, fmt, ap, NULL); ++ va_end(ap); ++} ++ ++void error_setg_internal(Error **errp, ++ const char *src, int line, const char *func, ++ const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ error_setv(errp, src, line, func, ERROR_CLASS_GENERIC_ERROR, fmt, ap, NULL); ++ va_end(ap); ++} ++ ++void error_setg_errno_internal(Error **errp, ++ const char *src, int line, const char *func, ++ int os_errno, const char *fmt, ...) ++{ ++ va_list ap; ++ int saved_errno = errno; ++ ++ va_start(ap, fmt); ++ error_setv(errp, src, line, func, ERROR_CLASS_GENERIC_ERROR, fmt, ap, ++ os_errno != 0 ? strerror(os_errno) : NULL); ++ va_end(ap); ++ ++ errno = saved_errno; ++} ++ ++void error_setg_file_open_internal(Error **errp, ++ const char *src, int line, const char *func, ++ int os_errno, const char *filename) ++{ ++ error_setg_errno_internal(errp, src, line, func, os_errno, ++ "Could not open '%s'", filename); ++} ++ ++void error_vprepend(Error *const *errp, const char *fmt, va_list ap) ++{ ++ GString *newmsg; ++ ++ if (!errp) { ++ return; ++ } ++ ++ newmsg = g_string_new(NULL); ++ g_string_vprintf(newmsg, fmt, ap); ++ g_string_append(newmsg, (*errp)->msg); ++ g_free((*errp)->msg); ++ (*errp)->msg = g_string_free(newmsg, 0); ++} ++ ++void error_prepend(Error *const *errp, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ error_vprepend(errp, fmt, ap); ++ va_end(ap); ++} ++ ++void error_append_hint(Error *const *errp, const char *fmt, ...) ++{ ++ va_list ap; ++ int saved_errno = errno; ++ Error *err; ++ ++ if (!errp) { ++ return; ++ } ++ err = *errp; ++ assert(err && errp != &error_abort && errp != &error_fatal); ++ ++ if (!err->hint) { ++ err->hint = g_string_new(NULL); ++ } ++ va_start(ap, fmt); ++ g_string_append_vprintf(err->hint, fmt, ap); ++ va_end(ap); ++ ++ errno = saved_errno; ++} ++ ++#ifdef _WIN32 ++ ++void error_setg_win32_internal(Error **errp, ++ const char *src, int line, const char *func, ++ int win32_err, const char *fmt, ...) ++{ ++ va_list ap; ++ char *suffix = NULL; ++ ++ if (errp == NULL) { ++ return; ++ } ++ ++ if (win32_err != 0) { ++ suffix = g_win32_error_message(win32_err); ++ } ++ ++ va_start(ap, fmt); ++ error_setv(errp, src, line, func, ERROR_CLASS_GENERIC_ERROR, ++ fmt, ap, suffix); ++ va_end(ap); ++ ++ g_free(suffix); ++} ++ ++#endif ++ ++Error *error_copy(const Error *err) ++{ ++ Error *err_new; ++ ++ err_new = g_malloc0(sizeof(*err)); ++ err_new->msg = g_strdup(err->msg); ++ err_new->err_class = err->err_class; ++ err_new->src = err->src; ++ err_new->line = err->line; ++ err_new->func = err->func; ++ if (err->hint) { ++ err_new->hint = g_string_new(err->hint->str); ++ } ++ ++ return err_new; ++} ++ ++ErrorClass error_get_class(const Error *err) ++{ ++ return err->err_class; ++} ++ ++const char *error_get_pretty(const Error *err) ++{ ++ return err->msg; ++} ++ ++void error_report_err(Error *err) ++{ ++ error_report("%s", error_get_pretty(err)); ++ if (err->hint) { ++ error_printf("%s", err->hint->str); ++ } ++ error_free(err); ++} ++ ++void warn_report_err(Error *err) ++{ ++ warn_report("%s", error_get_pretty(err)); ++ if (err->hint) { ++ error_printf("%s", err->hint->str); ++ } ++ error_free(err); ++} ++ ++void error_reportf_err(Error *err, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ error_vprepend(&err, fmt, ap); ++ va_end(ap); ++ error_report_err(err); ++} ++ ++ ++void warn_reportf_err(Error *err, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ error_vprepend(&err, fmt, ap); ++ va_end(ap); ++ warn_report_err(err); ++} ++ ++void error_free(Error *err) ++{ ++ if (err) { ++ g_free(err->msg); ++ if (err->hint) { ++ g_string_free(err->hint, true); ++ } ++ g_free(err); ++ } ++} ++ ++void error_free_or_abort(Error **errp) ++{ ++ assert(errp && *errp); ++ error_free(*errp); ++ *errp = NULL; ++} ++ ++void error_propagate(Error **dst_errp, Error *local_err) ++{ ++ if (!local_err) { ++ return; ++ } ++ error_handle(dst_errp, local_err); ++} ++ ++void error_propagate_prepend(Error **dst_errp, Error *err, ++ const char *fmt, ...) ++{ ++ va_list ap; ++ ++ if (dst_errp && !*dst_errp) { ++ va_start(ap, fmt); ++ error_vprepend(&err, fmt, ap); ++ va_end(ap); ++ } /* else error is being ignored, don't bother with prepending */ ++ error_propagate(dst_errp, err); ++} +diff --git a/qcow2/lib/util/event_notifier-posix.c b/qcow2/lib/util/event_notifier-posix.c +new file mode 100644 +index 00000000..76420c5b +--- /dev/null ++++ b/qcow2/lib/util/event_notifier-posix.c +@@ -0,0 +1,142 @@ ++/* ++ * event notifier support ++ * ++ * Copyright Red Hat, Inc. 2010 ++ * ++ * Authors: ++ * Michael S. Tsirkin ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++#include "qemu/event_notifier.h" ++#include "qemu/main-loop.h" ++ ++#ifdef CONFIG_EVENTFD ++#include ++#endif ++ ++#ifdef CONFIG_EVENTFD ++/* ++ * Initialize @e with existing file descriptor @fd. ++ * @fd must be a genuine eventfd object, emulation with pipe won't do. ++ */ ++void event_notifier_init_fd(EventNotifier *e, int fd) ++{ ++ e->rfd = fd; ++ e->wfd = fd; ++ e->initialized = true; ++} ++#endif ++ ++int event_notifier_init(EventNotifier *e, int active) ++{ ++ int fds[2]; ++ int ret; ++ ++#ifdef CONFIG_EVENTFD ++ ret = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); ++#else ++ ret = -1; ++ errno = ENOSYS; ++#endif ++ if (ret >= 0) { ++ e->rfd = e->wfd = ret; ++ } else { ++ if (errno != ENOSYS) { ++ return -errno; ++ } ++ if (!g_unix_open_pipe(fds, FD_CLOEXEC, NULL)) { ++ return -errno; ++ } ++ if (!g_unix_set_fd_nonblocking(fds[0], true, NULL)) { ++ ret = -errno; ++ goto fail; ++ } ++ if (!g_unix_set_fd_nonblocking(fds[1], true, NULL)) { ++ ret = -errno; ++ goto fail; ++ } ++ e->rfd = fds[0]; ++ e->wfd = fds[1]; ++ } ++ e->initialized = true; ++ if (active) { ++ event_notifier_set(e); ++ } ++ return 0; ++ ++fail: ++ close(fds[0]); ++ close(fds[1]); ++ return ret; ++} ++ ++void event_notifier_cleanup(EventNotifier *e) ++{ ++ if (!e->initialized) { ++ return; ++ } ++ ++ if (e->rfd != e->wfd) { ++ close(e->rfd); ++ } ++ ++ e->rfd = -1; ++ close(e->wfd); ++ e->wfd = -1; ++ e->initialized = false; ++} ++ ++int event_notifier_get_fd(const EventNotifier *e) ++{ ++ return e->rfd; ++} ++ ++int event_notifier_get_wfd(const EventNotifier *e) ++{ ++ return e->wfd; ++} ++ ++int event_notifier_set(EventNotifier *e) ++{ ++ static const uint64_t value = 1; ++ ssize_t ret; ++ ++ if (!e->initialized) { ++ return -1; ++ } ++ ++ do { ++ ret = write(e->wfd, &value, sizeof(value)); ++ } while (ret < 0 && errno == EINTR); ++ ++ /* EAGAIN is fine, a read must be pending. */ ++ if (ret < 0 && errno != EAGAIN) { ++ return -errno; ++ } ++ return 0; ++} ++ ++int event_notifier_test_and_clear(EventNotifier *e) ++{ ++ int value; ++ ssize_t len; ++ char buffer[512]; ++ ++ if (!e->initialized) { ++ return 0; ++ } ++ ++ /* Drain the notify pipe. For eventfd, only 8 bytes will be read. */ ++ value = 0; ++ do { ++ len = read(e->rfd, buffer, sizeof(buffer)); ++ value |= (len > 0); ++ } while ((len == -1 && errno == EINTR) || len == sizeof(buffer)); ++ ++ return value; ++} +diff --git a/qcow2/lib/util/fdmon-epoll.c b/qcow2/lib/util/fdmon-epoll.c +new file mode 100644 +index 00000000..c6413cb1 +--- /dev/null ++++ b/qcow2/lib/util/fdmon-epoll.c +@@ -0,0 +1,156 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * epoll(7) file descriptor monitoring ++ */ ++ ++#include "qemu/osdep.h" ++#include ++#include "qemu/rcu_queue.h" ++#include "aio-posix.h" ++ ++/* The fd number threshold to switch to epoll */ ++#define EPOLL_ENABLE_THRESHOLD 64 ++ ++void fdmon_epoll_disable(AioContext *ctx) ++{ ++ if (ctx->epollfd >= 0) { ++ close(ctx->epollfd); ++ ctx->epollfd = -1; ++ } ++ ++ /* Switch back */ ++ ctx->fdmon_ops = &fdmon_poll_ops; ++} ++ ++static inline int epoll_events_from_pfd(int pfd_events) ++{ ++ return (pfd_events & G_IO_IN ? EPOLLIN : 0) | ++ (pfd_events & G_IO_OUT ? EPOLLOUT : 0) | ++ (pfd_events & G_IO_HUP ? EPOLLHUP : 0) | ++ (pfd_events & G_IO_ERR ? EPOLLERR : 0); ++} ++ ++static void fdmon_epoll_update(AioContext *ctx, ++ AioHandler *old_node, ++ AioHandler *new_node) ++{ ++ struct epoll_event event = { ++ .data.ptr = new_node, ++ .events = new_node ? epoll_events_from_pfd(new_node->pfd.events) : 0, ++ }; ++ int r; ++ ++ if (!new_node) { ++ r = epoll_ctl(ctx->epollfd, EPOLL_CTL_DEL, old_node->pfd.fd, &event); ++ } else if (!old_node) { ++ r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, new_node->pfd.fd, &event); ++ } else { ++ r = epoll_ctl(ctx->epollfd, EPOLL_CTL_MOD, new_node->pfd.fd, &event); ++ } ++ ++ if (r) { ++ fdmon_epoll_disable(ctx); ++ } ++} ++ ++static int fdmon_epoll_wait(AioContext *ctx, AioHandlerList *ready_list, ++ int64_t timeout) ++{ ++ GPollFD pfd = { ++ .fd = ctx->epollfd, ++ .events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR, ++ }; ++ AioHandler *node; ++ int i, ret = 0; ++ struct epoll_event events[128]; ++ ++ if (timeout > 0) { ++ ret = qemu_poll_ns(&pfd, 1, timeout); ++ if (ret > 0) { ++ timeout = 0; ++ } ++ } ++ if (timeout <= 0 || ret > 0) { ++ ret = epoll_wait(ctx->epollfd, events, ++ ARRAY_SIZE(events), ++ timeout); ++ if (ret <= 0) { ++ goto out; ++ } ++ for (i = 0; i < ret; i++) { ++ int ev = events[i].events; ++ int revents = (ev & EPOLLIN ? G_IO_IN : 0) | ++ (ev & EPOLLOUT ? G_IO_OUT : 0) | ++ (ev & EPOLLHUP ? G_IO_HUP : 0) | ++ (ev & EPOLLERR ? G_IO_ERR : 0); ++ ++ node = events[i].data.ptr; ++ aio_add_ready_handler(ready_list, node, revents); ++ } ++ } ++out: ++ return ret; ++} ++ ++static const FDMonOps fdmon_epoll_ops = { ++ .update = fdmon_epoll_update, ++ .wait = fdmon_epoll_wait, ++ .need_wait = aio_poll_disabled, ++}; ++ ++static bool fdmon_epoll_try_enable(AioContext *ctx) ++{ ++ AioHandler *node; ++ struct epoll_event event; ++ ++ QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { ++ int r; ++ if (QLIST_IS_INSERTED(node, node_deleted) || !node->pfd.events) { ++ continue; ++ } ++ event.events = epoll_events_from_pfd(node->pfd.events); ++ event.data.ptr = node; ++ r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event); ++ if (r) { ++ return false; ++ } ++ } ++ ++ ctx->fdmon_ops = &fdmon_epoll_ops; ++ return true; ++} ++ ++bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd) ++{ ++ bool ok; ++ ++ if (ctx->epollfd < 0) { ++ return false; ++ } ++ ++ if (npfd < EPOLL_ENABLE_THRESHOLD) { ++ return false; ++ } ++ ++ /* The list must not change while we add fds to epoll */ ++ if (!qemu_lockcnt_dec_if_lock(&ctx->list_lock)) { ++ return false; ++ } ++ ++ ok = fdmon_epoll_try_enable(ctx); ++ ++ qemu_lockcnt_inc_and_unlock(&ctx->list_lock); ++ ++ if (!ok) { ++ fdmon_epoll_disable(ctx); ++ } ++ return ok; ++} ++ ++void fdmon_epoll_setup(AioContext *ctx) ++{ ++ ctx->epollfd = epoll_create1(EPOLL_CLOEXEC); ++ if (ctx->epollfd == -1) { ++ fprintf(stderr, "Failed to create epoll instance: %s", strerror(errno)); ++ } ++} +diff --git a/qcow2/lib/util/fdmon-poll.c b/qcow2/lib/util/fdmon-poll.c +new file mode 100644 +index 00000000..17df917c +--- /dev/null ++++ b/qcow2/lib/util/fdmon-poll.c +@@ -0,0 +1,107 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * poll(2) file descriptor monitoring ++ * ++ * Uses ppoll(2) when available, g_poll() otherwise. ++ */ ++ ++#include "qemu/osdep.h" ++#include "aio-posix.h" ++#include "qemu/rcu_queue.h" ++ ++/* ++ * These thread-local variables are used only in fdmon_poll_wait() around the ++ * call to the poll() system call. In particular they are not used while ++ * aio_poll is performing callbacks, which makes it much easier to think about ++ * reentrancy! ++ * ++ * Stack-allocated arrays would be perfect but they have size limitations; ++ * heap allocation is expensive enough that we want to reuse arrays across ++ * calls to aio_poll(). And because poll() has to be called without holding ++ * any lock, the arrays cannot be stored in AioContext. Thread-local data ++ * has none of the disadvantages of these three options. ++ */ ++static __thread GPollFD *pollfds; ++static __thread AioHandler **nodes; ++static __thread unsigned npfd, nalloc; ++static __thread Notifier pollfds_cleanup_notifier; ++ ++static void pollfds_cleanup(Notifier *n, void *unused) ++{ ++ g_assert(npfd == 0); ++ g_free(pollfds); ++ g_free(nodes); ++ nalloc = 0; ++} ++ ++static void add_pollfd(AioHandler *node) ++{ ++ if (npfd == nalloc) { ++ if (nalloc == 0) { ++ pollfds_cleanup_notifier.notify = pollfds_cleanup; ++ qemu_thread_atexit_add(&pollfds_cleanup_notifier); ++ nalloc = 8; ++ } else { ++ g_assert(nalloc <= INT_MAX); ++ nalloc *= 2; ++ } ++ pollfds = g_renew(GPollFD, pollfds, nalloc); ++ nodes = g_renew(AioHandler *, nodes, nalloc); ++ } ++ nodes[npfd] = node; ++ pollfds[npfd] = (GPollFD) { ++ .fd = node->pfd.fd, ++ .events = node->pfd.events, ++ }; ++ npfd++; ++} ++ ++static int fdmon_poll_wait(AioContext *ctx, AioHandlerList *ready_list, ++ int64_t timeout) ++{ ++ AioHandler *node; ++ int ret; ++ ++ assert(npfd == 0); ++ ++ QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { ++ if (!QLIST_IS_INSERTED(node, node_deleted) && node->pfd.events) { ++ add_pollfd(node); ++ } ++ } ++ ++ /* epoll(7) is faster above a certain number of fds */ ++ if (fdmon_epoll_try_upgrade(ctx, npfd)) { ++ npfd = 0; /* we won't need pollfds[], reset npfd */ ++ return ctx->fdmon_ops->wait(ctx, ready_list, timeout); ++ } ++ ++ ret = qemu_poll_ns(pollfds, npfd, timeout); ++ if (ret > 0) { ++ int i; ++ ++ for (i = 0; i < npfd; i++) { ++ int revents = pollfds[i].revents; ++ ++ if (revents) { ++ aio_add_ready_handler(ready_list, nodes[i], revents); ++ } ++ } ++ } ++ ++ npfd = 0; ++ return ret; ++} ++ ++static void fdmon_poll_update(AioContext *ctx, ++ AioHandler *old_node, ++ AioHandler *new_node) ++{ ++ /* Do nothing, AioHandler already contains the state we'll need */ ++} ++ ++const FDMonOps fdmon_poll_ops = { ++ .update = fdmon_poll_update, ++ .wait = fdmon_poll_wait, ++ .need_wait = aio_poll_disabled, ++}; +diff --git a/qcow2/lib/util/hbitmap.c b/qcow2/lib/util/hbitmap.c +new file mode 100644 +index 00000000..6d6e1b59 +--- /dev/null ++++ b/qcow2/lib/util/hbitmap.c +@@ -0,0 +1,955 @@ ++/* ++ * Hierarchical Bitmap Data Type ++ * ++ * Copyright Red Hat, Inc., 2012 ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/hbitmap.h" ++#include "qemu/host-utils.h" ++#include "trace.h" ++#include "crypto/hash.h" ++ ++/* HBitmaps provides an array of bits. The bits are stored as usual in an ++ * array of unsigned longs, but HBitmap is also optimized to provide fast ++ * iteration over set bits; going from one bit to the next is O(logB n) ++ * worst case, with B = sizeof(long) * CHAR_BIT: the result is low enough ++ * that the number of levels is in fact fixed. ++ * ++ * In order to do this, it stacks multiple bitmaps with progressively coarser ++ * granularity; in all levels except the last, bit N is set iff the N-th ++ * unsigned long is nonzero in the immediately next level. When iteration ++ * completes on the last level it can examine the 2nd-last level to quickly ++ * skip entire words, and even do so recursively to skip blocks of 64 words or ++ * powers thereof (32 on 32-bit machines). ++ * ++ * Given an index in the bitmap, it can be split in group of bits like ++ * this (for the 64-bit case): ++ * ++ * bits 0-57 => word in the last bitmap | bits 58-63 => bit in the word ++ * bits 0-51 => word in the 2nd-last bitmap | bits 52-57 => bit in the word ++ * bits 0-45 => word in the 3rd-last bitmap | bits 46-51 => bit in the word ++ * ++ * So it is easy to move up simply by shifting the index right by ++ * log2(BITS_PER_LONG) bits. To move down, you shift the index left ++ * similarly, and add the word index within the group. Iteration uses ++ * ffs (find first set bit) to find the next word to examine; this ++ * operation can be done in constant time in most current architectures. ++ * ++ * Setting or clearing a range of m bits on all levels, the work to perform ++ * is O(m + m/W + m/W^2 + ...), which is O(m) like on a regular bitmap. ++ * ++ * When iterating on a bitmap, each bit (on any level) is only visited ++ * once. Hence, The total cost of visiting a bitmap with m bits in it is ++ * the number of bits that are set in all bitmaps. Unless the bitmap is ++ * extremely sparse, this is also O(m + m/W + m/W^2 + ...), so the amortized ++ * cost of advancing from one bit to the next is usually constant (worst case ++ * O(logB n) as in the non-amortized complexity). ++ */ ++ ++struct HBitmap { ++ /* ++ * Size of the bitmap, as requested in hbitmap_alloc or in hbitmap_truncate. ++ */ ++ uint64_t orig_size; ++ ++ /* Number of total bits in the bottom level. */ ++ uint64_t size; ++ ++ /* Number of set bits in the bottom level. */ ++ uint64_t count; ++ ++ /* A scaling factor. Given a granularity of G, each bit in the bitmap will ++ * will actually represent a group of 2^G elements. Each operation on a ++ * range of bits first rounds the bits to determine which group they land ++ * in, and then affect the entire page; iteration will only visit the first ++ * bit of each group. Here is an example of operations in a size-16, ++ * granularity-1 HBitmap: ++ * ++ * initial state 00000000 ++ * set(start=0, count=9) 11111000 (iter: 0, 2, 4, 6, 8) ++ * reset(start=1, count=3) 00111000 (iter: 4, 6, 8) ++ * set(start=9, count=2) 00111100 (iter: 4, 6, 8, 10) ++ * reset(start=5, count=5) 00000000 ++ * ++ * From an implementation point of view, when setting or resetting bits, ++ * the bitmap will scale bit numbers right by this amount of bits. When ++ * iterating, the bitmap will scale bit numbers left by this amount of ++ * bits. ++ */ ++ int granularity; ++ ++ /* A meta dirty bitmap to track the dirtiness of bits in this HBitmap. */ ++ HBitmap *meta; ++ ++ /* A number of progressively less coarse bitmaps (i.e. level 0 is the ++ * coarsest). Each bit in level N represents a word in level N+1 that ++ * has a set bit, except the last level where each bit represents the ++ * actual bitmap. ++ * ++ * Note that all bitmaps have the same number of levels. Even a 1-bit ++ * bitmap will still allocate HBITMAP_LEVELS arrays. ++ */ ++ unsigned long *levels[HBITMAP_LEVELS]; ++ ++ /* The length of each levels[] array. */ ++ uint64_t sizes[HBITMAP_LEVELS]; ++}; ++ ++/* Advance hbi to the next nonzero word and return it. hbi->pos ++ * is updated. Returns zero if we reach the end of the bitmap. ++ */ ++static unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) ++{ ++ size_t pos = hbi->pos; ++ const HBitmap *hb = hbi->hb; ++ unsigned i = HBITMAP_LEVELS - 1; ++ ++ unsigned long cur; ++ do { ++ i--; ++ pos >>= BITS_PER_LEVEL; ++ cur = hbi->cur[i] & hb->levels[i][pos]; ++ } while (cur == 0); ++ ++ /* Check for end of iteration. We always use fewer than BITS_PER_LONG ++ * bits in the level 0 bitmap; thus we can repurpose the most significant ++ * bit as a sentinel. The sentinel is set in hbitmap_alloc and ensures ++ * that the above loop ends even without an explicit check on i. ++ */ ++ ++ if (i == 0 && cur == (1UL << (BITS_PER_LONG - 1))) { ++ return 0; ++ } ++ for (; i < HBITMAP_LEVELS - 1; i++) { ++ /* Shift back pos to the left, matching the right shifts above. ++ * The index of this word's least significant set bit provides ++ * the low-order bits. ++ */ ++ assert(cur); ++ pos = (pos << BITS_PER_LEVEL) + ctzl(cur); ++ hbi->cur[i] = cur & (cur - 1); ++ ++ /* Set up next level for iteration. */ ++ cur = hb->levels[i + 1][pos]; ++ } ++ ++ hbi->pos = pos; ++ trace_hbitmap_iter_skip_words(hbi->hb, hbi, pos, cur); ++ ++ assert(cur); ++ return cur; ++} ++ ++int64_t hbitmap_iter_next(HBitmapIter *hbi) ++{ ++ unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1] & ++ hbi->hb->levels[HBITMAP_LEVELS - 1][hbi->pos]; ++ int64_t item; ++ ++ if (cur == 0) { ++ cur = hbitmap_iter_skip_words(hbi); ++ if (cur == 0) { ++ return -1; ++ } ++ } ++ ++ /* The next call will resume work from the next bit. */ ++ hbi->cur[HBITMAP_LEVELS - 1] = cur & (cur - 1); ++ item = ((uint64_t)hbi->pos << BITS_PER_LEVEL) + ctzl(cur); ++ ++ return item << hbi->granularity; ++} ++ ++void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first) ++{ ++ unsigned i, bit; ++ uint64_t pos; ++ ++ hbi->hb = hb; ++ pos = first >> hb->granularity; ++ assert(pos < hb->size); ++ hbi->pos = pos >> BITS_PER_LEVEL; ++ hbi->granularity = hb->granularity; ++ ++ for (i = HBITMAP_LEVELS; i-- > 0; ) { ++ bit = pos & (BITS_PER_LONG - 1); ++ pos >>= BITS_PER_LEVEL; ++ ++ /* Drop bits representing items before first. */ ++ hbi->cur[i] = hb->levels[i][pos] & ~((1UL << bit) - 1); ++ ++ /* We have already added level i+1, so the lowest set bit has ++ * been processed. Clear it. ++ */ ++ if (i != HBITMAP_LEVELS - 1) { ++ hbi->cur[i] &= ~(1UL << bit); ++ } ++ } ++} ++ ++int64_t hbitmap_next_dirty(const HBitmap *hb, int64_t start, int64_t count) ++{ ++ HBitmapIter hbi; ++ int64_t first_dirty_off; ++ uint64_t end; ++ ++ assert(start >= 0 && count >= 0); ++ ++ if (start >= hb->orig_size || count == 0) { ++ return -1; ++ } ++ ++ end = count > hb->orig_size - start ? hb->orig_size : start + count; ++ ++ hbitmap_iter_init(&hbi, hb, start); ++ first_dirty_off = hbitmap_iter_next(&hbi); ++ ++ if (first_dirty_off < 0 || first_dirty_off >= end) { ++ return -1; ++ } ++ ++ return MAX(start, first_dirty_off); ++} ++ ++int64_t hbitmap_next_zero(const HBitmap *hb, int64_t start, int64_t count) ++{ ++ size_t pos = (start >> hb->granularity) >> BITS_PER_LEVEL; ++ unsigned long *last_lev = hb->levels[HBITMAP_LEVELS - 1]; ++ unsigned long cur = last_lev[pos]; ++ unsigned start_bit_offset; ++ uint64_t end_bit, sz; ++ int64_t res; ++ ++ assert(start >= 0 && count >= 0); ++ ++ if (start >= hb->orig_size || count == 0) { ++ return -1; ++ } ++ ++ end_bit = count > hb->orig_size - start ? ++ hb->size : ++ ((start + count - 1) >> hb->granularity) + 1; ++ sz = (end_bit + BITS_PER_LONG - 1) >> BITS_PER_LEVEL; ++ ++ /* There may be some zero bits in @cur before @start. We are not interested ++ * in them, let's set them. ++ */ ++ start_bit_offset = (start >> hb->granularity) & (BITS_PER_LONG - 1); ++ cur |= (1UL << start_bit_offset) - 1; ++ assert((start >> hb->granularity) < hb->size); ++ ++ if (cur == (unsigned long)-1) { ++ do { ++ pos++; ++ } while (pos < sz && last_lev[pos] == (unsigned long)-1); ++ ++ if (pos >= sz) { ++ return -1; ++ } ++ ++ cur = last_lev[pos]; ++ } ++ ++ res = (pos << BITS_PER_LEVEL) + ctol(cur); ++ if (res >= end_bit) { ++ return -1; ++ } ++ ++ res = res << hb->granularity; ++ if (res < start) { ++ assert(((start - res) >> hb->granularity) == 0); ++ return start; ++ } ++ ++ return res; ++} ++ ++bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end, ++ int64_t max_dirty_count, ++ int64_t *dirty_start, int64_t *dirty_count) ++{ ++ int64_t next_zero; ++ ++ assert(start >= 0 && end >= 0 && max_dirty_count > 0); ++ ++ end = MIN(end, hb->orig_size); ++ if (start >= end) { ++ return false; ++ } ++ ++ start = hbitmap_next_dirty(hb, start, end - start); ++ if (start < 0) { ++ return false; ++ } ++ ++ end = start + MIN(end - start, max_dirty_count); ++ ++ next_zero = hbitmap_next_zero(hb, start, end - start); ++ if (next_zero >= 0) { ++ end = next_zero; ++ } ++ ++ *dirty_start = start; ++ *dirty_count = end - start; ++ ++ return true; ++} ++ ++bool hbitmap_status(const HBitmap *hb, int64_t start, int64_t count, ++ int64_t *pnum) ++{ ++ int64_t next_dirty, next_zero; ++ ++ assert(start >= 0); ++ assert(count > 0); ++ assert(start + count <= hb->orig_size); ++ ++ next_dirty = hbitmap_next_dirty(hb, start, count); ++ if (next_dirty == -1) { ++ *pnum = count; ++ return false; ++ } ++ ++ if (next_dirty > start) { ++ *pnum = next_dirty - start; ++ return false; ++ } ++ ++ assert(next_dirty == start); ++ ++ next_zero = hbitmap_next_zero(hb, start, count); ++ if (next_zero == -1) { ++ *pnum = count; ++ return true; ++ } ++ ++ assert(next_zero > start); ++ *pnum = next_zero - start; ++ return true; ++} ++ ++bool hbitmap_empty(const HBitmap *hb) ++{ ++ return hb->count == 0; ++} ++ ++int hbitmap_granularity(const HBitmap *hb) ++{ ++ return hb->granularity; ++} ++ ++uint64_t hbitmap_count(const HBitmap *hb) ++{ ++ return hb->count << hb->granularity; ++} ++ ++/** ++ * hbitmap_iter_next_word: ++ * @hbi: HBitmapIter to operate on. ++ * @p_cur: Location where to store the next non-zero word. ++ * ++ * Return the index of the next nonzero word that is set in @hbi's ++ * associated HBitmap, and set *p_cur to the content of that word ++ * (bits before the index that was passed to hbitmap_iter_init are ++ * trimmed on the first call). Return -1, and set *p_cur to zero, ++ * if all remaining words are zero. ++ */ ++static size_t hbitmap_iter_next_word(HBitmapIter *hbi, unsigned long *p_cur) ++{ ++ unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1]; ++ ++ if (cur == 0) { ++ cur = hbitmap_iter_skip_words(hbi); ++ if (cur == 0) { ++ *p_cur = 0; ++ return -1; ++ } ++ } ++ ++ /* The next call will resume work from the next word. */ ++ hbi->cur[HBITMAP_LEVELS - 1] = 0; ++ *p_cur = cur; ++ return hbi->pos; ++} ++ ++/* Count the number of set bits between start and end, not accounting for ++ * the granularity. Also an example of how to use hbitmap_iter_next_word. ++ */ ++static uint64_t hb_count_between(HBitmap *hb, uint64_t start, uint64_t last) ++{ ++ HBitmapIter hbi; ++ uint64_t count = 0; ++ uint64_t end = last + 1; ++ unsigned long cur; ++ size_t pos; ++ ++ hbitmap_iter_init(&hbi, hb, start << hb->granularity); ++ for (;;) { ++ pos = hbitmap_iter_next_word(&hbi, &cur); ++ if (pos >= (end >> BITS_PER_LEVEL)) { ++ break; ++ } ++ count += ctpopl(cur); ++ } ++ ++ if (pos == (end >> BITS_PER_LEVEL)) { ++ /* Drop bits representing the END-th and subsequent items. */ ++ int bit = end & (BITS_PER_LONG - 1); ++ cur &= (1UL << bit) - 1; ++ count += ctpopl(cur); ++ } ++ ++ return count; ++} ++ ++/* Setting starts at the last layer and propagates up if an element ++ * changes. ++ */ ++static inline bool hb_set_elem(unsigned long *elem, uint64_t start, uint64_t last) ++{ ++ unsigned long mask; ++ unsigned long old; ++ ++ assert((last >> BITS_PER_LEVEL) == (start >> BITS_PER_LEVEL)); ++ assert(start <= last); ++ ++ mask = 2UL << (last & (BITS_PER_LONG - 1)); ++ mask -= 1UL << (start & (BITS_PER_LONG - 1)); ++ old = *elem; ++ *elem |= mask; ++ return old != *elem; ++} ++ ++/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... ++ * Returns true if at least one bit is changed. */ ++static bool hb_set_between(HBitmap *hb, int level, uint64_t start, ++ uint64_t last) ++{ ++ size_t pos = start >> BITS_PER_LEVEL; ++ size_t lastpos = last >> BITS_PER_LEVEL; ++ bool changed = false; ++ size_t i; ++ ++ i = pos; ++ if (i < lastpos) { ++ uint64_t next = (start | (BITS_PER_LONG - 1)) + 1; ++ changed |= hb_set_elem(&hb->levels[level][i], start, next - 1); ++ for (;;) { ++ start = next; ++ next += BITS_PER_LONG; ++ if (++i == lastpos) { ++ break; ++ } ++ changed |= (hb->levels[level][i] == 0); ++ hb->levels[level][i] = ~0UL; ++ } ++ } ++ changed |= hb_set_elem(&hb->levels[level][i], start, last); ++ ++ /* If there was any change in this layer, we may have to update ++ * the one above. ++ */ ++ if (level > 0 && changed) { ++ hb_set_between(hb, level - 1, pos, lastpos); ++ } ++ return changed; ++} ++ ++void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count) ++{ ++ /* Compute range in the last layer. */ ++ uint64_t first, n; ++ uint64_t last = start + count - 1; ++ ++ if (count == 0) { ++ return; ++ } ++ ++ trace_hbitmap_set(hb, start, count, ++ start >> hb->granularity, last >> hb->granularity); ++ ++ first = start >> hb->granularity; ++ last >>= hb->granularity; ++ assert(last < hb->size); ++ n = last - first + 1; ++ ++ hb->count += n - hb_count_between(hb, first, last); ++ if (hb_set_between(hb, HBITMAP_LEVELS - 1, first, last) && ++ hb->meta) { ++ hbitmap_set(hb->meta, start, count); ++ } ++} ++ ++/* Resetting works the other way round: propagate up if the new ++ * value is zero. ++ */ ++static inline bool hb_reset_elem(unsigned long *elem, uint64_t start, uint64_t last) ++{ ++ unsigned long mask; ++ bool blanked; ++ ++ assert((last >> BITS_PER_LEVEL) == (start >> BITS_PER_LEVEL)); ++ assert(start <= last); ++ ++ mask = 2UL << (last & (BITS_PER_LONG - 1)); ++ mask -= 1UL << (start & (BITS_PER_LONG - 1)); ++ blanked = *elem != 0 && ((*elem & ~mask) == 0); ++ *elem &= ~mask; ++ return blanked; ++} ++ ++/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... ++ * Returns true if at least one bit is changed. */ ++static bool hb_reset_between(HBitmap *hb, int level, uint64_t start, ++ uint64_t last) ++{ ++ size_t pos = start >> BITS_PER_LEVEL; ++ size_t lastpos = last >> BITS_PER_LEVEL; ++ bool changed = false; ++ size_t i; ++ ++ i = pos; ++ if (i < lastpos) { ++ uint64_t next = (start | (BITS_PER_LONG - 1)) + 1; ++ ++ /* Here we need a more complex test than when setting bits. Even if ++ * something was changed, we must not blank bits in the upper level ++ * unless the lower-level word became entirely zero. So, remove pos ++ * from the upper-level range if bits remain set. ++ */ ++ if (hb_reset_elem(&hb->levels[level][i], start, next - 1)) { ++ changed = true; ++ } else { ++ pos++; ++ } ++ ++ for (;;) { ++ start = next; ++ next += BITS_PER_LONG; ++ if (++i == lastpos) { ++ break; ++ } ++ changed |= (hb->levels[level][i] != 0); ++ hb->levels[level][i] = 0UL; ++ } ++ } ++ ++ /* Same as above, this time for lastpos. */ ++ if (hb_reset_elem(&hb->levels[level][i], start, last)) { ++ changed = true; ++ } else { ++ lastpos--; ++ } ++ ++ if (level > 0 && changed) { ++ hb_reset_between(hb, level - 1, pos, lastpos); ++ } ++ ++ return changed; ++ ++} ++ ++void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count) ++{ ++ /* Compute range in the last layer. */ ++ uint64_t first; ++ uint64_t last = start + count - 1; ++ uint64_t gran = 1ULL << hb->granularity; ++ ++ if (count == 0) { ++ return; ++ } ++ ++ assert(QEMU_IS_ALIGNED(start, gran)); ++ assert(QEMU_IS_ALIGNED(count, gran) || (start + count == hb->orig_size)); ++ ++ trace_hbitmap_reset(hb, start, count, ++ start >> hb->granularity, last >> hb->granularity); ++ ++ first = start >> hb->granularity; ++ last >>= hb->granularity; ++ assert(last < hb->size); ++ ++ hb->count -= hb_count_between(hb, first, last); ++ if (hb_reset_between(hb, HBITMAP_LEVELS - 1, first, last) && ++ hb->meta) { ++ hbitmap_set(hb->meta, start, count); ++ } ++} ++ ++void hbitmap_reset_all(HBitmap *hb) ++{ ++ unsigned int i; ++ ++ /* Same as hbitmap_alloc() except for memset() instead of malloc() */ ++ for (i = HBITMAP_LEVELS; --i >= 1; ) { ++ memset(hb->levels[i], 0, hb->sizes[i] * sizeof(unsigned long)); ++ } ++ ++ hb->levels[0][0] = 1UL << (BITS_PER_LONG - 1); ++ hb->count = 0; ++} ++ ++bool hbitmap_is_serializable(const HBitmap *hb) ++{ ++ /* Every serialized chunk must be aligned to 64 bits so that endianness ++ * requirements can be fulfilled on both 64 bit and 32 bit hosts. ++ * We have hbitmap_serialization_align() which converts this ++ * alignment requirement from bitmap bits to items covered (e.g. sectors). ++ * That value is: ++ * 64 << hb->granularity ++ * Since this value must not exceed UINT64_MAX, hb->granularity must be ++ * less than 58 (== 64 - 6, where 6 is ld(64), i.e. 1 << 6 == 64). ++ * ++ * In order for hbitmap_serialization_align() to always return a ++ * meaningful value, bitmaps that are to be serialized must have a ++ * granularity of less than 58. */ ++ ++ return hb->granularity < 58; ++} ++ ++bool hbitmap_get(const HBitmap *hb, uint64_t item) ++{ ++ /* Compute position and bit in the last layer. */ ++ uint64_t pos = item >> hb->granularity; ++ unsigned long bit = 1UL << (pos & (BITS_PER_LONG - 1)); ++ assert(pos < hb->size); ++ ++ return (hb->levels[HBITMAP_LEVELS - 1][pos >> BITS_PER_LEVEL] & bit) != 0; ++} ++ ++uint64_t hbitmap_serialization_align(const HBitmap *hb) ++{ ++ assert(hbitmap_is_serializable(hb)); ++ ++ /* Require at least 64 bit granularity to be safe on both 64 bit and 32 bit ++ * hosts. */ ++ return UINT64_C(64) << hb->granularity; ++} ++ ++/* Start should be aligned to serialization granularity, chunk size should be ++ * aligned to serialization granularity too, except for last chunk. ++ */ ++static void serialization_chunk(const HBitmap *hb, ++ uint64_t start, uint64_t count, ++ unsigned long **first_el, uint64_t *el_count) ++{ ++ uint64_t last = start + count - 1; ++ uint64_t gran = hbitmap_serialization_align(hb); ++ ++ assert((start & (gran - 1)) == 0); ++ assert((last >> hb->granularity) < hb->size); ++ if ((last >> hb->granularity) != hb->size - 1) { ++ assert((count & (gran - 1)) == 0); ++ } ++ ++ start = (start >> hb->granularity) >> BITS_PER_LEVEL; ++ last = (last >> hb->granularity) >> BITS_PER_LEVEL; ++ ++ *first_el = &hb->levels[HBITMAP_LEVELS - 1][start]; ++ *el_count = last - start + 1; ++} ++ ++uint64_t hbitmap_serialization_size(const HBitmap *hb, ++ uint64_t start, uint64_t count) ++{ ++ uint64_t el_count; ++ unsigned long *cur; ++ ++ if (!count) { ++ return 0; ++ } ++ serialization_chunk(hb, start, count, &cur, &el_count); ++ ++ return el_count * sizeof(unsigned long); ++} ++ ++void hbitmap_serialize_part(const HBitmap *hb, uint8_t *buf, ++ uint64_t start, uint64_t count) ++{ ++ uint64_t el_count; ++ unsigned long *cur, *end; ++ ++ if (!count) { ++ return; ++ } ++ serialization_chunk(hb, start, count, &cur, &el_count); ++ end = cur + el_count; ++ ++ while (cur != end) { ++ unsigned long el = ++ (BITS_PER_LONG == 32 ? cpu_to_le32(*cur) : cpu_to_le64(*cur)); ++ ++ memcpy(buf, &el, sizeof(el)); ++ buf += sizeof(el); ++ cur++; ++ } ++} ++ ++void hbitmap_deserialize_part(HBitmap *hb, uint8_t *buf, ++ uint64_t start, uint64_t count, ++ bool finish) ++{ ++ uint64_t el_count; ++ unsigned long *cur, *end; ++ ++ if (!count) { ++ return; ++ } ++ serialization_chunk(hb, start, count, &cur, &el_count); ++ end = cur + el_count; ++ ++ while (cur != end) { ++ memcpy(cur, buf, sizeof(*cur)); ++ ++ if (BITS_PER_LONG == 32) { ++ le32_to_cpus((uint32_t *)cur); ++ } else { ++ le64_to_cpus((uint64_t *)cur); ++ } ++ ++ buf += sizeof(unsigned long); ++ cur++; ++ } ++ if (finish) { ++ hbitmap_deserialize_finish(hb); ++ } ++} ++ ++void hbitmap_deserialize_zeroes(HBitmap *hb, uint64_t start, uint64_t count, ++ bool finish) ++{ ++ uint64_t el_count; ++ unsigned long *first; ++ ++ if (!count) { ++ return; ++ } ++ serialization_chunk(hb, start, count, &first, &el_count); ++ ++ memset(first, 0, el_count * sizeof(unsigned long)); ++ if (finish) { ++ hbitmap_deserialize_finish(hb); ++ } ++} ++ ++void hbitmap_deserialize_ones(HBitmap *hb, uint64_t start, uint64_t count, ++ bool finish) ++{ ++ uint64_t el_count; ++ unsigned long *first; ++ ++ if (!count) { ++ return; ++ } ++ serialization_chunk(hb, start, count, &first, &el_count); ++ ++ memset(first, 0xff, el_count * sizeof(unsigned long)); ++ if (finish) { ++ hbitmap_deserialize_finish(hb); ++ } ++} ++ ++void hbitmap_deserialize_finish(HBitmap *bitmap) ++{ ++ int64_t i, size, prev_size; ++ int lev; ++ ++ /* restore levels starting from penultimate to zero level, assuming ++ * that the last level is ok */ ++ size = MAX((bitmap->size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1); ++ for (lev = HBITMAP_LEVELS - 1; lev-- > 0; ) { ++ prev_size = size; ++ size = MAX((size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1); ++ memset(bitmap->levels[lev], 0, size * sizeof(unsigned long)); ++ ++ for (i = 0; i < prev_size; ++i) { ++ if (bitmap->levels[lev + 1][i]) { ++ bitmap->levels[lev][i >> BITS_PER_LEVEL] |= ++ 1UL << (i & (BITS_PER_LONG - 1)); ++ } ++ } ++ } ++ ++ bitmap->levels[0][0] |= 1UL << (BITS_PER_LONG - 1); ++ bitmap->count = hb_count_between(bitmap, 0, bitmap->size - 1); ++} ++ ++void hbitmap_free(HBitmap *hb) ++{ ++ unsigned i; ++ assert(!hb->meta); ++ for (i = HBITMAP_LEVELS; i-- > 0; ) { ++ g_free(hb->levels[i]); ++ } ++ g_free(hb); ++} ++ ++HBitmap *hbitmap_alloc(uint64_t size, int granularity) ++{ ++ HBitmap *hb = g_new0(struct HBitmap, 1); ++ unsigned i; ++ ++ assert(size <= INT64_MAX); ++ hb->orig_size = size; ++ ++ assert(granularity >= 0 && granularity < 64); ++ size = (size + (1ULL << granularity) - 1) >> granularity; ++ assert(size <= ((uint64_t)1 << HBITMAP_LOG_MAX_SIZE)); ++ ++ hb->size = size; ++ hb->granularity = granularity; ++ for (i = HBITMAP_LEVELS; i-- > 0; ) { ++ size = MAX((size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1); ++ hb->sizes[i] = size; ++ hb->levels[i] = g_new0(unsigned long, size); ++ } ++ ++ /* We necessarily have free bits in level 0 due to the definition ++ * of HBITMAP_LEVELS, so use one for a sentinel. This speeds up ++ * hbitmap_iter_skip_words. ++ */ ++ assert(size == 1); ++ hb->levels[0][0] |= 1UL << (BITS_PER_LONG - 1); ++ return hb; ++} ++ ++void hbitmap_truncate(HBitmap *hb, uint64_t size) ++{ ++ bool shrink; ++ unsigned i; ++ uint64_t num_elements = size; ++ uint64_t old; ++ ++ assert(size <= INT64_MAX); ++ hb->orig_size = size; ++ ++ /* Size comes in as logical elements, adjust for granularity. */ ++ size = (size + (1ULL << hb->granularity) - 1) >> hb->granularity; ++ assert(size <= ((uint64_t)1 << HBITMAP_LOG_MAX_SIZE)); ++ shrink = size < hb->size; ++ ++ /* bit sizes are identical; nothing to do. */ ++ if (size == hb->size) { ++ return; ++ } ++ ++ /* If we're losing bits, let's clear those bits before we invalidate all of ++ * our invariants. This helps keep the bitcount consistent, and will prevent ++ * us from carrying around garbage bits beyond the end of the map. ++ */ ++ if (shrink) { ++ /* Don't clear partial granularity groups; ++ * start at the first full one. */ ++ uint64_t start = ROUND_UP(num_elements, UINT64_C(1) << hb->granularity); ++ uint64_t fix_count = (hb->size << hb->granularity) - start; ++ ++ assert(fix_count); ++ hbitmap_reset(hb, start, fix_count); ++ } ++ ++ hb->size = size; ++ for (i = HBITMAP_LEVELS; i-- > 0; ) { ++ size = MAX(BITS_TO_LONGS(size), 1); ++ if (hb->sizes[i] == size) { ++ break; ++ } ++ old = hb->sizes[i]; ++ hb->sizes[i] = size; ++ hb->levels[i] = g_renew(unsigned long, hb->levels[i], size); ++ if (!shrink) { ++ memset(&hb->levels[i][old], 0x00, ++ (size - old) * sizeof(*hb->levels[i])); ++ } ++ } ++ if (hb->meta) { ++ hbitmap_truncate(hb->meta, hb->size << hb->granularity); ++ } ++} ++ ++/** ++ * hbitmap_sparse_merge: performs dst = dst | src ++ * works with differing granularities. ++ * best used when src is sparsely populated. ++ */ ++static void hbitmap_sparse_merge(HBitmap *dst, const HBitmap *src) ++{ ++ int64_t offset; ++ int64_t count; ++ ++ for (offset = 0; ++ hbitmap_next_dirty_area(src, offset, src->orig_size, INT64_MAX, ++ &offset, &count); ++ offset += count) ++ { ++ hbitmap_set(dst, offset, count); ++ } ++} ++ ++/** ++ * Given HBitmaps A and B, let R := A (BITOR) B. ++ * Bitmaps A and B will not be modified, ++ * except when bitmap R is an alias of A or B. ++ * Bitmaps must have same size. ++ */ ++void hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) ++{ ++ int i; ++ uint64_t j; ++ ++ assert(a->orig_size == result->orig_size); ++ assert(b->orig_size == result->orig_size); ++ ++ if ((!hbitmap_count(a) && result == b) || ++ (!hbitmap_count(b) && result == a)) { ++ return; ++ } ++ ++ if (!hbitmap_count(a) && !hbitmap_count(b)) { ++ hbitmap_reset_all(result); ++ return; ++ } ++ ++ if (a->granularity != b->granularity) { ++ if ((a != result) && (b != result)) { ++ hbitmap_reset_all(result); ++ } ++ if (a != result) { ++ hbitmap_sparse_merge(result, a); ++ } ++ if (b != result) { ++ hbitmap_sparse_merge(result, b); ++ } ++ return; ++ } ++ ++ /* This merge is O(size), as BITS_PER_LONG and HBITMAP_LEVELS are constant. ++ * It may be possible to improve running times for sparsely populated maps ++ * by using hbitmap_iter_next, but this is suboptimal for dense maps. ++ */ ++ assert(a->size == b->size); ++ for (i = HBITMAP_LEVELS - 1; i >= 0; i--) { ++ for (j = 0; j < a->sizes[i]; j++) { ++ result->levels[i][j] = a->levels[i][j] | b->levels[i][j]; ++ } ++ } ++ ++ /* Recompute the dirty count */ ++ result->count = hb_count_between(result, 0, result->size - 1); ++} ++ ++char *hbitmap_sha256(const HBitmap *bitmap, Error **errp) ++{ ++ size_t size = bitmap->sizes[HBITMAP_LEVELS - 1] * sizeof(unsigned long); ++ char *data = (char *)bitmap->levels[HBITMAP_LEVELS - 1]; ++ char *hash = NULL; ++ qcrypto_hash_digest(QCRYPTO_HASH_ALG_SHA256, data, size, &hash, errp); ++ ++ return hash; ++} +diff --git a/qcow2/lib/util/host-utils.c b/qcow2/lib/util/host-utils.c +new file mode 100644 +index 00000000..fb91bcba +--- /dev/null ++++ b/qcow2/lib/util/host-utils.c +@@ -0,0 +1,448 @@ ++/* ++ * Utility compute operations used by translated code. ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * Copyright (c) 2007 Aurelien Jarno ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/host-utils.h" ++ ++#ifndef CONFIG_INT128 ++/* Long integer helpers */ ++static inline void mul64(uint64_t *plow, uint64_t *phigh, ++ uint64_t a, uint64_t b) ++{ ++ typedef union { ++ uint64_t ll; ++ struct { ++#if HOST_BIG_ENDIAN ++ uint32_t high, low; ++#else ++ uint32_t low, high; ++#endif ++ } l; ++ } LL; ++ LL rl, rm, rn, rh, a0, b0; ++ uint64_t c; ++ ++ a0.ll = a; ++ b0.ll = b; ++ ++ rl.ll = (uint64_t)a0.l.low * b0.l.low; ++ rm.ll = (uint64_t)a0.l.low * b0.l.high; ++ rn.ll = (uint64_t)a0.l.high * b0.l.low; ++ rh.ll = (uint64_t)a0.l.high * b0.l.high; ++ ++ c = (uint64_t)rl.l.high + rm.l.low + rn.l.low; ++ rl.l.high = c; ++ c >>= 32; ++ c = c + rm.l.high + rn.l.high + rh.l.low; ++ rh.l.low = c; ++ rh.l.high += (uint32_t)(c >> 32); ++ ++ *plow = rl.ll; ++ *phigh = rh.ll; ++} ++ ++/* Unsigned 64x64 -> 128 multiplication */ ++void mulu64 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b) ++{ ++ mul64(plow, phigh, a, b); ++} ++ ++/* Signed 64x64 -> 128 multiplication */ ++void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b) ++{ ++ uint64_t rh; ++ ++ mul64(plow, &rh, a, b); ++ ++ /* Adjust for signs. */ ++ if (b < 0) { ++ rh -= a; ++ } ++ if (a < 0) { ++ rh -= b; ++ } ++ *phigh = rh; ++} ++ ++/* ++ * Unsigned 128-by-64 division. ++ * Returns the remainder. ++ * Returns quotient via plow and phigh. ++ * Also returns the remainder via the function return value. ++ */ ++uint64_t divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) ++{ ++ uint64_t dhi = *phigh; ++ uint64_t dlo = *plow; ++ uint64_t rem, dhighest; ++ int sh; ++ ++ if (divisor == 0 || dhi == 0) { ++ *plow = dlo / divisor; ++ *phigh = 0; ++ return dlo % divisor; ++ } else { ++ sh = clz64(divisor); ++ ++ if (dhi < divisor) { ++ if (sh != 0) { ++ /* normalize the divisor, shifting the dividend accordingly */ ++ divisor <<= sh; ++ dhi = (dhi << sh) | (dlo >> (64 - sh)); ++ dlo <<= sh; ++ } ++ ++ *phigh = 0; ++ *plow = udiv_qrnnd(&rem, dhi, dlo, divisor); ++ } else { ++ if (sh != 0) { ++ /* normalize the divisor, shifting the dividend accordingly */ ++ divisor <<= sh; ++ dhighest = dhi >> (64 - sh); ++ dhi = (dhi << sh) | (dlo >> (64 - sh)); ++ dlo <<= sh; ++ ++ *phigh = udiv_qrnnd(&dhi, dhighest, dhi, divisor); ++ } else { ++ /** ++ * dhi >= divisor ++ * Since the MSB of divisor is set (sh == 0), ++ * (dhi - divisor) < divisor ++ * ++ * Thus, the high part of the quotient is 1, and we can ++ * calculate the low part with a single call to udiv_qrnnd ++ * after subtracting divisor from dhi ++ */ ++ dhi -= divisor; ++ *phigh = 1; ++ } ++ ++ *plow = udiv_qrnnd(&rem, dhi, dlo, divisor); ++ } ++ ++ /* ++ * since the dividend/divisor might have been normalized, ++ * the remainder might also have to be shifted back ++ */ ++ return rem >> sh; ++ } ++} ++ ++/* ++ * Signed 128-by-64 division. ++ * Returns quotient via plow and phigh. ++ * Also returns the remainder via the function return value. ++ */ ++int64_t divs128(uint64_t *plow, int64_t *phigh, int64_t divisor) ++{ ++ bool neg_quotient = false, neg_remainder = false; ++ uint64_t unsig_hi = *phigh, unsig_lo = *plow; ++ uint64_t rem; ++ ++ if (*phigh < 0) { ++ neg_quotient = !neg_quotient; ++ neg_remainder = !neg_remainder; ++ ++ if (unsig_lo == 0) { ++ unsig_hi = -unsig_hi; ++ } else { ++ unsig_hi = ~unsig_hi; ++ unsig_lo = -unsig_lo; ++ } ++ } ++ ++ if (divisor < 0) { ++ neg_quotient = !neg_quotient; ++ ++ divisor = -divisor; ++ } ++ ++ rem = divu128(&unsig_lo, &unsig_hi, (uint64_t)divisor); ++ ++ if (neg_quotient) { ++ if (unsig_lo == 0) { ++ *phigh = -unsig_hi; ++ *plow = 0; ++ } else { ++ *phigh = ~unsig_hi; ++ *plow = -unsig_lo; ++ } ++ } else { ++ *phigh = unsig_hi; ++ *plow = unsig_lo; ++ } ++ ++ if (neg_remainder) { ++ return -rem; ++ } else { ++ return rem; ++ } ++} ++#endif ++ ++/** ++ * urshift - 128-bit Unsigned Right Shift. ++ * @plow: in/out - lower 64-bit integer. ++ * @phigh: in/out - higher 64-bit integer. ++ * @shift: in - bytes to shift, between 0 and 127. ++ * ++ * Result is zero-extended and stored in plow/phigh, which are ++ * input/output variables. Shift values outside the range will ++ * be mod to 128. In other words, the caller is responsible to ++ * verify/assert both the shift range and plow/phigh pointers. ++ */ ++void urshift(uint64_t *plow, uint64_t *phigh, int32_t shift) ++{ ++ shift &= 127; ++ if (shift == 0) { ++ return; ++ } ++ ++ uint64_t h = *phigh >> (shift & 63); ++ if (shift >= 64) { ++ *plow = h; ++ *phigh = 0; ++ } else { ++ *plow = (*plow >> (shift & 63)) | (*phigh << (64 - (shift & 63))); ++ *phigh = h; ++ } ++} ++ ++/** ++ * ulshift - 128-bit Unsigned Left Shift. ++ * @plow: in/out - lower 64-bit integer. ++ * @phigh: in/out - higher 64-bit integer. ++ * @shift: in - bytes to shift, between 0 and 127. ++ * @overflow: out - true if any 1-bit is shifted out. ++ * ++ * Result is zero-extended and stored in plow/phigh, which are ++ * input/output variables. Shift values outside the range will ++ * be mod to 128. In other words, the caller is responsible to ++ * verify/assert both the shift range and plow/phigh pointers. ++ */ ++void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow) ++{ ++ uint64_t low = *plow; ++ uint64_t high = *phigh; ++ ++ shift &= 127; ++ if (shift == 0) { ++ return; ++ } ++ ++ /* check if any bit will be shifted out */ ++ urshift(&low, &high, 128 - shift); ++ if (low | high) { ++ *overflow = true; ++ } ++ ++ if (shift >= 64) { ++ *phigh = *plow << (shift & 63); ++ *plow = 0; ++ } else { ++ *phigh = (*plow >> (64 - (shift & 63))) | (*phigh << (shift & 63)); ++ *plow = *plow << shift; ++ } ++} ++ ++/* ++ * Unsigned 256-by-128 division. ++ * Returns the remainder via r. ++ * Returns lower 128 bit of quotient. ++ * Needs a normalized divisor (most significant bit set to 1). ++ * ++ * Adapted from include/qemu/host-utils.h udiv_qrnnd, ++ * from the GNU Multi Precision Library - longlong.h __udiv_qrnnd ++ * (https://gmplib.org/repo/gmp/file/tip/longlong.h) ++ * ++ * Licensed under the GPLv2/LGPLv3 ++ */ ++static Int128 udiv256_qrnnd(Int128 *r, Int128 n1, Int128 n0, Int128 d) ++{ ++ Int128 d0, d1, q0, q1, r1, r0, m; ++ uint64_t mp0, mp1; ++ ++ d0 = int128_make64(int128_getlo(d)); ++ d1 = int128_make64(int128_gethi(d)); ++ ++ r1 = int128_remu(n1, d1); ++ q1 = int128_divu(n1, d1); ++ mp0 = int128_getlo(q1); ++ mp1 = int128_gethi(q1); ++ mulu128(&mp0, &mp1, int128_getlo(d0)); ++ m = int128_make128(mp0, mp1); ++ r1 = int128_make128(int128_gethi(n0), int128_getlo(r1)); ++ if (int128_ult(r1, m)) { ++ q1 = int128_sub(q1, int128_one()); ++ r1 = int128_add(r1, d); ++ if (int128_uge(r1, d)) { ++ if (int128_ult(r1, m)) { ++ q1 = int128_sub(q1, int128_one()); ++ r1 = int128_add(r1, d); ++ } ++ } ++ } ++ r1 = int128_sub(r1, m); ++ ++ r0 = int128_remu(r1, d1); ++ q0 = int128_divu(r1, d1); ++ mp0 = int128_getlo(q0); ++ mp1 = int128_gethi(q0); ++ mulu128(&mp0, &mp1, int128_getlo(d0)); ++ m = int128_make128(mp0, mp1); ++ r0 = int128_make128(int128_getlo(n0), int128_getlo(r0)); ++ if (int128_ult(r0, m)) { ++ q0 = int128_sub(q0, int128_one()); ++ r0 = int128_add(r0, d); ++ if (int128_uge(r0, d)) { ++ if (int128_ult(r0, m)) { ++ q0 = int128_sub(q0, int128_one()); ++ r0 = int128_add(r0, d); ++ } ++ } ++ } ++ r0 = int128_sub(r0, m); ++ ++ *r = r0; ++ return int128_or(int128_lshift(q1, 64), q0); ++} ++ ++/* ++ * Unsigned 256-by-128 division. ++ * Returns the remainder. ++ * Returns quotient via plow and phigh. ++ * Also returns the remainder via the function return value. ++ */ ++Int128 divu256(Int128 *plow, Int128 *phigh, Int128 divisor) ++{ ++ Int128 dhi = *phigh; ++ Int128 dlo = *plow; ++ Int128 rem, dhighest; ++ int sh; ++ ++ if (!int128_nz(divisor) || !int128_nz(dhi)) { ++ *plow = int128_divu(dlo, divisor); ++ *phigh = int128_zero(); ++ return int128_remu(dlo, divisor); ++ } else { ++ sh = clz128(divisor); ++ ++ if (int128_ult(dhi, divisor)) { ++ if (sh != 0) { ++ /* normalize the divisor, shifting the dividend accordingly */ ++ divisor = int128_lshift(divisor, sh); ++ dhi = int128_or(int128_lshift(dhi, sh), ++ int128_urshift(dlo, (128 - sh))); ++ dlo = int128_lshift(dlo, sh); ++ } ++ ++ *phigh = int128_zero(); ++ *plow = udiv256_qrnnd(&rem, dhi, dlo, divisor); ++ } else { ++ if (sh != 0) { ++ /* normalize the divisor, shifting the dividend accordingly */ ++ divisor = int128_lshift(divisor, sh); ++ dhighest = int128_rshift(dhi, (128 - sh)); ++ dhi = int128_or(int128_lshift(dhi, sh), ++ int128_urshift(dlo, (128 - sh))); ++ dlo = int128_lshift(dlo, sh); ++ ++ *phigh = udiv256_qrnnd(&dhi, dhighest, dhi, divisor); ++ } else { ++ /* ++ * dhi >= divisor ++ * Since the MSB of divisor is set (sh == 0), ++ * (dhi - divisor) < divisor ++ * ++ * Thus, the high part of the quotient is 1, and we can ++ * calculate the low part with a single call to udiv_qrnnd ++ * after subtracting divisor from dhi ++ */ ++ dhi = int128_sub(dhi, divisor); ++ *phigh = int128_one(); ++ } ++ ++ *plow = udiv256_qrnnd(&rem, dhi, dlo, divisor); ++ } ++ ++ /* ++ * since the dividend/divisor might have been normalized, ++ * the remainder might also have to be shifted back ++ */ ++ rem = int128_urshift(rem, sh); ++ return rem; ++ } ++} ++ ++/* ++ * Signed 256-by-128 division. ++ * Returns quotient via plow and phigh. ++ * Also returns the remainder via the function return value. ++ */ ++Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor) ++{ ++ bool neg_quotient = false, neg_remainder = false; ++ Int128 unsig_hi = *phigh, unsig_lo = *plow; ++ Int128 rem; ++ ++ if (!int128_nonneg(*phigh)) { ++ neg_quotient = !neg_quotient; ++ neg_remainder = !neg_remainder; ++ ++ if (!int128_nz(unsig_lo)) { ++ unsig_hi = int128_neg(unsig_hi); ++ } else { ++ unsig_hi = int128_not(unsig_hi); ++ unsig_lo = int128_neg(unsig_lo); ++ } ++ } ++ ++ if (!int128_nonneg(divisor)) { ++ neg_quotient = !neg_quotient; ++ ++ divisor = int128_neg(divisor); ++ } ++ ++ rem = divu256(&unsig_lo, &unsig_hi, divisor); ++ ++ if (neg_quotient) { ++ if (!int128_nz(unsig_lo)) { ++ *phigh = int128_neg(unsig_hi); ++ *plow = int128_zero(); ++ } else { ++ *phigh = int128_not(unsig_hi); ++ *plow = int128_neg(unsig_lo); ++ } ++ } else { ++ *phigh = unsig_hi; ++ *plow = unsig_lo; ++ } ++ ++ if (neg_remainder) { ++ return int128_neg(rem); ++ } else { ++ return rem; ++ } ++} +diff --git a/qcow2/lib/util/id.c b/qcow2/lib/util/id.c +new file mode 100644 +index 00000000..ded41c50 +--- /dev/null ++++ b/qcow2/lib/util/id.c +@@ -0,0 +1,69 @@ ++/* ++ * Dealing with identifiers ++ * ++ * Copyright (C) 2014 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/ctype.h" ++#include "qemu/id.h" ++ ++bool id_wellformed(const char *id) ++{ ++ int i; ++ ++ if (!qemu_isalpha(id[0])) { ++ return false; ++ } ++ for (i = 1; id[i]; i++) { ++ if (!qemu_isalnum(id[i]) && !strchr("-._", id[i])) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++#define ID_SPECIAL_CHAR '#' ++ ++static const char *const id_subsys_str[ID_MAX] = { ++ [ID_QDEV] = "qdev", ++ [ID_BLOCK] = "block", ++ [ID_CHR] = "chr", ++ [ID_NET] = "net", ++}; ++ ++/* ++ * Generates an ID of the form PREFIX SUBSYSTEM NUMBER ++ * where: ++ * ++ * - PREFIX is the reserved character '#' ++ * - SUBSYSTEM identifies the subsystem creating the ID ++ * - NUMBER is a decimal number unique within SUBSYSTEM. ++ * ++ * Example: "#block146" ++ * ++ * Note that these IDs do not satisfy id_wellformed(). ++ * ++ * The caller is responsible for freeing the returned string with g_free() ++ */ ++char *id_generate(IdSubSystems id) ++{ ++ static uint64_t id_counters[ID_MAX]; ++ uint32_t rnd; ++ ++ assert(id < ARRAY_SIZE(id_subsys_str)); ++ assert(id_subsys_str[id]); ++ ++ rnd = g_random_int_range(0, 100); ++ ++ return g_strdup_printf("%c%s%" PRIu64 "%02" PRId32, ID_SPECIAL_CHAR, ++ id_subsys_str[id], ++ id_counters[id]++, ++ rnd); ++} +diff --git a/qcow2/lib/util/iov.c b/qcow2/lib/util/iov.c +new file mode 100644 +index 00000000..7e73948f +--- /dev/null ++++ b/qcow2/lib/util/iov.c +@@ -0,0 +1,712 @@ ++/* ++ * Helpers for getting linearized buffers from iov / filling buffers into iovs ++ * ++ * Copyright IBM, Corp. 2007, 2008 ++ * Copyright (C) 2010 Red Hat, Inc. ++ * ++ * Author(s): ++ * Anthony Liguori ++ * Amit Shah ++ * Michael Tokarev ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/iov.h" ++#include "qemu/sockets.h" ++#include "qemu/cutils.h" ++ ++size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt, ++ size_t offset, const void *buf, size_t bytes) ++{ ++ size_t done; ++ unsigned int i; ++ for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) { ++ if (offset < iov[i].iov_len) { ++ size_t len = MIN(iov[i].iov_len - offset, bytes - done); ++ memcpy(iov[i].iov_base + offset, buf + done, len); ++ done += len; ++ offset = 0; ++ } else { ++ offset -= iov[i].iov_len; ++ } ++ } ++ assert(offset == 0); ++ return done; ++} ++ ++size_t iov_to_buf_full(const struct iovec *iov, const unsigned int iov_cnt, ++ size_t offset, void *buf, size_t bytes) ++{ ++ size_t done; ++ unsigned int i; ++ for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) { ++ if (offset < iov[i].iov_len) { ++ size_t len = MIN(iov[i].iov_len - offset, bytes - done); ++ memcpy(buf + done, iov[i].iov_base + offset, len); ++ done += len; ++ offset = 0; ++ } else { ++ offset -= iov[i].iov_len; ++ } ++ } ++ assert(offset == 0); ++ return done; ++} ++ ++size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt, ++ size_t offset, int fillc, size_t bytes) ++{ ++ size_t done; ++ unsigned int i; ++ for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) { ++ if (offset < iov[i].iov_len) { ++ size_t len = MIN(iov[i].iov_len - offset, bytes - done); ++ memset(iov[i].iov_base + offset, fillc, len); ++ done += len; ++ offset = 0; ++ } else { ++ offset -= iov[i].iov_len; ++ } ++ } ++ assert(offset == 0); ++ return done; ++} ++ ++size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt) ++{ ++ size_t len; ++ unsigned int i; ++ ++ len = 0; ++ for (i = 0; i < iov_cnt; i++) { ++ len += iov[i].iov_len; ++ } ++ return len; ++} ++ ++/* helper function for iov_send_recv() */ ++static ssize_t ++do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send) ++{ ++#ifdef CONFIG_POSIX ++ ssize_t ret; ++ struct msghdr msg; ++ memset(&msg, 0, sizeof(msg)); ++ msg.msg_iov = iov; ++ msg.msg_iovlen = iov_cnt; ++ do { ++ ret = do_send ++ ? sendmsg(sockfd, &msg, 0) ++ : recvmsg(sockfd, &msg, 0); ++ } while (ret < 0 && errno == EINTR); ++ return ret; ++#else ++ /* else send piece-by-piece */ ++ /*XXX Note: windows has WSASend() and WSARecv() */ ++ unsigned i = 0; ++ ssize_t ret = 0; ++ ssize_t off = 0; ++ while (i < iov_cnt) { ++ ssize_t r = do_send ++ ? send(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0) ++ : recv(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0); ++ if (r > 0) { ++ ret += r; ++ off += r; ++ if (off < iov[i].iov_len) { ++ continue; ++ } ++ } else if (!r) { ++ break; ++ } else if (errno == EINTR) { ++ continue; ++ } else { ++ /* else it is some "other" error, ++ * only return if there was no data processed. */ ++ if (ret == 0) { ++ ret = -1; ++ } ++ break; ++ } ++ off = 0; ++ i++; ++ } ++ return ret; ++#endif ++} ++ ++ssize_t iov_send_recv(int sockfd, const struct iovec *_iov, unsigned iov_cnt, ++ size_t offset, size_t bytes, ++ bool do_send) ++{ ++ ssize_t total = 0; ++ ssize_t ret; ++ size_t orig_len, tail; ++ unsigned niov; ++ struct iovec *local_iov, *iov; ++ ++ if (bytes <= 0) { ++ return 0; ++ } ++ ++ local_iov = g_new0(struct iovec, iov_cnt); ++ iov_copy(local_iov, iov_cnt, _iov, iov_cnt, offset, bytes); ++ offset = 0; ++ iov = local_iov; ++ ++ while (bytes > 0) { ++ /* Find the start position, skipping `offset' bytes: ++ * first, skip all full-sized vector elements, */ ++ for (niov = 0; niov < iov_cnt && offset >= iov[niov].iov_len; ++niov) { ++ offset -= iov[niov].iov_len; ++ } ++ ++ /* niov == iov_cnt would only be valid if bytes == 0, which ++ * we already ruled out in the loop condition. */ ++ assert(niov < iov_cnt); ++ iov += niov; ++ iov_cnt -= niov; ++ ++ if (offset) { ++ /* second, skip `offset' bytes from the (now) first element, ++ * undo it on exit */ ++ iov[0].iov_base += offset; ++ iov[0].iov_len -= offset; ++ } ++ /* Find the end position skipping `bytes' bytes: */ ++ /* first, skip all full-sized elements */ ++ tail = bytes; ++ for (niov = 0; niov < iov_cnt && iov[niov].iov_len <= tail; ++niov) { ++ tail -= iov[niov].iov_len; ++ } ++ if (tail) { ++ /* second, fixup the last element, and remember the original ++ * length */ ++ assert(niov < iov_cnt); ++ assert(iov[niov].iov_len > tail); ++ orig_len = iov[niov].iov_len; ++ iov[niov++].iov_len = tail; ++ ret = do_send_recv(sockfd, iov, niov, do_send); ++ /* Undo the changes above before checking for errors */ ++ iov[niov-1].iov_len = orig_len; ++ } else { ++ ret = do_send_recv(sockfd, iov, niov, do_send); ++ } ++ if (offset) { ++ iov[0].iov_base -= offset; ++ iov[0].iov_len += offset; ++ } ++ ++ if (ret < 0) { ++ assert(errno != EINTR); ++ g_free(local_iov); ++ if (errno == EAGAIN && total > 0) { ++ return total; ++ } ++ return -1; ++ } ++ ++ if (ret == 0 && !do_send) { ++ /* recv returns 0 when the peer has performed an orderly ++ * shutdown. */ ++ break; ++ } ++ ++ /* Prepare for the next iteration */ ++ offset += ret; ++ total += ret; ++ bytes -= ret; ++ } ++ ++ g_free(local_iov); ++ return total; ++} ++ ++ ++void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, ++ FILE *fp, const char *prefix, size_t limit) ++{ ++ int v; ++ size_t size = 0; ++ char *buf; ++ ++ for (v = 0; v < iov_cnt; v++) { ++ size += iov[v].iov_len; ++ } ++ size = size > limit ? limit : size; ++ buf = g_malloc(size); ++ iov_to_buf(iov, iov_cnt, 0, buf, size); ++ qemu_hexdump(fp, prefix, buf, size); ++ g_free(buf); ++} ++ ++unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt, ++ const struct iovec *iov, unsigned int iov_cnt, ++ size_t offset, size_t bytes) ++{ ++ size_t len; ++ unsigned int i, j; ++ for (i = 0, j = 0; ++ i < iov_cnt && j < dst_iov_cnt && (offset || bytes); i++) { ++ if (offset >= iov[i].iov_len) { ++ offset -= iov[i].iov_len; ++ continue; ++ } ++ len = MIN(bytes, iov[i].iov_len - offset); ++ ++ dst_iov[j].iov_base = iov[i].iov_base + offset; ++ dst_iov[j].iov_len = len; ++ j++; ++ bytes -= len; ++ offset = 0; ++ } ++ assert(offset == 0); ++ return j; ++} ++ ++/* io vectors */ ++ ++void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint) ++{ ++ qiov->iov = g_new(struct iovec, alloc_hint); ++ qiov->niov = 0; ++ qiov->nalloc = alloc_hint; ++ qiov->size = 0; ++} ++ ++void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov) ++{ ++ int i; ++ ++ qiov->iov = iov; ++ qiov->niov = niov; ++ qiov->nalloc = -1; ++ qiov->size = 0; ++ for (i = 0; i < niov; i++) ++ qiov->size += iov[i].iov_len; ++} ++ ++void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len) ++{ ++ assert(qiov->nalloc != -1); ++ ++ if (qiov->niov == qiov->nalloc) { ++ qiov->nalloc = 2 * qiov->nalloc + 1; ++ qiov->iov = g_renew(struct iovec, qiov->iov, qiov->nalloc); ++ } ++ qiov->iov[qiov->niov].iov_base = base; ++ qiov->iov[qiov->niov].iov_len = len; ++ qiov->size += len; ++ ++qiov->niov; ++} ++ ++/* ++ * Concatenates (partial) iovecs from src_iov to the end of dst. ++ * It starts copying after skipping `soffset' bytes at the ++ * beginning of src and adds individual vectors from src to ++ * dst copies up to `sbytes' bytes total, or up to the end ++ * of src_iov if it comes first. This way, it is okay to specify ++ * very large value for `sbytes' to indicate "up to the end ++ * of src". ++ * Only vector pointers are processed, not the actual data buffers. ++ */ ++size_t qemu_iovec_concat_iov(QEMUIOVector *dst, ++ struct iovec *src_iov, unsigned int src_cnt, ++ size_t soffset, size_t sbytes) ++{ ++ int i; ++ size_t done; ++ ++ if (!sbytes) { ++ return 0; ++ } ++ assert(dst->nalloc != -1); ++ for (i = 0, done = 0; done < sbytes && i < src_cnt; i++) { ++ if (soffset < src_iov[i].iov_len) { ++ size_t len = MIN(src_iov[i].iov_len - soffset, sbytes - done); ++ qemu_iovec_add(dst, src_iov[i].iov_base + soffset, len); ++ done += len; ++ soffset = 0; ++ } else { ++ soffset -= src_iov[i].iov_len; ++ } ++ } ++ assert(soffset == 0); /* offset beyond end of src */ ++ ++ return done; ++} ++ ++/* ++ * Concatenates (partial) iovecs from src to the end of dst. ++ * It starts copying after skipping `soffset' bytes at the ++ * beginning of src and adds individual vectors from src to ++ * dst copies up to `sbytes' bytes total, or up to the end ++ * of src if it comes first. This way, it is okay to specify ++ * very large value for `sbytes' to indicate "up to the end ++ * of src". ++ * Only vector pointers are processed, not the actual data buffers. ++ */ ++void qemu_iovec_concat(QEMUIOVector *dst, ++ QEMUIOVector *src, size_t soffset, size_t sbytes) ++{ ++ qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes); ++} ++ ++/* ++ * qiov_find_iov ++ * ++ * Return pointer to iovec structure, where byte at @offset in original vector ++ * @iov exactly is. ++ * Set @remaining_offset to be offset inside that iovec to the same byte. ++ */ ++static struct iovec *iov_skip_offset(struct iovec *iov, size_t offset, ++ size_t *remaining_offset) ++{ ++ while (offset > 0 && offset >= iov->iov_len) { ++ offset -= iov->iov_len; ++ iov++; ++ } ++ *remaining_offset = offset; ++ ++ return iov; ++} ++ ++/* ++ * qemu_iovec_slice ++ * ++ * Find subarray of iovec's, containing requested range. @head would ++ * be offset in first iov (returned by the function), @tail would be ++ * count of extra bytes in last iovec (returned iov + @niov - 1). ++ */ ++struct iovec *qemu_iovec_slice(QEMUIOVector *qiov, ++ size_t offset, size_t len, ++ size_t *head, size_t *tail, int *niov) ++{ ++ struct iovec *iov, *end_iov; ++ ++ assert(offset + len <= qiov->size); ++ ++ iov = iov_skip_offset(qiov->iov, offset, head); ++ end_iov = iov_skip_offset(iov, *head + len, tail); ++ ++ if (*tail > 0) { ++ assert(*tail < end_iov->iov_len); ++ *tail = end_iov->iov_len - *tail; ++ end_iov++; ++ } ++ ++ *niov = end_iov - iov; ++ ++ return iov; ++} ++ ++int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len) ++{ ++ size_t head, tail; ++ int niov; ++ ++ qemu_iovec_slice(qiov, offset, len, &head, &tail, &niov); ++ ++ return niov; ++} ++ ++/* ++ * Check if the contents of subrange of qiov data is all zeroes. ++ */ ++bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t offset, size_t bytes) ++{ ++ struct iovec *iov; ++ size_t current_offset; ++ ++ assert(offset + bytes <= qiov->size); ++ ++ iov = iov_skip_offset(qiov->iov, offset, ¤t_offset); ++ ++ while (bytes) { ++ uint8_t *base = (uint8_t *)iov->iov_base + current_offset; ++ size_t len = MIN(iov->iov_len - current_offset, bytes); ++ ++ if (!buffer_is_zero(base, len)) { ++ return false; ++ } ++ ++ current_offset = 0; ++ bytes -= len; ++ iov++; ++ } ++ ++ return true; ++} ++ ++void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source, ++ size_t offset, size_t len) ++{ ++ struct iovec *slice_iov; ++ int slice_niov; ++ size_t slice_head, slice_tail; ++ ++ assert(source->size >= len); ++ assert(source->size - len >= offset); ++ ++ slice_iov = qemu_iovec_slice(source, offset, len, ++ &slice_head, &slice_tail, &slice_niov); ++ if (slice_niov == 1) { ++ qemu_iovec_init_buf(qiov, slice_iov[0].iov_base + slice_head, len); ++ } else { ++ qemu_iovec_init(qiov, slice_niov); ++ qemu_iovec_concat_iov(qiov, slice_iov, slice_niov, slice_head, len); ++ } ++} ++ ++void qemu_iovec_destroy(QEMUIOVector *qiov) ++{ ++ if (qiov->nalloc != -1) { ++ g_free(qiov->iov); ++ } ++ ++ memset(qiov, 0, sizeof(*qiov)); ++} ++ ++void qemu_iovec_reset(QEMUIOVector *qiov) ++{ ++ assert(qiov->nalloc != -1); ++ ++ qiov->niov = 0; ++ qiov->size = 0; ++} ++ ++size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset, ++ void *buf, size_t bytes) ++{ ++ return iov_to_buf(qiov->iov, qiov->niov, offset, buf, bytes); ++} ++ ++size_t qemu_iovec_from_buf(QEMUIOVector *qiov, size_t offset, ++ const void *buf, size_t bytes) ++{ ++ return iov_from_buf(qiov->iov, qiov->niov, offset, buf, bytes); ++} ++ ++size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset, ++ int fillc, size_t bytes) ++{ ++ return iov_memset(qiov->iov, qiov->niov, offset, fillc, bytes); ++} ++ ++/** ++ * Check that I/O vector contents are identical ++ * ++ * The IO vectors must have the same structure (same length of all parts). ++ * A typical usage is to compare vectors created with qemu_iovec_clone(). ++ * ++ * @a: I/O vector ++ * @b: I/O vector ++ * @ret: Offset to first mismatching byte or -1 if match ++ */ ++ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b) ++{ ++ int i; ++ ssize_t offset = 0; ++ ++ assert(a->niov == b->niov); ++ for (i = 0; i < a->niov; i++) { ++ size_t len = 0; ++ uint8_t *p = (uint8_t *)a->iov[i].iov_base; ++ uint8_t *q = (uint8_t *)b->iov[i].iov_base; ++ ++ assert(a->iov[i].iov_len == b->iov[i].iov_len); ++ while (len < a->iov[i].iov_len && *p++ == *q++) { ++ len++; ++ } ++ ++ offset += len; ++ ++ if (len != a->iov[i].iov_len) { ++ return offset; ++ } ++ } ++ return -1; ++} ++ ++typedef struct { ++ int src_index; ++ struct iovec *src_iov; ++ void *dest_base; ++} IOVectorSortElem; ++ ++static int sortelem_cmp_src_base(const void *a, const void *b) ++{ ++ const IOVectorSortElem *elem_a = a; ++ const IOVectorSortElem *elem_b = b; ++ ++ /* Don't overflow */ ++ if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) { ++ return -1; ++ } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) { ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ ++static int sortelem_cmp_src_index(const void *a, const void *b) ++{ ++ const IOVectorSortElem *elem_a = a; ++ const IOVectorSortElem *elem_b = b; ++ ++ return elem_a->src_index - elem_b->src_index; ++} ++ ++/** ++ * Copy contents of I/O vector ++ * ++ * The relative relationships of overlapping iovecs are preserved. This is ++ * necessary to ensure identical semantics in the cloned I/O vector. ++ */ ++void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf) ++{ ++ g_autofree IOVectorSortElem *sortelems = g_new(IOVectorSortElem, src->niov); ++ void *last_end; ++ int i; ++ ++ /* Sort by source iovecs by base address */ ++ for (i = 0; i < src->niov; i++) { ++ sortelems[i].src_index = i; ++ sortelems[i].src_iov = &src->iov[i]; ++ } ++ qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base); ++ ++ /* Allocate buffer space taking into account overlapping iovecs */ ++ last_end = NULL; ++ for (i = 0; i < src->niov; i++) { ++ struct iovec *cur = sortelems[i].src_iov; ++ ptrdiff_t rewind = 0; ++ ++ /* Detect overlap */ ++ if (last_end && last_end > cur->iov_base) { ++ rewind = last_end - cur->iov_base; ++ } ++ ++ sortelems[i].dest_base = buf - rewind; ++ buf += cur->iov_len - MIN(rewind, cur->iov_len); ++ last_end = MAX(cur->iov_base + cur->iov_len, last_end); ++ } ++ ++ /* Sort by source iovec index and build destination iovec */ ++ qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index); ++ for (i = 0; i < src->niov; i++) { ++ qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len); ++ } ++} ++ ++void iov_discard_undo(IOVDiscardUndo *undo) ++{ ++ /* Restore original iovec if it was modified */ ++ if (undo->modified_iov) { ++ *undo->modified_iov = undo->orig; ++ } ++} ++ ++size_t iov_discard_front_undoable(struct iovec **iov, ++ unsigned int *iov_cnt, ++ size_t bytes, ++ IOVDiscardUndo *undo) ++{ ++ size_t total = 0; ++ struct iovec *cur; ++ ++ if (undo) { ++ undo->modified_iov = NULL; ++ } ++ ++ for (cur = *iov; *iov_cnt > 0; cur++) { ++ if (cur->iov_len > bytes) { ++ if (undo) { ++ undo->modified_iov = cur; ++ undo->orig = *cur; ++ } ++ ++ cur->iov_base += bytes; ++ cur->iov_len -= bytes; ++ total += bytes; ++ break; ++ } ++ ++ bytes -= cur->iov_len; ++ total += cur->iov_len; ++ *iov_cnt -= 1; ++ } ++ ++ *iov = cur; ++ return total; ++} ++ ++size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt, ++ size_t bytes) ++{ ++ return iov_discard_front_undoable(iov, iov_cnt, bytes, NULL); ++} ++ ++size_t iov_discard_back_undoable(struct iovec *iov, ++ unsigned int *iov_cnt, ++ size_t bytes, ++ IOVDiscardUndo *undo) ++{ ++ size_t total = 0; ++ struct iovec *cur; ++ ++ if (undo) { ++ undo->modified_iov = NULL; ++ } ++ ++ if (*iov_cnt == 0) { ++ return 0; ++ } ++ ++ cur = iov + (*iov_cnt - 1); ++ ++ while (*iov_cnt > 0) { ++ if (cur->iov_len > bytes) { ++ if (undo) { ++ undo->modified_iov = cur; ++ undo->orig = *cur; ++ } ++ ++ cur->iov_len -= bytes; ++ total += bytes; ++ break; ++ } ++ ++ bytes -= cur->iov_len; ++ total += cur->iov_len; ++ cur--; ++ *iov_cnt -= 1; ++ } ++ ++ return total; ++} ++ ++size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt, ++ size_t bytes) ++{ ++ return iov_discard_back_undoable(iov, iov_cnt, bytes, NULL); ++} ++ ++void qemu_iovec_discard_back(QEMUIOVector *qiov, size_t bytes) ++{ ++ size_t total; ++ unsigned int niov = qiov->niov; ++ ++ assert(qiov->size >= bytes); ++ total = iov_discard_back(qiov->iov, &niov, bytes); ++ assert(total == bytes); ++ ++ qiov->niov = niov; ++ qiov->size -= bytes; ++} +diff --git a/qcow2/lib/util/lockcnt.c b/qcow2/lib/util/lockcnt.c +new file mode 100644 +index 00000000..5da36946 +--- /dev/null ++++ b/qcow2/lib/util/lockcnt.c +@@ -0,0 +1,399 @@ ++/* ++ * QemuLockCnt implementation ++ * ++ * Copyright Red Hat, Inc. 2017 ++ * ++ * Author: ++ * Paolo Bonzini ++ */ ++#include "qemu/osdep.h" ++#include "qemu/thread.h" ++#include "qemu/atomic.h" ++#include "trace.h" ++ ++#ifdef CONFIG_LINUX ++#include "qemu/futex.h" ++ ++/* On Linux, bits 0-1 are a futex-based lock, bits 2-31 are the counter. ++ * For the mutex algorithm see Ulrich Drepper's "Futexes Are Tricky" (ok, ++ * this is not the most relaxing citation I could make...). It is similar ++ * to mutex2 in the paper. ++ */ ++ ++#define QEMU_LOCKCNT_STATE_MASK 3 ++#define QEMU_LOCKCNT_STATE_FREE 0 /* free, uncontended */ ++#define QEMU_LOCKCNT_STATE_LOCKED 1 /* locked, uncontended */ ++#define QEMU_LOCKCNT_STATE_WAITING 2 /* locked, contended */ ++ ++#define QEMU_LOCKCNT_COUNT_STEP 4 ++#define QEMU_LOCKCNT_COUNT_SHIFT 2 ++ ++void qemu_lockcnt_init(QemuLockCnt *lockcnt) ++{ ++ lockcnt->count = 0; ++} ++ ++void qemu_lockcnt_destroy(QemuLockCnt *lockcnt) ++{ ++} ++ ++/* *val is the current value of lockcnt->count. ++ * ++ * If the lock is free, try a cmpxchg from *val to new_if_free; return ++ * true and set *val to the old value found by the cmpxchg in ++ * lockcnt->count. ++ * ++ * If the lock is taken, wait for it to be released and return false ++ * *without trying again to take the lock*. Again, set *val to the ++ * new value of lockcnt->count. ++ * ++ * If *waited is true on return, new_if_free's bottom two bits must not ++ * be QEMU_LOCKCNT_STATE_LOCKED on subsequent calls, because the caller ++ * does not know if there are other waiters. Furthermore, after *waited ++ * is set the caller has effectively acquired the lock. If it returns ++ * with the lock not taken, it must wake another futex waiter. ++ */ ++static bool qemu_lockcnt_cmpxchg_or_wait(QemuLockCnt *lockcnt, int *val, ++ int new_if_free, bool *waited) ++{ ++ /* Fast path for when the lock is free. */ ++ if ((*val & QEMU_LOCKCNT_STATE_MASK) == QEMU_LOCKCNT_STATE_FREE) { ++ int expected = *val; ++ ++ trace_lockcnt_fast_path_attempt(lockcnt, expected, new_if_free); ++ *val = qatomic_cmpxchg(&lockcnt->count, expected, new_if_free); ++ if (*val == expected) { ++ trace_lockcnt_fast_path_success(lockcnt, expected, new_if_free); ++ *val = new_if_free; ++ return true; ++ } ++ } ++ ++ /* The slow path moves from locked to waiting if necessary, then ++ * does a futex wait. Both steps can be repeated ad nauseam, ++ * only getting out of the loop if we can have another shot at the ++ * fast path. Once we can, get out to compute the new destination ++ * value for the fast path. ++ */ ++ while ((*val & QEMU_LOCKCNT_STATE_MASK) != QEMU_LOCKCNT_STATE_FREE) { ++ if ((*val & QEMU_LOCKCNT_STATE_MASK) == QEMU_LOCKCNT_STATE_LOCKED) { ++ int expected = *val; ++ int new = expected - QEMU_LOCKCNT_STATE_LOCKED + QEMU_LOCKCNT_STATE_WAITING; ++ ++ trace_lockcnt_futex_wait_prepare(lockcnt, expected, new); ++ *val = qatomic_cmpxchg(&lockcnt->count, expected, new); ++ if (*val == expected) { ++ *val = new; ++ } ++ continue; ++ } ++ ++ if ((*val & QEMU_LOCKCNT_STATE_MASK) == QEMU_LOCKCNT_STATE_WAITING) { ++ *waited = true; ++ trace_lockcnt_futex_wait(lockcnt, *val); ++ qemu_futex_wait(&lockcnt->count, *val); ++ *val = qatomic_read(&lockcnt->count); ++ trace_lockcnt_futex_wait_resume(lockcnt, *val); ++ continue; ++ } ++ ++ abort(); ++ } ++ return false; ++} ++ ++static void lockcnt_wake(QemuLockCnt *lockcnt) ++{ ++ trace_lockcnt_futex_wake(lockcnt); ++ qemu_futex_wake(&lockcnt->count, 1); ++} ++ ++void qemu_lockcnt_inc(QemuLockCnt *lockcnt) ++{ ++ int val = qatomic_read(&lockcnt->count); ++ bool waited = false; ++ ++ for (;;) { ++ if (val >= QEMU_LOCKCNT_COUNT_STEP) { ++ int expected = val; ++ val = qatomic_cmpxchg(&lockcnt->count, val, ++ val + QEMU_LOCKCNT_COUNT_STEP); ++ if (val == expected) { ++ break; ++ } ++ } else { ++ /* The fast path is (0, unlocked)->(1, unlocked). */ ++ if (qemu_lockcnt_cmpxchg_or_wait(lockcnt, &val, QEMU_LOCKCNT_COUNT_STEP, ++ &waited)) { ++ break; ++ } ++ } ++ } ++ ++ /* If we were woken by another thread, we should also wake one because ++ * we are effectively releasing the lock that was given to us. This is ++ * the case where qemu_lockcnt_lock would leave QEMU_LOCKCNT_STATE_WAITING ++ * in the low bits, and qemu_lockcnt_inc_and_unlock would find it and ++ * wake someone. ++ */ ++ if (waited) { ++ lockcnt_wake(lockcnt); ++ } ++} ++ ++void qemu_lockcnt_dec(QemuLockCnt *lockcnt) ++{ ++ qatomic_sub(&lockcnt->count, QEMU_LOCKCNT_COUNT_STEP); ++} ++ ++/* Decrement a counter, and return locked if it is decremented to zero. ++ * If the function returns true, it is impossible for the counter to ++ * become nonzero until the next qemu_lockcnt_unlock. ++ */ ++bool qemu_lockcnt_dec_and_lock(QemuLockCnt *lockcnt) ++{ ++ int val = qatomic_read(&lockcnt->count); ++ int locked_state = QEMU_LOCKCNT_STATE_LOCKED; ++ bool waited = false; ++ ++ for (;;) { ++ if (val >= 2 * QEMU_LOCKCNT_COUNT_STEP) { ++ int expected = val; ++ val = qatomic_cmpxchg(&lockcnt->count, val, ++ val - QEMU_LOCKCNT_COUNT_STEP); ++ if (val == expected) { ++ break; ++ } ++ } else { ++ /* If count is going 1->0, take the lock. The fast path is ++ * (1, unlocked)->(0, locked) or (1, unlocked)->(0, waiting). ++ */ ++ if (qemu_lockcnt_cmpxchg_or_wait(lockcnt, &val, locked_state, &waited)) { ++ return true; ++ } ++ ++ if (waited) { ++ /* At this point we do not know if there are more waiters. Assume ++ * there are. ++ */ ++ locked_state = QEMU_LOCKCNT_STATE_WAITING; ++ } ++ } ++ } ++ ++ /* If we were woken by another thread, but we're returning in unlocked ++ * state, we should also wake a thread because we are effectively ++ * releasing the lock that was given to us. This is the case where ++ * qemu_lockcnt_lock would leave QEMU_LOCKCNT_STATE_WAITING in the low ++ * bits, and qemu_lockcnt_unlock would find it and wake someone. ++ */ ++ if (waited) { ++ lockcnt_wake(lockcnt); ++ } ++ return false; ++} ++ ++/* If the counter is one, decrement it and return locked. Otherwise do ++ * nothing. ++ * ++ * If the function returns true, it is impossible for the counter to ++ * become nonzero until the next qemu_lockcnt_unlock. ++ */ ++bool qemu_lockcnt_dec_if_lock(QemuLockCnt *lockcnt) ++{ ++ int val = qatomic_read(&lockcnt->count); ++ int locked_state = QEMU_LOCKCNT_STATE_LOCKED; ++ bool waited = false; ++ ++ while (val < 2 * QEMU_LOCKCNT_COUNT_STEP) { ++ /* If count is going 1->0, take the lock. The fast path is ++ * (1, unlocked)->(0, locked) or (1, unlocked)->(0, waiting). ++ */ ++ if (qemu_lockcnt_cmpxchg_or_wait(lockcnt, &val, locked_state, &waited)) { ++ return true; ++ } ++ ++ if (waited) { ++ /* At this point we do not know if there are more waiters. Assume ++ * there are. ++ */ ++ locked_state = QEMU_LOCKCNT_STATE_WAITING; ++ } ++ } ++ ++ /* If we were woken by another thread, but we're returning in unlocked ++ * state, we should also wake a thread because we are effectively ++ * releasing the lock that was given to us. This is the case where ++ * qemu_lockcnt_lock would leave QEMU_LOCKCNT_STATE_WAITING in the low ++ * bits, and qemu_lockcnt_inc_and_unlock would find it and wake someone. ++ */ ++ if (waited) { ++ lockcnt_wake(lockcnt); ++ } ++ return false; ++} ++ ++void qemu_lockcnt_lock(QemuLockCnt *lockcnt) ++{ ++ int val = qatomic_read(&lockcnt->count); ++ int step = QEMU_LOCKCNT_STATE_LOCKED; ++ bool waited = false; ++ ++ /* The third argument is only used if the low bits of val are 0 ++ * (QEMU_LOCKCNT_STATE_FREE), so just blindly mix in the desired ++ * state. ++ */ ++ while (!qemu_lockcnt_cmpxchg_or_wait(lockcnt, &val, val + step, &waited)) { ++ if (waited) { ++ /* At this point we do not know if there are more waiters. Assume ++ * there are. ++ */ ++ step = QEMU_LOCKCNT_STATE_WAITING; ++ } ++ } ++} ++ ++void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt) ++{ ++ int expected, new, val; ++ ++ val = qatomic_read(&lockcnt->count); ++ do { ++ expected = val; ++ new = (val + QEMU_LOCKCNT_COUNT_STEP) & ~QEMU_LOCKCNT_STATE_MASK; ++ trace_lockcnt_unlock_attempt(lockcnt, val, new); ++ val = qatomic_cmpxchg(&lockcnt->count, val, new); ++ } while (val != expected); ++ ++ trace_lockcnt_unlock_success(lockcnt, val, new); ++ if (val & QEMU_LOCKCNT_STATE_WAITING) { ++ lockcnt_wake(lockcnt); ++ } ++} ++ ++void qemu_lockcnt_unlock(QemuLockCnt *lockcnt) ++{ ++ int expected, new, val; ++ ++ val = qatomic_read(&lockcnt->count); ++ do { ++ expected = val; ++ new = val & ~QEMU_LOCKCNT_STATE_MASK; ++ trace_lockcnt_unlock_attempt(lockcnt, val, new); ++ val = qatomic_cmpxchg(&lockcnt->count, val, new); ++ } while (val != expected); ++ ++ trace_lockcnt_unlock_success(lockcnt, val, new); ++ if (val & QEMU_LOCKCNT_STATE_WAITING) { ++ lockcnt_wake(lockcnt); ++ } ++} ++ ++unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt) ++{ ++ return qatomic_read(&lockcnt->count) >> QEMU_LOCKCNT_COUNT_SHIFT; ++} ++#else ++void qemu_lockcnt_init(QemuLockCnt *lockcnt) ++{ ++ qemu_mutex_init(&lockcnt->mutex); ++ lockcnt->count = 0; ++} ++ ++void qemu_lockcnt_destroy(QemuLockCnt *lockcnt) ++{ ++ qemu_mutex_destroy(&lockcnt->mutex); ++} ++ ++void qemu_lockcnt_inc(QemuLockCnt *lockcnt) ++{ ++ int old; ++ for (;;) { ++ old = qatomic_read(&lockcnt->count); ++ if (old == 0) { ++ qemu_lockcnt_lock(lockcnt); ++ qemu_lockcnt_inc_and_unlock(lockcnt); ++ return; ++ } else { ++ if (qatomic_cmpxchg(&lockcnt->count, old, old + 1) == old) { ++ return; ++ } ++ } ++ } ++} ++ ++void qemu_lockcnt_dec(QemuLockCnt *lockcnt) ++{ ++ qatomic_dec(&lockcnt->count); ++} ++ ++/* Decrement a counter, and return locked if it is decremented to zero. ++ * It is impossible for the counter to become nonzero while the mutex ++ * is taken. ++ */ ++bool qemu_lockcnt_dec_and_lock(QemuLockCnt *lockcnt) ++{ ++ int val = qatomic_read(&lockcnt->count); ++ while (val > 1) { ++ int old = qatomic_cmpxchg(&lockcnt->count, val, val - 1); ++ if (old != val) { ++ val = old; ++ continue; ++ } ++ ++ return false; ++ } ++ ++ qemu_lockcnt_lock(lockcnt); ++ if (qatomic_fetch_dec(&lockcnt->count) == 1) { ++ return true; ++ } ++ ++ qemu_lockcnt_unlock(lockcnt); ++ return false; ++} ++ ++/* Decrement a counter and return locked if it is decremented to zero. ++ * Otherwise do nothing. ++ * ++ * It is impossible for the counter to become nonzero while the mutex ++ * is taken. ++ */ ++bool qemu_lockcnt_dec_if_lock(QemuLockCnt *lockcnt) ++{ ++ /* No need for acquire semantics if we return false. */ ++ int val = qatomic_read(&lockcnt->count); ++ if (val > 1) { ++ return false; ++ } ++ ++ qemu_lockcnt_lock(lockcnt); ++ if (qatomic_fetch_dec(&lockcnt->count) == 1) { ++ return true; ++ } ++ ++ qemu_lockcnt_inc_and_unlock(lockcnt); ++ return false; ++} ++ ++void qemu_lockcnt_lock(QemuLockCnt *lockcnt) ++{ ++ qemu_mutex_lock(&lockcnt->mutex); ++} ++ ++void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt) ++{ ++ qatomic_inc(&lockcnt->count); ++ qemu_mutex_unlock(&lockcnt->mutex); ++} ++ ++void qemu_lockcnt_unlock(QemuLockCnt *lockcnt) ++{ ++ qemu_mutex_unlock(&lockcnt->mutex); ++} ++ ++unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt) ++{ ++ return qatomic_read(&lockcnt->count); ++} ++#endif +diff --git a/qcow2/lib/util/main-loop.c b/qcow2/lib/util/main-loop.c +new file mode 100644 +index 00000000..a0386cfe +--- /dev/null ++++ b/qcow2/lib/util/main-loop.c +@@ -0,0 +1,653 @@ ++/* ++ * QEMU System Emulator ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/cutils.h" ++#include "qemu/timer.h" ++#include "sysemu/cpu-timers.h" ++#include "sysemu/replay.h" ++#include "qemu/main-loop.h" ++#include "block/aio.h" ++#include "block/thread-pool.h" ++#include "qemu/error-report.h" ++#include "qemu/queue.h" ++#include "qom/object.h" ++ ++#ifndef _WIN32 ++#include ++#endif ++ ++#ifndef _WIN32 ++ ++/* If we have signalfd, we mask out the signals we want to handle and then ++ * use signalfd to listen for them. We rely on whatever the current signal ++ * handler is to dispatch the signals when we receive them. ++ */ ++/* ++ * Disable CFI checks. ++ * We are going to call a signal handler directly. Such handler may or may not ++ * have been defined in our binary, so there's no guarantee that the pointer ++ * used to set the handler is a cfi-valid pointer. Since the handlers are ++ * stored in kernel memory, changing the handler to an attacker-defined ++ * function requires being able to call a sigaction() syscall, ++ * which is not as easy as overwriting a pointer in memory. ++ */ ++QEMU_DISABLE_CFI ++static void sigfd_handler(void *opaque) ++{ ++ int fd = (intptr_t)opaque; ++ struct qemu_signalfd_siginfo info; ++ struct sigaction action; ++ ssize_t len; ++ ++ while (1) { ++ len = RETRY_ON_EINTR(read(fd, &info, sizeof(info))); ++ ++ if (len == -1 && errno == EAGAIN) { ++ break; ++ } ++ ++ if (len != sizeof(info)) { ++ error_report("read from sigfd returned %zd: %s", len, ++ g_strerror(errno)); ++ return; ++ } ++ ++ sigaction(info.ssi_signo, NULL, &action); ++ if ((action.sa_flags & SA_SIGINFO) && action.sa_sigaction) { ++ sigaction_invoke(&action, &info); ++ } else if (action.sa_handler) { ++ action.sa_handler(info.ssi_signo); ++ } ++ } ++} ++ ++static int qemu_signal_init(Error **errp) ++{ ++ int sigfd; ++ sigset_t set; ++ ++ /* ++ * SIG_IPI must be blocked in the main thread and must not be caught ++ * by sigwait() in the signal thread. Otherwise, the cpu thread will ++ * not catch it reliably. ++ */ ++ sigemptyset(&set); ++ sigaddset(&set, SIG_IPI); ++ sigaddset(&set, SIGIO); ++ sigaddset(&set, SIGALRM); ++ sigaddset(&set, SIGBUS); ++ /* SIGINT cannot be handled via signalfd, so that ^C can be used ++ * to interrupt QEMU when it is being run under gdb. SIGHUP and ++ * SIGTERM are also handled asynchronously, even though it is not ++ * strictly necessary, because they use the same handler as SIGINT. ++ */ ++ pthread_sigmask(SIG_BLOCK, &set, NULL); ++ ++ sigdelset(&set, SIG_IPI); ++ sigfd = qemu_signalfd(&set); ++ if (sigfd == -1) { ++ error_setg_errno(errp, errno, "failed to create signalfd"); ++ return -errno; ++ } ++ ++ g_unix_set_fd_nonblocking(sigfd, true, NULL); ++ ++ qemu_set_fd_handler(sigfd, sigfd_handler, NULL, (void *)(intptr_t)sigfd); ++ ++ return 0; ++} ++ ++#else /* _WIN32 */ ++ ++static int qemu_signal_init(Error **errp) ++{ ++ return 0; ++} ++#endif ++ ++static AioContext *qemu_aio_context; ++static QEMUBH *qemu_notify_bh; ++ ++static void notify_event_cb(void *opaque) ++{ ++ /* No need to do anything; this bottom half is only used to ++ * kick the kernel out of ppoll/poll/WaitForMultipleObjects. ++ */ ++} ++ ++AioContext *qemu_get_aio_context(void) ++{ ++ return qemu_aio_context; ++} ++ ++void qemu_notify_event(void) ++{ ++ if (!qemu_aio_context) { ++ return; ++ } ++ qemu_bh_schedule(qemu_notify_bh); ++} ++ ++static GArray *gpollfds; ++ ++int qemu_init_main_loop(Error **errp) ++{ ++ int ret; ++ GSource *src; ++ ++ init_clocks(qemu_timer_notify_cb); ++ ++ ret = qemu_signal_init(errp); ++ if (ret) { ++ return ret; ++ } ++ ++ qemu_aio_context = aio_context_new(errp); ++ if (!qemu_aio_context) { ++ return -EMFILE; ++ } ++ qemu_set_current_aio_context(qemu_aio_context); ++ qemu_notify_bh = qemu_bh_new(notify_event_cb, NULL); ++ gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); ++ src = aio_get_g_source(qemu_aio_context); ++ g_source_set_name(src, "aio-context"); ++ g_source_attach(src, NULL); ++ g_source_unref(src); ++ src = iohandler_get_g_source(); ++ g_source_set_name(src, "io-handler"); ++ g_source_attach(src, NULL); ++ g_source_unref(src); ++ return 0; ++} ++ ++static void main_loop_update_params(EventLoopBase *base, Error **errp) ++{ ++ ERRP_GUARD(); ++ ++ if (!qemu_aio_context) { ++ error_setg(errp, "qemu aio context not ready"); ++ return; ++ } ++ ++ aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch); ++ ++ aio_context_set_thread_pool_params(qemu_aio_context, base->thread_pool_min, ++ base->thread_pool_max, errp); ++} ++ ++MainLoop *mloop; ++ ++static void main_loop_init(EventLoopBase *base, Error **errp) ++{ ++ MainLoop *m = MAIN_LOOP(base); ++ ++ if (mloop) { ++ error_setg(errp, "only one main-loop instance allowed"); ++ return; ++ } ++ ++ main_loop_update_params(base, errp); ++ ++ mloop = m; ++ return; ++} ++ ++static bool main_loop_can_be_deleted(EventLoopBase *base) ++{ ++ return false; ++} ++ ++static void main_loop_class_init(ObjectClass *oc, void *class_data) ++{ ++ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(oc); ++ ++ bc->init = main_loop_init; ++ bc->update_params = main_loop_update_params; ++ bc->can_be_deleted = main_loop_can_be_deleted; ++} ++ ++static const TypeInfo main_loop_info = { ++ .name = TYPE_MAIN_LOOP, ++ .parent = TYPE_EVENT_LOOP_BASE, ++ .class_init = main_loop_class_init, ++ .instance_size = sizeof(MainLoop), ++}; ++ ++static void main_loop_register_types(void) ++{ ++ type_register_static(&main_loop_info); ++} ++ ++type_init(main_loop_register_types) ++ ++static int max_priority; ++ ++#ifndef _WIN32 ++static int glib_pollfds_idx; ++static int glib_n_poll_fds; ++ ++static void glib_pollfds_fill(int64_t *cur_timeout) ++{ ++ GMainContext *context = g_main_context_default(); ++ int timeout = 0; ++ int64_t timeout_ns; ++ int n; ++ ++ g_main_context_prepare(context, &max_priority); ++ ++ glib_pollfds_idx = gpollfds->len; ++ n = glib_n_poll_fds; ++ do { ++ GPollFD *pfds; ++ glib_n_poll_fds = n; ++ g_array_set_size(gpollfds, glib_pollfds_idx + glib_n_poll_fds); ++ pfds = &g_array_index(gpollfds, GPollFD, glib_pollfds_idx); ++ n = g_main_context_query(context, max_priority, &timeout, pfds, ++ glib_n_poll_fds); ++ } while (n != glib_n_poll_fds); ++ ++ if (timeout < 0) { ++ timeout_ns = -1; ++ } else { ++ timeout_ns = (int64_t)timeout * (int64_t)SCALE_MS; ++ } ++ ++ *cur_timeout = qemu_soonest_timeout(timeout_ns, *cur_timeout); ++} ++ ++static void glib_pollfds_poll(void) ++{ ++ GMainContext *context = g_main_context_default(); ++ GPollFD *pfds = &g_array_index(gpollfds, GPollFD, glib_pollfds_idx); ++ ++ if (g_main_context_check(context, max_priority, pfds, glib_n_poll_fds)) { ++ g_main_context_dispatch(context); ++ } ++} ++ ++#define MAX_MAIN_LOOP_SPIN (1000) ++ ++static int os_host_main_loop_wait(int64_t timeout) ++{ ++ GMainContext *context = g_main_context_default(); ++ int ret; ++ ++ g_main_context_acquire(context); ++ ++ glib_pollfds_fill(&timeout); ++ ++ bql_unlock(); ++ replay_mutex_unlock(); ++ ++ ret = qemu_poll_ns((GPollFD *)gpollfds->data, gpollfds->len, timeout); ++ ++ replay_mutex_lock(); ++ bql_lock(); ++ ++ glib_pollfds_poll(); ++ ++ g_main_context_release(context); ++ ++ return ret; ++} ++#else ++/***********************************************************/ ++/* Polling handling */ ++ ++typedef struct PollingEntry { ++ PollingFunc *func; ++ void *opaque; ++ struct PollingEntry *next; ++} PollingEntry; ++ ++static PollingEntry *first_polling_entry; ++ ++int qemu_add_polling_cb(PollingFunc *func, void *opaque) ++{ ++ PollingEntry **ppe, *pe; ++ pe = g_new0(PollingEntry, 1); ++ pe->func = func; ++ pe->opaque = opaque; ++ for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next); ++ *ppe = pe; ++ return 0; ++} ++ ++void qemu_del_polling_cb(PollingFunc *func, void *opaque) ++{ ++ PollingEntry **ppe, *pe; ++ for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next) { ++ pe = *ppe; ++ if (pe->func == func && pe->opaque == opaque) { ++ *ppe = pe->next; ++ g_free(pe); ++ break; ++ } ++ } ++} ++ ++/***********************************************************/ ++/* Wait objects support */ ++typedef struct WaitObjects { ++ int num; ++ int revents[MAXIMUM_WAIT_OBJECTS]; ++ HANDLE events[MAXIMUM_WAIT_OBJECTS]; ++ WaitObjectFunc *func[MAXIMUM_WAIT_OBJECTS]; ++ void *opaque[MAXIMUM_WAIT_OBJECTS]; ++} WaitObjects; ++ ++static WaitObjects wait_objects = {0}; ++ ++int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) ++{ ++ int i; ++ WaitObjects *w = &wait_objects; ++ ++ if (w->num >= MAXIMUM_WAIT_OBJECTS) { ++ return -1; ++ } ++ ++ for (i = 0; i < w->num; i++) { ++ /* check if the same handle is added twice */ ++ if (w->events[i] == handle) { ++ return -1; ++ } ++ } ++ ++ w->events[w->num] = handle; ++ w->func[w->num] = func; ++ w->opaque[w->num] = opaque; ++ w->revents[w->num] = 0; ++ w->num++; ++ return 0; ++} ++ ++void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) ++{ ++ int i, found; ++ WaitObjects *w = &wait_objects; ++ ++ found = 0; ++ for (i = 0; i < w->num; i++) { ++ if (w->events[i] == handle) { ++ found = 1; ++ } ++ if (found && i < (MAXIMUM_WAIT_OBJECTS - 1)) { ++ w->events[i] = w->events[i + 1]; ++ w->func[i] = w->func[i + 1]; ++ w->opaque[i] = w->opaque[i + 1]; ++ w->revents[i] = w->revents[i + 1]; ++ } ++ } ++ if (found) { ++ w->num--; ++ } ++} ++ ++static int pollfds_fill(GArray *pollfds, fd_set *rfds, fd_set *wfds, ++ fd_set *xfds) ++{ ++ int nfds = -1; ++ int i; ++ ++ for (i = 0; i < pollfds->len; i++) { ++ GPollFD *pfd = &g_array_index(pollfds, GPollFD, i); ++ int fd = pfd->fd; ++ int events = pfd->events; ++ if (events & G_IO_IN) { ++ FD_SET(fd, rfds); ++ nfds = MAX(nfds, fd); ++ } ++ if (events & G_IO_OUT) { ++ FD_SET(fd, wfds); ++ nfds = MAX(nfds, fd); ++ } ++ if (events & G_IO_PRI) { ++ FD_SET(fd, xfds); ++ nfds = MAX(nfds, fd); ++ } ++ } ++ return nfds; ++} ++ ++static void pollfds_poll(GArray *pollfds, int nfds, fd_set *rfds, ++ fd_set *wfds, fd_set *xfds) ++{ ++ int i; ++ ++ for (i = 0; i < pollfds->len; i++) { ++ GPollFD *pfd = &g_array_index(pollfds, GPollFD, i); ++ int fd = pfd->fd; ++ int revents = 0; ++ ++ if (FD_ISSET(fd, rfds)) { ++ revents |= G_IO_IN; ++ } ++ if (FD_ISSET(fd, wfds)) { ++ revents |= G_IO_OUT; ++ } ++ if (FD_ISSET(fd, xfds)) { ++ revents |= G_IO_PRI; ++ } ++ pfd->revents = revents & pfd->events; ++ } ++} ++ ++static int os_host_main_loop_wait(int64_t timeout) ++{ ++ GMainContext *context = g_main_context_default(); ++ GPollFD poll_fds[1024 * 2]; /* this is probably overkill */ ++ int select_ret = 0; ++ int g_poll_ret, ret, i, n_poll_fds; ++ PollingEntry *pe; ++ WaitObjects *w = &wait_objects; ++ gint poll_timeout; ++ int64_t poll_timeout_ns; ++ static struct timeval tv0; ++ fd_set rfds, wfds, xfds; ++ int nfds; ++ ++ g_main_context_acquire(context); ++ ++ /* XXX: need to suppress polling by better using win32 events */ ++ ret = 0; ++ for (pe = first_polling_entry; pe != NULL; pe = pe->next) { ++ ret |= pe->func(pe->opaque); ++ } ++ if (ret != 0) { ++ g_main_context_release(context); ++ return ret; ++ } ++ ++ FD_ZERO(&rfds); ++ FD_ZERO(&wfds); ++ FD_ZERO(&xfds); ++ nfds = pollfds_fill(gpollfds, &rfds, &wfds, &xfds); ++ if (nfds >= 0) { ++ select_ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv0); ++ if (select_ret != 0) { ++ timeout = 0; ++ } ++ if (select_ret > 0) { ++ pollfds_poll(gpollfds, nfds, &rfds, &wfds, &xfds); ++ } ++ } ++ ++ g_main_context_prepare(context, &max_priority); ++ n_poll_fds = g_main_context_query(context, max_priority, &poll_timeout, ++ poll_fds, ARRAY_SIZE(poll_fds)); ++ g_assert(n_poll_fds + w->num <= ARRAY_SIZE(poll_fds)); ++ ++ for (i = 0; i < w->num; i++) { ++ poll_fds[n_poll_fds + i].fd = (DWORD_PTR)w->events[i]; ++ poll_fds[n_poll_fds + i].events = G_IO_IN; ++ } ++ ++ if (poll_timeout < 0) { ++ poll_timeout_ns = -1; ++ } else { ++ poll_timeout_ns = (int64_t)poll_timeout * (int64_t)SCALE_MS; ++ } ++ ++ poll_timeout_ns = qemu_soonest_timeout(poll_timeout_ns, timeout); ++ ++ bql_unlock(); ++ ++ replay_mutex_unlock(); ++ ++ g_poll_ret = qemu_poll_ns(poll_fds, n_poll_fds + w->num, poll_timeout_ns); ++ ++ replay_mutex_lock(); ++ ++ bql_lock(); ++ if (g_poll_ret > 0) { ++ for (i = 0; i < w->num; i++) { ++ w->revents[i] = poll_fds[n_poll_fds + i].revents; ++ } ++ for (i = 0; i < w->num; i++) { ++ if (w->revents[i] && w->func[i]) { ++ w->func[i](w->opaque[i]); ++ } ++ } ++ } ++ ++ if (g_main_context_check(context, max_priority, poll_fds, n_poll_fds)) { ++ g_main_context_dispatch(context); ++ } ++ ++ g_main_context_release(context); ++ ++ return select_ret || g_poll_ret; ++} ++#endif ++ ++static NotifierList main_loop_poll_notifiers = ++ NOTIFIER_LIST_INITIALIZER(main_loop_poll_notifiers); ++ ++void main_loop_poll_add_notifier(Notifier *notify) ++{ ++ notifier_list_add(&main_loop_poll_notifiers, notify); ++} ++ ++void main_loop_poll_remove_notifier(Notifier *notify) ++{ ++ notifier_remove(notify); ++} ++ ++void main_loop_wait(int nonblocking) ++{ ++ MainLoopPoll mlpoll = { ++ .state = MAIN_LOOP_POLL_FILL, ++ .timeout = UINT32_MAX, ++ .pollfds = gpollfds, ++ }; ++ int ret; ++ int64_t timeout_ns; ++ ++ if (nonblocking) { ++ mlpoll.timeout = 0; ++ } ++ ++ /* poll any events */ ++ g_array_set_size(gpollfds, 0); /* reset for new iteration */ ++ /* XXX: separate device handlers from system ones */ ++ notifier_list_notify(&main_loop_poll_notifiers, &mlpoll); ++ ++ if (mlpoll.timeout == UINT32_MAX) { ++ timeout_ns = -1; ++ } else { ++ timeout_ns = (uint64_t)mlpoll.timeout * (int64_t)(SCALE_MS); ++ } ++ ++ timeout_ns = qemu_soonest_timeout(timeout_ns, ++ timerlistgroup_deadline_ns( ++ &main_loop_tlg)); ++ ++ ret = os_host_main_loop_wait(timeout_ns); ++ mlpoll.state = ret < 0 ? MAIN_LOOP_POLL_ERR : MAIN_LOOP_POLL_OK; ++ notifier_list_notify(&main_loop_poll_notifiers, &mlpoll); ++ ++ if (icount_enabled()) { ++ /* ++ * CPU thread can infinitely wait for event after ++ * missing the warp ++ */ ++ icount_start_warp_timer(); ++ } ++ qemu_clock_run_all_timers(); ++} ++ ++/* Functions to operate on the main QEMU AioContext. */ ++ ++QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name, ++ MemReentrancyGuard *reentrancy_guard) ++{ ++ return aio_bh_new_full(qemu_aio_context, cb, opaque, name, ++ reentrancy_guard); ++} ++ ++/* ++ * Functions to operate on the I/O handler AioContext. ++ * This context runs on top of main loop. We can't reuse qemu_aio_context ++ * because iohandlers mustn't be polled by aio_poll(qemu_aio_context). ++ */ ++static AioContext *iohandler_ctx; ++ ++static void iohandler_init(void) ++{ ++ if (!iohandler_ctx) { ++ iohandler_ctx = aio_context_new(&error_abort); ++ } ++} ++ ++AioContext *iohandler_get_aio_context(void) ++{ ++ iohandler_init(); ++ return iohandler_ctx; ++} ++ ++GSource *iohandler_get_g_source(void) ++{ ++ iohandler_init(); ++ return aio_get_g_source(iohandler_ctx); ++} ++ ++void qemu_set_fd_handler(int fd, ++ IOHandler *fd_read, ++ IOHandler *fd_write, ++ void *opaque) ++{ ++ iohandler_init(); ++ aio_set_fd_handler(iohandler_ctx, fd, fd_read, fd_write, NULL, NULL, ++ opaque); ++} ++ ++void event_notifier_set_handler(EventNotifier *e, ++ EventNotifierHandler *handler) ++{ ++ iohandler_init(); ++ aio_set_event_notifier(iohandler_ctx, e, handler, NULL, NULL); ++} +diff --git a/qcow2/lib/util/memalign.c b/qcow2/lib/util/memalign.c +new file mode 100644 +index 00000000..c199ae70 +--- /dev/null ++++ b/qcow2/lib/util/memalign.c +@@ -0,0 +1,92 @@ ++/* ++ * memalign.c: Allocate an aligned memory region ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2010-2016 Red Hat, Inc. ++ * Copyright (c) 2022 Linaro Ltd ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/host-utils.h" ++#include "qemu/memalign.h" ++#include "trace.h" ++ ++void *qemu_try_memalign(size_t alignment, size_t size) ++{ ++ void *ptr; ++ ++ if (alignment < sizeof(void*)) { ++ alignment = sizeof(void*); ++ } else { ++ g_assert(is_power_of_2(alignment)); ++ } ++ ++ /* ++ * Handling of 0 allocations varies among the different ++ * platform APIs (for instance _aligned_malloc() will ++ * fail) -- ensure that we always return a valid non-NULL ++ * pointer that can be freed by qemu_vfree(). ++ */ ++ if (size == 0) { ++ size++; ++ } ++#if defined(CONFIG_POSIX_MEMALIGN) ++ int ret; ++ ret = posix_memalign(&ptr, alignment, size); ++ if (ret != 0) { ++ errno = ret; ++ ptr = NULL; ++ } ++#elif defined(CONFIG_ALIGNED_MALLOC) ++ ptr = _aligned_malloc(size, alignment); ++#elif defined(CONFIG_VALLOC) ++ ptr = valloc(size); ++#elif defined(CONFIG_MEMALIGN) ++ ptr = memalign(alignment, size); ++#else ++ #error No function to allocate aligned memory available ++#endif ++ trace_qemu_memalign(alignment, size, ptr); ++ return ptr; ++} ++ ++void *qemu_memalign(size_t alignment, size_t size) ++{ ++ void *p = qemu_try_memalign(alignment, size); ++ if (p) { ++ return p; ++ } ++ fprintf(stderr, ++ "qemu_memalign: failed to allocate %zu bytes at alignment %zu: %s\n", ++ size, alignment, strerror(errno)); ++ abort(); ++} ++ ++void qemu_vfree(void *ptr) ++{ ++ trace_qemu_vfree(ptr); ++#if !defined(CONFIG_POSIX_MEMALIGN) && defined(CONFIG_ALIGNED_MALLOC) ++ /* Only Windows _aligned_malloc needs a special free function */ ++ _aligned_free(ptr); ++#else ++ free(ptr); ++#endif ++} +diff --git a/qcow2/lib/util/module.c b/qcow2/lib/util/module.c +new file mode 100644 +index 00000000..3eb0f06d +--- /dev/null ++++ b/qcow2/lib/util/module.c +@@ -0,0 +1,405 @@ ++/* ++ * QEMU Module Infrastructure ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#include "qemu/osdep.h" ++#ifdef CONFIG_MODULES ++#include ++#endif ++#include "qemu/queue.h" ++#include "qemu/module.h" ++#include "qemu/cutils.h" ++#include "qemu/config-file.h" ++#include "qapi/error.h" ++#ifdef CONFIG_MODULE_UPGRADES ++#include "qemu-version.h" ++#endif ++#include "trace.h" ++ ++typedef struct ModuleEntry ++{ ++ void (*init)(void); ++ QTAILQ_ENTRY(ModuleEntry) node; ++ module_init_type type; ++} ModuleEntry; ++ ++typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList; ++ ++static ModuleTypeList init_type_list[MODULE_INIT_MAX]; ++static bool modules_init_done[MODULE_INIT_MAX]; ++ ++static ModuleTypeList dso_init_list; ++ ++static void init_lists(void) ++{ ++ static int inited; ++ int i; ++ ++ if (inited) { ++ return; ++ } ++ ++ for (i = 0; i < MODULE_INIT_MAX; i++) { ++ QTAILQ_INIT(&init_type_list[i]); ++ } ++ ++ QTAILQ_INIT(&dso_init_list); ++ ++ inited = 1; ++} ++ ++ ++static ModuleTypeList *find_type(module_init_type type) ++{ ++ init_lists(); ++ ++ return &init_type_list[type]; ++} ++ ++void register_module_init(void (*fn)(void), module_init_type type) ++{ ++ ModuleEntry *e; ++ ModuleTypeList *l; ++ ++ e = g_malloc0(sizeof(*e)); ++ e->init = fn; ++ e->type = type; ++ ++ l = find_type(type); ++ ++ QTAILQ_INSERT_TAIL(l, e, node); ++} ++ ++void register_dso_module_init(void (*fn)(void), module_init_type type) ++{ ++ ModuleEntry *e; ++ ++ init_lists(); ++ ++ e = g_malloc0(sizeof(*e)); ++ e->init = fn; ++ e->type = type; ++ ++ QTAILQ_INSERT_TAIL(&dso_init_list, e, node); ++} ++ ++void module_call_init(module_init_type type) ++{ ++ ModuleTypeList *l; ++ ModuleEntry *e; ++ ++ if (modules_init_done[type]) { ++ return; ++ } ++ ++ l = find_type(type); ++ ++ QTAILQ_FOREACH(e, l, node) { ++ e->init(); ++ } ++ ++ modules_init_done[type] = true; ++} ++ ++#ifdef CONFIG_MODULES ++ ++static const QemuModinfo module_info_stub[] = { { ++ /* end of list */ ++} }; ++static const QemuModinfo *module_info = module_info_stub; ++static const char *module_arch; ++ ++void module_init_info(const QemuModinfo *info) ++{ ++ module_info = info; ++} ++ ++void module_allow_arch(const char *arch) ++{ ++ module_arch = arch; ++} ++ ++static bool module_check_arch(const QemuModinfo *modinfo) ++{ ++ if (modinfo->arch) { ++ if (!module_arch) { ++ /* no arch set -> ignore all */ ++ return false; ++ } ++ if (strcmp(module_arch, modinfo->arch) != 0) { ++ /* mismatch */ ++ return false; ++ } ++ } ++ return true; ++} ++ ++/* ++ * module_load_dso: attempt to load an existing dso file ++ * ++ * fname: full pathname of the file to load ++ * export_symbols: if true, add the symbols to the global name space ++ * errp: error to set. ++ * ++ * Return value: true on success, false on error, and errp will be set. ++ */ ++static bool module_load_dso(const char *fname, bool export_symbols, ++ Error **errp) ++{ ++ GModule *g_module; ++ void (*sym)(void); ++ ModuleEntry *e, *next; ++ int flags; ++ ++ assert(QTAILQ_EMPTY(&dso_init_list)); ++ ++ flags = 0; ++ if (!export_symbols) { ++ flags |= G_MODULE_BIND_LOCAL; ++ } ++ g_module = g_module_open(fname, flags); ++ if (!g_module) { ++ error_setg(errp, "failed to open module: %s", g_module_error()); ++ return false; ++ } ++ if (!g_module_symbol(g_module, DSO_STAMP_FUN_STR, (gpointer *)&sym)) { ++ error_setg(errp, "failed to initialize module: %s", fname); ++ /* ++ * Print some info if this is a QEMU module (but from different build), ++ * this will make debugging user problems easier. ++ */ ++ if (g_module_symbol(g_module, "qemu_module_dummy", (gpointer *)&sym)) { ++ error_append_hint(errp, ++ "Only modules from the same build can be loaded.\n"); ++ } ++ g_module_close(g_module); ++ return false; ++ } ++ ++ QTAILQ_FOREACH(e, &dso_init_list, node) { ++ e->init(); ++ register_module_init(e->init, e->type); ++ } ++ trace_module_load_module(fname); ++ QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) { ++ QTAILQ_REMOVE(&dso_init_list, e, node); ++ g_free(e); ++ } ++ return true; ++} ++ ++int module_load(const char *prefix, const char *name, Error **errp) ++{ ++ int rv = -1; ++#ifdef CONFIG_MODULE_UPGRADES ++ char *version_dir; ++#endif ++ const char *search_dir; ++ char *dirs[5]; ++ char *module_name; ++ int i = 0, n_dirs = 0; ++ bool export_symbols = false; ++ static GHashTable *loaded_modules; ++ const QemuModinfo *modinfo; ++ const char **sl; ++ ++ if (!g_module_supported()) { ++ error_setg(errp, "%s", "this platform does not support GLib modules"); ++ return -1; ++ } ++ ++ if (!loaded_modules) { ++ loaded_modules = g_hash_table_new(g_str_hash, g_str_equal); ++ } ++ ++ /* allocate all resources managed by the out: label here */ ++ module_name = g_strdup_printf("%s%s", prefix, name); ++ ++ if (g_hash_table_contains(loaded_modules, module_name)) { ++ g_free(module_name); ++ return 2; /* module already loaded */ ++ } ++ g_hash_table_add(loaded_modules, module_name); ++ ++ search_dir = getenv("QEMU_MODULE_DIR"); ++ if (search_dir != NULL) { ++ dirs[n_dirs++] = g_strdup_printf("%s", search_dir); ++ } ++ dirs[n_dirs++] = get_relocated_path(CONFIG_QEMU_MODDIR); ++ ++#ifdef CONFIG_MODULE_UPGRADES ++ version_dir = g_strcanon(g_strdup(QEMU_PKGVERSION), ++ G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "+-.~", ++ '_'); ++ dirs[n_dirs++] = g_strdup_printf("/var/run/qemu/%s", version_dir); ++#endif ++ assert(n_dirs <= ARRAY_SIZE(dirs)); ++ ++ /* end of resources managed by the out: label */ ++ ++ for (modinfo = module_info; modinfo->name != NULL; modinfo++) { ++ if (modinfo->arch) { ++ if (strcmp(modinfo->name, module_name) == 0) { ++ if (!module_check_arch(modinfo)) { ++ error_setg(errp, "module arch does not match: " ++ "expected '%s', got '%s'", module_arch, modinfo->arch); ++ goto out; ++ } ++ } ++ } ++ if (modinfo->deps) { ++ if (strcmp(modinfo->name, module_name) == 0) { ++ /* we depend on other module(s) */ ++ for (sl = modinfo->deps; *sl != NULL; sl++) { ++ int subrv = module_load("", *sl, errp); ++ if (subrv <= 0) { ++ rv = subrv; ++ goto out; ++ } ++ } ++ } else { ++ for (sl = modinfo->deps; *sl != NULL; sl++) { ++ if (strcmp(module_name, *sl) == 0) { ++ /* another module depends on us */ ++ export_symbols = true; ++ } ++ } ++ } ++ } ++ } ++ ++ for (i = 0; i < n_dirs; i++) { ++ char *fname = g_strdup_printf("%s/%s%s", ++ dirs[i], module_name, CONFIG_HOST_DSOSUF); ++ int ret = access(fname, F_OK); ++ if (ret != 0 && (errno == ENOENT || errno == ENOTDIR)) { ++ /* ++ * if we don't find the module in this dir, try the next one. ++ * If we don't find it in any dir, that can be fine too: user ++ * did not install the module. We will return 0 in this case ++ * with no error set. ++ */ ++ g_free(fname); ++ continue; ++ } else if (ret != 0) { ++ /* most common is EACCES here */ ++ error_setg_errno(errp, errno, "error trying to access %s", fname); ++ } else if (module_load_dso(fname, export_symbols, errp)) { ++ rv = 1; /* module successfully loaded */ ++ } ++ g_free(fname); ++ goto out; ++ } ++ rv = 0; /* module not found */ ++ ++out: ++ if (rv <= 0) { ++ g_hash_table_remove(loaded_modules, module_name); ++ g_free(module_name); ++ } ++ for (i = 0; i < n_dirs; i++) { ++ g_free(dirs[i]); ++ } ++ return rv; ++} ++ ++static bool module_loaded_qom_all; ++ ++int module_load_qom(const char *type, Error **errp) ++{ ++ const QemuModinfo *modinfo; ++ const char **sl; ++ int rv = 0; ++ ++ if (!type) { ++ error_setg(errp, "%s", "type is NULL"); ++ return -1; ++ } ++ ++ trace_module_lookup_object_type(type); ++ for (modinfo = module_info; modinfo->name != NULL; modinfo++) { ++ if (!modinfo->objs) { ++ continue; ++ } ++ if (!module_check_arch(modinfo)) { ++ continue; ++ } ++ for (sl = modinfo->objs; *sl != NULL; sl++) { ++ if (strcmp(type, *sl) == 0) { ++ if (rv > 0) { ++ error_setg(errp, "multiple modules providing '%s'", type); ++ return -1; ++ } ++ rv = module_load("", modinfo->name, errp); ++ if (rv < 0) { ++ return rv; ++ } ++ } ++ } ++ } ++ return rv; ++} ++ ++void module_load_qom_all(void) ++{ ++ const QemuModinfo *modinfo; ++ ++ if (module_loaded_qom_all) { ++ return; ++ } ++ ++ for (modinfo = module_info; modinfo->name != NULL; modinfo++) { ++ Error *local_err = NULL; ++ if (!modinfo->objs) { ++ continue; ++ } ++ if (!module_check_arch(modinfo)) { ++ continue; ++ } ++ if (module_load("", modinfo->name, &local_err) < 0) { ++ error_report_err(local_err); ++ } ++ } ++ module_loaded_qom_all = true; ++} ++ ++void qemu_load_module_for_opts(const char *group) ++{ ++ const QemuModinfo *modinfo; ++ const char **sl; ++ ++ for (modinfo = module_info; modinfo->name != NULL; modinfo++) { ++ if (!modinfo->opts) { ++ continue; ++ } ++ for (sl = modinfo->opts; *sl != NULL; sl++) { ++ if (strcmp(group, *sl) == 0) { ++ Error *local_err = NULL; ++ if (module_load("", modinfo->name, &local_err) < 0) { ++ error_report_err(local_err); ++ } ++ } ++ } ++ } ++} ++ ++#else ++ ++void module_allow_arch(const char *arch) {} ++void qemu_load_module_for_opts(const char *group) {} ++int module_load(const char *prefix, const char *name, Error **errp) { return 2; } ++int module_load_qom(const char *type, Error **errp) { return 2; } ++void module_load_qom_all(void) {} ++ ++#endif +diff --git a/qcow2/lib/util/notify.c b/qcow2/lib/util/notify.c +new file mode 100644 +index 00000000..c6e158ff +--- /dev/null ++++ b/qcow2/lib/util/notify.c +@@ -0,0 +1,77 @@ ++/* ++ * Notifier lists ++ * ++ * Copyright IBM, Corp. 2010 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/notify.h" ++ ++void notifier_list_init(NotifierList *list) ++{ ++ QLIST_INIT(&list->notifiers); ++} ++ ++void notifier_list_add(NotifierList *list, Notifier *notifier) ++{ ++ QLIST_INSERT_HEAD(&list->notifiers, notifier, node); ++} ++ ++void notifier_remove(Notifier *notifier) ++{ ++ QLIST_REMOVE(notifier, node); ++} ++ ++void notifier_list_notify(NotifierList *list, void *data) ++{ ++ Notifier *notifier, *next; ++ ++ QLIST_FOREACH_SAFE(notifier, &list->notifiers, node, next) { ++ notifier->notify(notifier, data); ++ } ++} ++ ++bool notifier_list_empty(NotifierList *list) ++{ ++ return QLIST_EMPTY(&list->notifiers); ++} ++ ++void notifier_with_return_list_init(NotifierWithReturnList *list) ++{ ++ QLIST_INIT(&list->notifiers); ++} ++ ++void notifier_with_return_list_add(NotifierWithReturnList *list, ++ NotifierWithReturn *notifier) ++{ ++ QLIST_INSERT_HEAD(&list->notifiers, notifier, node); ++} ++ ++void notifier_with_return_remove(NotifierWithReturn *notifier) ++{ ++ QLIST_REMOVE(notifier, node); ++} ++ ++int notifier_with_return_list_notify(NotifierWithReturnList *list, void *data, ++ Error **errp) ++{ ++ NotifierWithReturn *notifier, *next; ++ int ret = 0; ++ ++ QLIST_FOREACH_SAFE(notifier, &list->notifiers, node, next) { ++ ret = notifier->notify(notifier, data, errp); ++ if (ret != 0) { ++ break; ++ } ++ } ++ return ret; ++} +diff --git a/qcow2/lib/util/osdep.c b/qcow2/lib/util/osdep.c +new file mode 100644 +index 00000000..77036983 +--- /dev/null ++++ b/qcow2/lib/util/osdep.c +@@ -0,0 +1,615 @@ ++/* ++ * QEMU low level functions ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/cutils.h" ++#include "qemu/sockets.h" ++#include "qemu/error-report.h" ++#include "qemu/madvise.h" ++#include "qemu/mprotect.h" ++#include "qemu/hw-version.h" ++#include "monitor/monitor.h" ++ ++static const char *hw_version = QEMU_HW_VERSION; ++ ++int socket_set_cork(int fd, int v) ++{ ++#if defined(SOL_TCP) && defined(TCP_CORK) ++ return setsockopt(fd, SOL_TCP, TCP_CORK, &v, sizeof(v)); ++#else ++ return 0; ++#endif ++} ++ ++int socket_set_nodelay(int fd) ++{ ++ int v = 1; ++ return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); ++} ++ ++int qemu_madvise(void *addr, size_t len, int advice) ++{ ++ if (advice == QEMU_MADV_INVALID) { ++ errno = EINVAL; ++ return -1; ++ } ++#if defined(CONFIG_MADVISE) ++ return madvise(addr, len, advice); ++#elif defined(CONFIG_POSIX_MADVISE) ++ int rc = posix_madvise(addr, len, advice); ++ if (rc) { ++ errno = rc; ++ return -1; ++ } ++ return 0; ++#else ++ errno = ENOSYS; ++ return -1; ++#endif ++} ++ ++static int qemu_mprotect__osdep(void *addr, size_t size, int prot) ++{ ++ g_assert(!((uintptr_t)addr & ~qemu_real_host_page_mask())); ++ g_assert(!(size & ~qemu_real_host_page_mask())); ++ ++#ifdef _WIN32 ++ DWORD old_protect; ++ ++ if (!VirtualProtect(addr, size, prot, &old_protect)) { ++ g_autofree gchar *emsg = g_win32_error_message(GetLastError()); ++ error_report("%s: VirtualProtect failed: %s", __func__, emsg); ++ return -1; ++ } ++ return 0; ++#else ++ if (mprotect(addr, size, prot)) { ++ error_report("%s: mprotect failed: %s", __func__, strerror(errno)); ++ return -1; ++ } ++ return 0; ++#endif ++} ++ ++int qemu_mprotect_rw(void *addr, size_t size) ++{ ++#ifdef _WIN32 ++ return qemu_mprotect__osdep(addr, size, PAGE_READWRITE); ++#else ++ return qemu_mprotect__osdep(addr, size, PROT_READ | PROT_WRITE); ++#endif ++} ++ ++int qemu_mprotect_rwx(void *addr, size_t size) ++{ ++#ifdef _WIN32 ++ return qemu_mprotect__osdep(addr, size, PAGE_EXECUTE_READWRITE); ++#else ++ return qemu_mprotect__osdep(addr, size, PROT_READ | PROT_WRITE | PROT_EXEC); ++#endif ++} ++ ++int qemu_mprotect_none(void *addr, size_t size) ++{ ++#ifdef _WIN32 ++ return qemu_mprotect__osdep(addr, size, PAGE_NOACCESS); ++#else ++ return qemu_mprotect__osdep(addr, size, PROT_NONE); ++#endif ++} ++ ++#ifndef _WIN32 ++ ++static int fcntl_op_setlk = -1; ++static int fcntl_op_getlk = -1; ++ ++/* ++ * Dups an fd and sets the flags ++ */ ++int qemu_dup_flags(int fd, int flags) ++{ ++ int ret; ++ int serrno; ++ int dup_flags; ++ ++ ret = qemu_dup(fd); ++ if (ret == -1) { ++ goto fail; ++ } ++ ++ dup_flags = fcntl(ret, F_GETFL); ++ if (dup_flags == -1) { ++ goto fail; ++ } ++ ++ if ((flags & O_SYNC) != (dup_flags & O_SYNC)) { ++ errno = EINVAL; ++ goto fail; ++ } ++ ++ /* Set/unset flags that we can with fcntl */ ++ if (fcntl(ret, F_SETFL, flags) == -1) { ++ goto fail; ++ } ++ ++ /* Truncate the file in the cases that open() would truncate it */ ++ if (flags & O_TRUNC || ++ ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))) { ++ if (ftruncate(ret, 0) == -1) { ++ goto fail; ++ } ++ } ++ ++ return ret; ++ ++fail: ++ serrno = errno; ++ if (ret != -1) { ++ close(ret); ++ } ++ errno = serrno; ++ return -1; ++} ++ ++int qemu_dup(int fd) ++{ ++ int ret; ++#ifdef F_DUPFD_CLOEXEC ++ ret = fcntl(fd, F_DUPFD_CLOEXEC, 0); ++#else ++ ret = dup(fd); ++ if (ret != -1) { ++ qemu_set_cloexec(ret); ++ } ++#endif ++ return ret; ++} ++ ++static int qemu_parse_fdset(const char *param) ++{ ++ return qemu_parse_fd(param); ++} ++ ++static void qemu_probe_lock_ops(void) ++{ ++ if (fcntl_op_setlk == -1) { ++#ifdef F_OFD_SETLK ++ int fd; ++ int ret; ++ struct flock fl = { ++ .l_whence = SEEK_SET, ++ .l_start = 0, ++ .l_len = 0, ++ .l_type = F_WRLCK, ++ }; ++ ++ fd = open("/dev/null", O_RDWR); ++ if (fd < 0) { ++ fprintf(stderr, ++ "Failed to open /dev/null for OFD lock probing: %s\n", ++ strerror(errno)); ++ fcntl_op_setlk = F_SETLK; ++ fcntl_op_getlk = F_GETLK; ++ return; ++ } ++ ret = fcntl(fd, F_OFD_GETLK, &fl); ++ close(fd); ++ if (!ret) { ++ fcntl_op_setlk = F_OFD_SETLK; ++ fcntl_op_getlk = F_OFD_GETLK; ++ } else { ++ fcntl_op_setlk = F_SETLK; ++ fcntl_op_getlk = F_GETLK; ++ } ++#else ++ fcntl_op_setlk = F_SETLK; ++ fcntl_op_getlk = F_GETLK; ++#endif ++ } ++} ++ ++bool qemu_has_ofd_lock(void) ++{ ++ qemu_probe_lock_ops(); ++#ifdef F_OFD_SETLK ++ return fcntl_op_setlk == F_OFD_SETLK; ++#else ++ return false; ++#endif ++} ++ ++static int qemu_lock_fcntl(int fd, int64_t start, int64_t len, int fl_type) ++{ ++ int ret; ++ struct flock fl = { ++ .l_whence = SEEK_SET, ++ .l_start = start, ++ .l_len = len, ++ .l_type = fl_type, ++ }; ++ qemu_probe_lock_ops(); ++ ret = RETRY_ON_EINTR(fcntl(fd, fcntl_op_setlk, &fl)); ++ return ret == -1 ? -errno : 0; ++} ++ ++int qemu_lock_fd(int fd, int64_t start, int64_t len, bool exclusive) ++{ ++ return qemu_lock_fcntl(fd, start, len, exclusive ? F_WRLCK : F_RDLCK); ++} ++ ++int qemu_unlock_fd(int fd, int64_t start, int64_t len) ++{ ++ return qemu_lock_fcntl(fd, start, len, F_UNLCK); ++} ++ ++int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive) ++{ ++ int ret; ++ struct flock fl = { ++ .l_whence = SEEK_SET, ++ .l_start = start, ++ .l_len = len, ++ .l_type = exclusive ? F_WRLCK : F_RDLCK, ++ }; ++ qemu_probe_lock_ops(); ++ ret = fcntl(fd, fcntl_op_getlk, &fl); ++ if (ret == -1) { ++ return -errno; ++ } else { ++ return fl.l_type == F_UNLCK ? 0 : -EAGAIN; ++ } ++} ++#endif ++ ++bool qemu_has_direct_io(void) ++{ ++#ifdef O_DIRECT ++ return true; ++#else ++ return false; ++#endif ++} ++ ++static int qemu_open_cloexec(const char *name, int flags, mode_t mode) ++{ ++ int ret; ++#ifdef O_CLOEXEC ++ ret = open(name, flags | O_CLOEXEC, mode); ++#else ++ ret = open(name, flags, mode); ++ if (ret >= 0) { ++ qemu_set_cloexec(ret); ++ } ++#endif ++ return ret; ++} ++ ++/* ++ * Opens a file with FD_CLOEXEC set ++ */ ++static int ++qemu_open_internal(const char *name, int flags, mode_t mode, Error **errp) ++{ ++ int ret; ++ ++#ifndef _WIN32 ++ const char *fdset_id_str; ++ ++ /* Attempt dup of fd from fd set */ ++ if (strstart(name, "/dev/fdset/", &fdset_id_str)) { ++ int64_t fdset_id; ++ ++ fdset_id = qemu_parse_fdset(fdset_id_str); ++ if (fdset_id == -1) { ++ error_setg(errp, "Could not parse fdset %s", name); ++ errno = EINVAL; ++ return -1; ++ } ++ ++ return monitor_fdset_dup_fd_add(fdset_id, flags, errp); ++ } ++#endif ++ ++ ret = qemu_open_cloexec(name, flags, mode); ++ ++ if (ret == -1) { ++ const char *action = flags & O_CREAT ? "create" : "open"; ++#ifdef O_DIRECT ++ /* Give more helpful error message for O_DIRECT */ ++ if (errno == EINVAL && (flags & O_DIRECT)) { ++ ret = open(name, flags & ~O_DIRECT, mode); ++ if (ret != -1) { ++ close(ret); ++ error_setg(errp, "Could not %s '%s': " ++ "filesystem does not support O_DIRECT", ++ action, name); ++ errno = EINVAL; /* restore first open()'s errno */ ++ return -1; ++ } ++ } ++#endif /* O_DIRECT */ ++ error_setg_errno(errp, errno, "Could not %s '%s'", ++ action, name); ++ } ++ ++ return ret; ++} ++ ++ ++int qemu_open(const char *name, int flags, Error **errp) ++{ ++ assert(!(flags & O_CREAT)); ++ ++ return qemu_open_internal(name, flags, 0, errp); ++} ++ ++ ++int qemu_create(const char *name, int flags, mode_t mode, Error **errp) ++{ ++ assert(!(flags & O_CREAT)); ++ ++ return qemu_open_internal(name, flags | O_CREAT, mode, errp); ++} ++ ++ ++int qemu_open_old(const char *name, int flags, ...) ++{ ++ va_list ap; ++ mode_t mode = 0; ++ int ret; ++ ++ va_start(ap, flags); ++ if (flags & O_CREAT) { ++ mode = va_arg(ap, int); ++ } ++ va_end(ap); ++ ++ ret = qemu_open_internal(name, flags, mode, NULL); ++ ++#ifdef O_DIRECT ++ if (ret == -1 && errno == EINVAL && (flags & O_DIRECT)) { ++ error_report("file system may not support O_DIRECT"); ++ errno = EINVAL; /* in case it was clobbered */ ++ } ++#endif /* O_DIRECT */ ++ ++ return ret; ++} ++ ++int qemu_close(int fd) ++{ ++ /* Close fd that was dup'd from an fdset */ ++ monitor_fdset_dup_fd_remove(fd); ++ return close(fd); ++} ++ ++/* ++ * Delete a file from the filesystem, unless the filename is /dev/fdset/... ++ * ++ * Returns: On success, zero is returned. On error, -1 is returned, ++ * and errno is set appropriately. ++ */ ++int qemu_unlink(const char *name) ++{ ++ if (g_str_has_prefix(name, "/dev/fdset/")) { ++ return 0; ++ } ++ ++ return unlink(name); ++} ++ ++/* ++ * A variant of write(2) which handles partial write. ++ * ++ * Return the number of bytes transferred. ++ * Set errno if fewer than `count' bytes are written. ++ * ++ * This function don't work with non-blocking fd's. ++ * Any of the possibilities with non-blocking fd's is bad: ++ * - return a short write (then name is wrong) ++ * - busy wait adding (errno == EAGAIN) to the loop ++ */ ++ssize_t qemu_write_full(int fd, const void *buf, size_t count) ++{ ++ ssize_t ret = 0; ++ ssize_t total = 0; ++ ++ while (count) { ++ ret = write(fd, buf, count); ++ if (ret < 0) { ++ if (errno == EINTR) ++ continue; ++ break; ++ } ++ ++ count -= ret; ++ buf += ret; ++ total += ret; ++ } ++ ++ return total; ++} ++ ++/* ++ * Opens a socket with FD_CLOEXEC set ++ */ ++int qemu_socket(int domain, int type, int protocol) ++{ ++ int ret; ++ ++#ifdef SOCK_CLOEXEC ++ ret = socket(domain, type | SOCK_CLOEXEC, protocol); ++ if (ret != -1 || errno != EINVAL) { ++ return ret; ++ } ++#endif ++ ret = socket(domain, type, protocol); ++ if (ret >= 0) { ++ qemu_set_cloexec(ret); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Accept a connection and set FD_CLOEXEC ++ */ ++int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen) ++{ ++ int ret; ++ ++#ifdef CONFIG_ACCEPT4 ++ ret = accept4(s, addr, addrlen, SOCK_CLOEXEC); ++ if (ret != -1 || errno != ENOSYS) { ++ return ret; ++ } ++#endif ++ ret = accept(s, addr, addrlen); ++ if (ret >= 0) { ++ qemu_set_cloexec(ret); ++ } ++ ++ return ret; ++} ++ ++ssize_t qemu_send_full(int s, const void *buf, size_t count) ++{ ++ ssize_t ret = 0; ++ ssize_t total = 0; ++ ++ while (count) { ++ ret = send(s, buf, count, 0); ++ if (ret < 0) { ++ if (errno == EINTR) { ++ continue; ++ } ++ break; ++ } ++ ++ count -= ret; ++ buf += ret; ++ total += ret; ++ } ++ ++ return total; ++} ++ ++void qemu_set_hw_version(const char *version) ++{ ++ hw_version = version; ++} ++ ++const char *qemu_hw_version(void) ++{ ++ return hw_version; ++} ++ ++#ifdef _WIN32 ++static void socket_cleanup(void) ++{ ++ WSACleanup(); ++} ++#endif ++ ++int socket_init(void) ++{ ++#ifdef _WIN32 ++ WSADATA Data; ++ int ret, err; ++ ++ ret = WSAStartup(MAKEWORD(2, 2), &Data); ++ if (ret != 0) { ++ err = WSAGetLastError(); ++ fprintf(stderr, "WSAStartup: %d\n", err); ++ return -1; ++ } ++ atexit(socket_cleanup); ++#endif ++ return 0; ++} ++ ++ ++#ifndef CONFIG_IOVEC ++static ssize_t ++readv_writev(int fd, const struct iovec *iov, int iov_cnt, bool do_write) ++{ ++ unsigned i = 0; ++ ssize_t ret = 0; ++ ssize_t off = 0; ++ while (i < iov_cnt) { ++ ssize_t r = do_write ++ ? write(fd, iov[i].iov_base + off, iov[i].iov_len - off) ++ : read(fd, iov[i].iov_base + off, iov[i].iov_len - off); ++ if (r > 0) { ++ ret += r; ++ off += r; ++ if (off < iov[i].iov_len) { ++ continue; ++ } ++ } else if (!r) { ++ break; ++ } else if (errno == EINTR) { ++ continue; ++ } else { ++ /* else it is some "other" error, ++ * only return if there was no data processed. */ ++ if (ret == 0) { ++ ret = -1; ++ } ++ break; ++ } ++ off = 0; ++ i++; ++ } ++ return ret; ++} ++ ++ssize_t ++readv(int fd, const struct iovec *iov, int iov_cnt) ++{ ++ return readv_writev(fd, iov, iov_cnt, false); ++} ++ ++ssize_t ++writev(int fd, const struct iovec *iov, int iov_cnt) ++{ ++ return readv_writev(fd, iov, iov_cnt, true); ++} ++#endif ++ ++/* ++ * Make sure data goes on disk, but if possible do not bother to ++ * write out the inode just for timestamp updates. ++ * ++ * Unfortunately even in 2009 many operating systems do not support ++ * fdatasync and have to fall back to fsync. ++ */ ++int qemu_fdatasync(int fd) ++{ ++#ifdef CONFIG_FDATASYNC ++ return fdatasync(fd); ++#else ++ return fsync(fd); ++#endif ++} +diff --git a/qcow2/lib/util/oslib-posix.c b/qcow2/lib/util/oslib-posix.c +new file mode 100644 +index 00000000..11b35e48 +--- /dev/null ++++ b/qcow2/lib/util/oslib-posix.c +@@ -0,0 +1,933 @@ ++/* ++ * os-posix-lib.c ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2010 Red Hat, Inc. ++ * ++ * QEMU library functions on POSIX which are shared between QEMU and ++ * the QEMU tools. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include ++ ++#include ++ ++#include "sysemu/sysemu.h" ++#include "trace.h" ++#include "qapi/error.h" ++#include "qemu/error-report.h" ++#include "qemu/madvise.h" ++#include "qemu/sockets.h" ++#include "qemu/thread.h" ++#include ++#include "qemu/cutils.h" ++#include "qemu/units.h" ++#include "qemu/thread-context.h" ++#include "qemu/main-loop.h" ++ ++#ifdef CONFIG_LINUX ++#include ++#endif ++ ++#ifdef __FreeBSD__ ++#include ++#include ++#include ++#endif ++ ++#ifdef __NetBSD__ ++#include ++#endif ++ ++#include "qemu/mmap-alloc.h" ++ ++#define MAX_MEM_PREALLOC_THREAD_COUNT 16 ++ ++struct MemsetThread; ++ ++static QLIST_HEAD(, MemsetContext) memset_contexts = ++ QLIST_HEAD_INITIALIZER(memset_contexts); ++ ++typedef struct MemsetContext { ++ bool all_threads_created; ++ bool any_thread_failed; ++ struct MemsetThread *threads; ++ int num_threads; ++ QLIST_ENTRY(MemsetContext) next; ++} MemsetContext; ++ ++struct MemsetThread { ++ char *addr; ++ size_t numpages; ++ size_t hpagesize; ++ QemuThread pgthread; ++ sigjmp_buf env; ++ MemsetContext *context; ++}; ++typedef struct MemsetThread MemsetThread; ++ ++/* used by sigbus_handler() */ ++static MemsetContext *sigbus_memset_context; ++struct sigaction sigbus_oldact; ++static QemuMutex sigbus_mutex; ++ ++static QemuMutex page_mutex; ++static QemuCond page_cond; ++ ++int qemu_get_thread_id(void) ++{ ++#if defined(__linux__) ++ return syscall(SYS_gettid); ++#elif defined(__FreeBSD__) ++ /* thread id is up to INT_MAX */ ++ long tid; ++ thr_self(&tid); ++ return (int)tid; ++#elif defined(__NetBSD__) ++ return _lwp_self(); ++#elif defined(__OpenBSD__) ++ return getthrid(); ++#else ++ return getpid(); ++#endif ++} ++ ++int qemu_daemon(int nochdir, int noclose) ++{ ++ return daemon(nochdir, noclose); ++} ++ ++bool qemu_write_pidfile(const char *path, Error **errp) ++{ ++ int fd; ++ char pidstr[32]; ++ ++ while (1) { ++ struct stat a, b; ++ struct flock lock = { ++ .l_type = F_WRLCK, ++ .l_whence = SEEK_SET, ++ .l_len = 0, ++ }; ++ ++ fd = qemu_create(path, O_WRONLY, S_IRUSR | S_IWUSR, errp); ++ if (fd == -1) { ++ return false; ++ } ++ ++ if (fstat(fd, &b) < 0) { ++ error_setg_errno(errp, errno, "Cannot stat file"); ++ goto fail_close; ++ } ++ ++ if (fcntl(fd, F_SETLK, &lock)) { ++ error_setg_errno(errp, errno, "Cannot lock pid file"); ++ goto fail_close; ++ } ++ ++ /* ++ * Now make sure the path we locked is the same one that now ++ * exists on the filesystem. ++ */ ++ if (stat(path, &a) < 0) { ++ /* ++ * PID file disappeared, someone else must be racing with ++ * us, so try again. ++ */ ++ close(fd); ++ continue; ++ } ++ ++ if (a.st_ino == b.st_ino) { ++ break; ++ } ++ ++ /* ++ * PID file was recreated, someone else must be racing with ++ * us, so try again. ++ */ ++ close(fd); ++ } ++ ++ if (ftruncate(fd, 0) < 0) { ++ error_setg_errno(errp, errno, "Failed to truncate pid file"); ++ goto fail_unlink; ++ } ++ ++ snprintf(pidstr, sizeof(pidstr), FMT_pid "\n", getpid()); ++ if (qemu_write_full(fd, pidstr, strlen(pidstr)) != strlen(pidstr)) { ++ error_setg(errp, "Failed to write pid file"); ++ goto fail_unlink; ++ } ++ ++ return true; ++ ++fail_unlink: ++ unlink(path); ++fail_close: ++ close(fd); ++ return false; ++} ++ ++/* alloc shared memory pages */ ++void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared, ++ bool noreserve) ++{ ++ const uint32_t qemu_map_flags = (shared ? QEMU_MAP_SHARED : 0) | ++ (noreserve ? QEMU_MAP_NORESERVE : 0); ++ size_t align = QEMU_VMALLOC_ALIGN; ++ void *ptr = qemu_ram_mmap(-1, size, align, qemu_map_flags, 0); ++ ++ if (ptr == MAP_FAILED) { ++ return NULL; ++ } ++ ++ if (alignment) { ++ *alignment = align; ++ } ++ ++ trace_qemu_anon_ram_alloc(size, ptr); ++ return ptr; ++} ++ ++void qemu_anon_ram_free(void *ptr, size_t size) ++{ ++ trace_qemu_anon_ram_free(ptr, size); ++ qemu_ram_munmap(-1, ptr, size); ++} ++ ++void qemu_socket_set_block(int fd) ++{ ++ g_unix_set_fd_nonblocking(fd, false, NULL); ++} ++ ++int qemu_socket_try_set_nonblock(int fd) ++{ ++ return g_unix_set_fd_nonblocking(fd, true, NULL) ? 0 : -errno; ++} ++ ++void qemu_socket_set_nonblock(int fd) ++{ ++ int f; ++ f = qemu_socket_try_set_nonblock(fd); ++ assert(f == 0); ++} ++ ++int socket_set_fast_reuse(int fd) ++{ ++ int val = 1, ret; ++ ++ ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, ++ (const char *)&val, sizeof(val)); ++ ++ assert(ret == 0); ++ ++ return ret; ++} ++ ++void qemu_set_cloexec(int fd) ++{ ++ int f; ++ f = fcntl(fd, F_GETFD); ++ assert(f != -1); ++ f = fcntl(fd, F_SETFD, f | FD_CLOEXEC); ++ assert(f != -1); ++} ++ ++int qemu_socketpair(int domain, int type, int protocol, int sv[2]) ++{ ++ int ret; ++ ++#ifdef SOCK_CLOEXEC ++ ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv); ++ if (ret != -1 || errno != EINVAL) { ++ return ret; ++ } ++#endif ++ ret = socketpair(domain, type, protocol, sv); ++ if (ret == 0) { ++ qemu_set_cloexec(sv[0]); ++ qemu_set_cloexec(sv[1]); ++ } ++ ++ return ret; ++} ++ ++char * ++qemu_get_local_state_dir(void) ++{ ++ return get_relocated_path(CONFIG_QEMU_LOCALSTATEDIR); ++} ++ ++void qemu_set_tty_echo(int fd, bool echo) ++{ ++ struct termios tty; ++ ++ tcgetattr(fd, &tty); ++ ++ if (echo) { ++ tty.c_lflag |= ECHO | ECHONL | ICANON | IEXTEN; ++ } else { ++ tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN); ++ } ++ ++ tcsetattr(fd, TCSANOW, &tty); ++} ++ ++#ifdef CONFIG_LINUX ++static void sigbus_handler(int signal, siginfo_t *siginfo, void *ctx) ++#else /* CONFIG_LINUX */ ++static void sigbus_handler(int signal) ++#endif /* CONFIG_LINUX */ ++{ ++ int i; ++ ++ if (sigbus_memset_context) { ++ for (i = 0; i < sigbus_memset_context->num_threads; i++) { ++ MemsetThread *thread = &sigbus_memset_context->threads[i]; ++ ++ if (qemu_thread_is_self(&thread->pgthread)) { ++ siglongjmp(thread->env, 1); ++ } ++ } ++ } ++ ++#ifdef CONFIG_LINUX ++ /* ++ * We assume that the MCE SIGBUS handler could have been registered. We ++ * should never receive BUS_MCEERR_AO on any of our threads, but only on ++ * the main thread registered for PR_MCE_KILL_EARLY. Further, we should not ++ * receive BUS_MCEERR_AR triggered by action of other threads on one of ++ * our threads. So, no need to check for unrelated SIGBUS when seeing one ++ * for our threads. ++ * ++ * We will forward to the MCE handler, which will either handle the SIGBUS ++ * or reinstall the default SIGBUS handler and reraise the SIGBUS. The ++ * default SIGBUS handler will crash the process, so we don't care. ++ */ ++ if (sigbus_oldact.sa_flags & SA_SIGINFO) { ++ sigbus_oldact.sa_sigaction(signal, siginfo, ctx); ++ return; ++ } ++#endif /* CONFIG_LINUX */ ++ warn_report("qemu_prealloc_mem: unrelated SIGBUS detected and ignored"); ++} ++ ++static void *do_touch_pages(void *arg) ++{ ++ MemsetThread *memset_args = (MemsetThread *)arg; ++ sigset_t set, oldset; ++ int ret = 0; ++ ++ /* ++ * On Linux, the page faults from the loop below can cause mmap_sem ++ * contention with allocation of the thread stacks. Do not start ++ * clearing until all threads have been created. ++ */ ++ qemu_mutex_lock(&page_mutex); ++ while (!memset_args->context->all_threads_created) { ++ qemu_cond_wait(&page_cond, &page_mutex); ++ } ++ qemu_mutex_unlock(&page_mutex); ++ ++ /* unblock SIGBUS */ ++ sigemptyset(&set); ++ sigaddset(&set, SIGBUS); ++ pthread_sigmask(SIG_UNBLOCK, &set, &oldset); ++ ++ if (sigsetjmp(memset_args->env, 1)) { ++ ret = -EFAULT; ++ } else { ++ char *addr = memset_args->addr; ++ size_t numpages = memset_args->numpages; ++ size_t hpagesize = memset_args->hpagesize; ++ size_t i; ++ for (i = 0; i < numpages; i++) { ++ /* ++ * Read & write back the same value, so we don't ++ * corrupt existing user/app data that might be ++ * stored. ++ * ++ * 'volatile' to stop compiler optimizing this away ++ * to a no-op ++ */ ++ *(volatile char *)addr = *addr; ++ addr += hpagesize; ++ } ++ } ++ pthread_sigmask(SIG_SETMASK, &oldset, NULL); ++ return (void *)(uintptr_t)ret; ++} ++ ++static void *do_madv_populate_write_pages(void *arg) ++{ ++ MemsetThread *memset_args = (MemsetThread *)arg; ++ const size_t size = memset_args->numpages * memset_args->hpagesize; ++ char * const addr = memset_args->addr; ++ int ret = 0; ++ ++ /* See do_touch_pages(). */ ++ qemu_mutex_lock(&page_mutex); ++ while (!memset_args->context->all_threads_created) { ++ qemu_cond_wait(&page_cond, &page_mutex); ++ } ++ qemu_mutex_unlock(&page_mutex); ++ ++ if (size && qemu_madvise(addr, size, QEMU_MADV_POPULATE_WRITE)) { ++ ret = -errno; ++ } ++ return (void *)(uintptr_t)ret; ++} ++ ++static inline int get_memset_num_threads(size_t hpagesize, size_t numpages, ++ int max_threads) ++{ ++ long host_procs = sysconf(_SC_NPROCESSORS_ONLN); ++ int ret = 1; ++ ++ if (host_procs > 0) { ++ ret = MIN(MIN(host_procs, MAX_MEM_PREALLOC_THREAD_COUNT), max_threads); ++ } ++ ++ /* Especially with gigantic pages, don't create more threads than pages. */ ++ ret = MIN(ret, numpages); ++ /* Don't start threads to prealloc comparatively little memory. */ ++ ret = MIN(ret, MAX(1, hpagesize * numpages / (64 * MiB))); ++ ++ /* In case sysconf() fails, we fall back to single threaded */ ++ return ret; ++} ++ ++static int wait_and_free_mem_prealloc_context(MemsetContext *context) ++{ ++ int i, ret = 0, tmp; ++ ++ for (i = 0; i < context->num_threads; i++) { ++ tmp = (uintptr_t)qemu_thread_join(&context->threads[i].pgthread); ++ ++ if (tmp) { ++ ret = tmp; ++ } ++ } ++ g_free(context->threads); ++ g_free(context); ++ return ret; ++} ++ ++static int touch_all_pages(char *area, size_t hpagesize, size_t numpages, ++ int max_threads, ThreadContext *tc, bool async, ++ bool use_madv_populate_write) ++{ ++ static gsize initialized = 0; ++ MemsetContext *context = g_malloc0(sizeof(MemsetContext)); ++ size_t numpages_per_thread, leftover; ++ void *(*touch_fn)(void *); ++ int ret, i = 0; ++ char *addr = area; ++ ++ /* ++ * Asynchronous preallocation is only allowed when using MADV_POPULATE_WRITE ++ * and prealloc context for thread placement. ++ */ ++ if (!use_madv_populate_write || !tc) { ++ async = false; ++ } ++ ++ context->num_threads = ++ get_memset_num_threads(hpagesize, numpages, max_threads); ++ ++ if (g_once_init_enter(&initialized)) { ++ qemu_mutex_init(&page_mutex); ++ qemu_cond_init(&page_cond); ++ g_once_init_leave(&initialized, 1); ++ } ++ ++ if (use_madv_populate_write) { ++ /* ++ * Avoid creating a single thread for MADV_POPULATE_WRITE when ++ * preallocating synchronously. ++ */ ++ if (context->num_threads == 1 && !async) { ++ ret = 0; ++ if (qemu_madvise(area, hpagesize * numpages, ++ QEMU_MADV_POPULATE_WRITE)) { ++ ret = -errno; ++ } ++ g_free(context); ++ return ret; ++ } ++ touch_fn = do_madv_populate_write_pages; ++ } else { ++ touch_fn = do_touch_pages; ++ } ++ ++ context->threads = g_new0(MemsetThread, context->num_threads); ++ numpages_per_thread = numpages / context->num_threads; ++ leftover = numpages % context->num_threads; ++ for (i = 0; i < context->num_threads; i++) { ++ context->threads[i].addr = addr; ++ context->threads[i].numpages = numpages_per_thread + (i < leftover); ++ context->threads[i].hpagesize = hpagesize; ++ context->threads[i].context = context; ++ if (tc) { ++ thread_context_create_thread(tc, &context->threads[i].pgthread, ++ "touch_pages", ++ touch_fn, &context->threads[i], ++ QEMU_THREAD_JOINABLE); ++ } else { ++ qemu_thread_create(&context->threads[i].pgthread, "touch_pages", ++ touch_fn, &context->threads[i], ++ QEMU_THREAD_JOINABLE); ++ } ++ addr += context->threads[i].numpages * hpagesize; ++ } ++ ++ if (async) { ++ /* ++ * async requests currently require the BQL. Add it to the list and kick ++ * preallocation off during qemu_finish_async_prealloc_mem(). ++ */ ++ assert(bql_locked()); ++ QLIST_INSERT_HEAD(&memset_contexts, context, next); ++ return 0; ++ } ++ ++ if (!use_madv_populate_write) { ++ sigbus_memset_context = context; ++ } ++ ++ qemu_mutex_lock(&page_mutex); ++ context->all_threads_created = true; ++ qemu_cond_broadcast(&page_cond); ++ qemu_mutex_unlock(&page_mutex); ++ ++ ret = wait_and_free_mem_prealloc_context(context); ++ ++ if (!use_madv_populate_write) { ++ sigbus_memset_context = NULL; ++ } ++ return ret; ++} ++ ++bool qemu_finish_async_prealloc_mem(Error **errp) ++{ ++ int ret = 0, tmp; ++ MemsetContext *context, *next_context; ++ ++ /* Waiting for preallocation requires the BQL. */ ++ assert(bql_locked()); ++ if (QLIST_EMPTY(&memset_contexts)) { ++ return true; ++ } ++ ++ qemu_mutex_lock(&page_mutex); ++ QLIST_FOREACH(context, &memset_contexts, next) { ++ context->all_threads_created = true; ++ } ++ qemu_cond_broadcast(&page_cond); ++ qemu_mutex_unlock(&page_mutex); ++ ++ QLIST_FOREACH_SAFE(context, &memset_contexts, next, next_context) { ++ QLIST_REMOVE(context, next); ++ tmp = wait_and_free_mem_prealloc_context(context); ++ if (tmp) { ++ ret = tmp; ++ } ++ } ++ ++ if (ret) { ++ error_setg_errno(errp, -ret, ++ "qemu_prealloc_mem: preallocating memory failed"); ++ return false; ++ } ++ return true; ++} ++ ++static bool madv_populate_write_possible(char *area, size_t pagesize) ++{ ++ return !qemu_madvise(area, pagesize, QEMU_MADV_POPULATE_WRITE) || ++ errno != EINVAL; ++} ++ ++bool qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, ++ ThreadContext *tc, bool async, Error **errp) ++{ ++ static gsize initialized; ++ int ret; ++ size_t hpagesize = qemu_fd_getpagesize(fd); ++ size_t numpages = DIV_ROUND_UP(sz, hpagesize); ++ bool use_madv_populate_write; ++ struct sigaction act; ++ bool rv = true; ++ ++ /* ++ * Sense on every invocation, as MADV_POPULATE_WRITE cannot be used for ++ * some special mappings, such as mapping /dev/mem. ++ */ ++ use_madv_populate_write = madv_populate_write_possible(area, hpagesize); ++ ++ if (!use_madv_populate_write) { ++ if (g_once_init_enter(&initialized)) { ++ qemu_mutex_init(&sigbus_mutex); ++ g_once_init_leave(&initialized, 1); ++ } ++ ++ qemu_mutex_lock(&sigbus_mutex); ++ memset(&act, 0, sizeof(act)); ++#ifdef CONFIG_LINUX ++ act.sa_sigaction = &sigbus_handler; ++ act.sa_flags = SA_SIGINFO; ++#else /* CONFIG_LINUX */ ++ act.sa_handler = &sigbus_handler; ++ act.sa_flags = 0; ++#endif /* CONFIG_LINUX */ ++ ++ ret = sigaction(SIGBUS, &act, &sigbus_oldact); ++ if (ret) { ++ qemu_mutex_unlock(&sigbus_mutex); ++ error_setg_errno(errp, errno, ++ "qemu_prealloc_mem: failed to install signal handler"); ++ return false; ++ } ++ } ++ ++ /* touch pages simultaneously */ ++ ret = touch_all_pages(area, hpagesize, numpages, max_threads, tc, async, ++ use_madv_populate_write); ++ if (ret) { ++ error_setg_errno(errp, -ret, ++ "qemu_prealloc_mem: preallocating memory failed"); ++ rv = false; ++ } ++ ++ if (!use_madv_populate_write) { ++ ret = sigaction(SIGBUS, &sigbus_oldact, NULL); ++ if (ret) { ++ /* Terminate QEMU since it can't recover from error */ ++ perror("qemu_prealloc_mem: failed to reinstall signal handler"); ++ exit(1); ++ } ++ qemu_mutex_unlock(&sigbus_mutex); ++ } ++ return rv; ++} ++ ++char *qemu_get_pid_name(pid_t pid) ++{ ++ char *name = NULL; ++ ++#if defined(__FreeBSD__) ++ /* BSDs don't have /proc, but they provide a nice substitute */ ++ struct kinfo_proc *proc = kinfo_getproc(pid); ++ ++ if (proc) { ++ name = g_strdup(proc->ki_comm); ++ free(proc); ++ } ++#else ++ /* Assume a system with reasonable procfs */ ++ char *pid_path; ++ size_t len; ++ ++ pid_path = g_strdup_printf("/proc/%d/cmdline", pid); ++ g_file_get_contents(pid_path, &name, &len, NULL); ++ g_free(pid_path); ++#endif ++ ++ return name; ++} ++ ++ ++void *qemu_alloc_stack(size_t *sz) ++{ ++ void *ptr; ++ int flags; ++#ifdef CONFIG_DEBUG_STACK_USAGE ++ void *ptr2; ++#endif ++ size_t pagesz = qemu_real_host_page_size(); ++#ifdef _SC_THREAD_STACK_MIN ++ /* avoid stacks smaller than _SC_THREAD_STACK_MIN */ ++ long min_stack_sz = sysconf(_SC_THREAD_STACK_MIN); ++ *sz = MAX(MAX(min_stack_sz, 0), *sz); ++#endif ++ /* adjust stack size to a multiple of the page size */ ++ *sz = ROUND_UP(*sz, pagesz); ++ /* allocate one extra page for the guard page */ ++ *sz += pagesz; ++ ++ flags = MAP_PRIVATE | MAP_ANONYMOUS; ++#if defined(MAP_STACK) && defined(__OpenBSD__) ++ /* Only enable MAP_STACK on OpenBSD. Other OS's such as ++ * Linux/FreeBSD/NetBSD have a flag with the same name ++ * but have differing functionality. OpenBSD will SEGV ++ * if it spots execution with a stack pointer pointing ++ * at memory that was not allocated with MAP_STACK. ++ */ ++ flags |= MAP_STACK; ++#endif ++ ++ ptr = mmap(NULL, *sz, PROT_READ | PROT_WRITE, flags, -1, 0); ++ if (ptr == MAP_FAILED) { ++ perror("failed to allocate memory for stack"); ++ abort(); ++ } ++ ++ /* Stack grows down -- guard page at the bottom. */ ++ if (mprotect(ptr, pagesz, PROT_NONE) != 0) { ++ perror("failed to set up stack guard page"); ++ abort(); ++ } ++ ++#ifdef CONFIG_DEBUG_STACK_USAGE ++ for (ptr2 = ptr + pagesz; ptr2 < ptr + *sz; ptr2 += sizeof(uint32_t)) { ++ *(uint32_t *)ptr2 = 0xdeadbeaf; ++ } ++#endif ++ ++ return ptr; ++} ++ ++#ifdef CONFIG_DEBUG_STACK_USAGE ++static __thread unsigned int max_stack_usage; ++#endif ++ ++void qemu_free_stack(void *stack, size_t sz) ++{ ++#ifdef CONFIG_DEBUG_STACK_USAGE ++ unsigned int usage; ++ void *ptr; ++ ++ for (ptr = stack + qemu_real_host_page_size(); ptr < stack + sz; ++ ptr += sizeof(uint32_t)) { ++ if (*(uint32_t *)ptr != 0xdeadbeaf) { ++ break; ++ } ++ } ++ usage = sz - (uintptr_t) (ptr - stack); ++ if (usage > max_stack_usage) { ++ error_report("thread %d max stack usage increased from %u to %u", ++ qemu_get_thread_id(), max_stack_usage, usage); ++ max_stack_usage = usage; ++ } ++#endif ++ ++ munmap(stack, sz); ++} ++ ++/* ++ * Disable CFI checks. ++ * We are going to call a signal handler directly. Such handler may or may not ++ * have been defined in our binary, so there's no guarantee that the pointer ++ * used to set the handler is a cfi-valid pointer. Since the handlers are ++ * stored in kernel memory, changing the handler to an attacker-defined ++ * function requires being able to call a sigaction() syscall, ++ * which is not as easy as overwriting a pointer in memory. ++ */ ++QEMU_DISABLE_CFI ++void sigaction_invoke(struct sigaction *action, ++ struct qemu_signalfd_siginfo *info) ++{ ++ siginfo_t si = {}; ++ si.si_signo = info->ssi_signo; ++ si.si_errno = info->ssi_errno; ++ si.si_code = info->ssi_code; ++ ++ /* Convert the minimal set of fields defined by POSIX. ++ * Positive si_code values are reserved for kernel-generated ++ * signals, where the valid siginfo fields are determined by ++ * the signal number. But according to POSIX, it is unspecified ++ * whether SI_USER and SI_QUEUE have values less than or equal to ++ * zero. ++ */ ++ if (info->ssi_code == SI_USER || info->ssi_code == SI_QUEUE || ++ info->ssi_code <= 0) { ++ /* SIGTERM, etc. */ ++ si.si_pid = info->ssi_pid; ++ si.si_uid = info->ssi_uid; ++ } else if (info->ssi_signo == SIGILL || info->ssi_signo == SIGFPE || ++ info->ssi_signo == SIGSEGV || info->ssi_signo == SIGBUS) { ++ si.si_addr = (void *)(uintptr_t)info->ssi_addr; ++ } else if (info->ssi_signo == SIGCHLD) { ++ si.si_pid = info->ssi_pid; ++ si.si_status = info->ssi_status; ++ si.si_uid = info->ssi_uid; ++ } ++ action->sa_sigaction(info->ssi_signo, &si, NULL); ++} ++ ++size_t qemu_get_host_physmem(void) ++{ ++#ifdef _SC_PHYS_PAGES ++ long pages = sysconf(_SC_PHYS_PAGES); ++ if (pages > 0) { ++ if (pages > SIZE_MAX / qemu_real_host_page_size()) { ++ return SIZE_MAX; ++ } else { ++ return pages * qemu_real_host_page_size(); ++ } ++ } ++#endif ++ return 0; ++} ++ ++int qemu_msync(void *addr, size_t length, int fd) ++{ ++ size_t align_mask = ~(qemu_real_host_page_size() - 1); ++ ++ /** ++ * There are no strict reqs as per the length of mapping ++ * to be synced. Still the length needs to follow the address ++ * alignment changes. Additionally - round the size to the multiple ++ * of PAGE_SIZE ++ */ ++ length += ((uintptr_t)addr & (qemu_real_host_page_size() - 1)); ++ length = (length + ~align_mask) & align_mask; ++ ++ addr = (void *)((uintptr_t)addr & align_mask); ++ ++ return msync(addr, length, MS_SYNC); ++} ++ ++static bool qemu_close_all_open_fd_proc(const int *skip, unsigned int nskip) ++{ ++ struct dirent *de; ++ int fd, dfd; ++ DIR *dir; ++ unsigned int skip_start = 0, skip_end = nskip; ++ ++ dir = opendir("/proc/self/fd"); ++ if (!dir) { ++ /* If /proc is not mounted, there is nothing that can be done. */ ++ return false; ++ } ++ /* Avoid closing the directory. */ ++ dfd = dirfd(dir); ++ ++ for (de = readdir(dir); de; de = readdir(dir)) { ++ bool close_fd = true; ++ ++ if (de->d_name[0] == '.') { ++ continue; ++ } ++ fd = atoi(de->d_name); ++ if (fd == dfd) { ++ continue; ++ } ++ ++ for (unsigned int i = skip_start; i < skip_end; i++) { ++ if (fd < skip[i]) { ++ /* We are below the next skipped fd, break */ ++ break; ++ } else if (fd == skip[i]) { ++ close_fd = false; ++ /* Restrict the range as we found fds matching start/end */ ++ if (i == skip_start) { ++ skip_start++; ++ } else if (i == skip_end) { ++ skip_end--; ++ } ++ break; ++ } ++ } ++ ++ if (close_fd) { ++ close(fd); ++ } ++ } ++ closedir(dir); ++ ++ return true; ++} ++ ++static bool qemu_close_all_open_fd_close_range(const int *skip, ++ unsigned int nskip, ++ int open_max) ++{ ++#ifdef CONFIG_CLOSE_RANGE ++ int max_fd = open_max - 1; ++ int first = 0, last; ++ unsigned int cur_skip = 0; ++ int ret; ++ ++ do { ++ /* Find the start boundary of the range to close */ ++ while (cur_skip < nskip && first == skip[cur_skip]) { ++ cur_skip++; ++ first++; ++ } ++ ++ /* Find the upper boundary of the range to close */ ++ last = max_fd; ++ if (cur_skip < nskip) { ++ last = skip[cur_skip] - 1; ++ last = MIN(last, max_fd); ++ } ++ ++ /* With the adjustments to the range, we might be done. */ ++ if (first > last) { ++ break; ++ } ++ ++ ret = close_range(first, last, 0); ++ if (ret < 0) { ++ return false; ++ } ++ ++ first = last + 1; ++ } while (last < max_fd); ++ ++ return true; ++#else ++ return false; ++#endif ++} ++ ++static void qemu_close_all_open_fd_fallback(const int *skip, unsigned int nskip, ++ int open_max) ++{ ++ unsigned int cur_skip = 0; ++ ++ /* Fallback */ ++ for (int i = 0; i < open_max; i++) { ++ if (cur_skip < nskip && i == skip[cur_skip]) { ++ cur_skip++; ++ continue; ++ } ++ close(i); ++ } ++} ++ ++/* ++ * Close all open file descriptors. ++ */ ++void qemu_close_all_open_fd(const int *skip, unsigned int nskip) ++{ ++ int open_max = sysconf(_SC_OPEN_MAX); ++ ++ assert(skip != NULL || nskip == 0); ++ ++ if (!qemu_close_all_open_fd_close_range(skip, nskip, open_max) && ++ !qemu_close_all_open_fd_proc(skip, nskip)) { ++ qemu_close_all_open_fd_fallback(skip, nskip, open_max); ++ } ++} +diff --git a/qcow2/lib/util/qemu-coroutine-lock.c b/qcow2/lib/util/qemu-coroutine-lock.c +new file mode 100644 +index 00000000..25344353 +--- /dev/null ++++ b/qcow2/lib/util/qemu-coroutine-lock.c +@@ -0,0 +1,469 @@ ++/* ++ * coroutine queues and locks ++ * ++ * Copyright (c) 2011 Kevin Wolf ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ * ++ * The lock-free mutex implementation is based on OSv ++ * (core/lfmutex.cc, include/lockfree/mutex.hh). ++ * Copyright (C) 2013 Cloudius Systems, Ltd. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/coroutine_int.h" ++#include "qemu/processor.h" ++#include "qemu/queue.h" ++#include "block/aio.h" ++#include "trace.h" ++ ++void qemu_co_queue_init(CoQueue *queue) ++{ ++ QSIMPLEQ_INIT(&queue->entries); ++} ++ ++void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock, ++ CoQueueWaitFlags flags) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ if (flags & CO_QUEUE_WAIT_FRONT) { ++ QSIMPLEQ_INSERT_HEAD(&queue->entries, self, co_queue_next); ++ } else { ++ QSIMPLEQ_INSERT_TAIL(&queue->entries, self, co_queue_next); ++ } ++ ++ if (lock) { ++ qemu_lockable_unlock(lock); ++ } ++ ++ /* There is no race condition here. Other threads will call ++ * aio_co_schedule on our AioContext, which can reenter this ++ * coroutine but only after this yield and after the main loop ++ * has gone through the next iteration. ++ */ ++ qemu_coroutine_yield(); ++ assert(qemu_in_coroutine()); ++ ++ /* TODO: OSv implements wait morphing here, where the wakeup ++ * primitive automatically places the woken coroutine on the ++ * mutex's queue. This avoids the thundering herd effect. ++ * This could be implemented for CoMutexes, but not really for ++ * other cases of QemuLockable. ++ */ ++ if (lock) { ++ qemu_lockable_lock(lock); ++ } ++} ++ ++bool qemu_co_enter_next_impl(CoQueue *queue, QemuLockable *lock) ++{ ++ Coroutine *next; ++ ++ next = QSIMPLEQ_FIRST(&queue->entries); ++ if (!next) { ++ return false; ++ } ++ ++ QSIMPLEQ_REMOVE_HEAD(&queue->entries, co_queue_next); ++ if (lock) { ++ qemu_lockable_unlock(lock); ++ } ++ aio_co_wake(next); ++ if (lock) { ++ qemu_lockable_lock(lock); ++ } ++ return true; ++} ++ ++bool coroutine_fn qemu_co_queue_next(CoQueue *queue) ++{ ++ /* No unlock/lock needed in coroutine context. */ ++ return qemu_co_enter_next_impl(queue, NULL); ++} ++ ++void qemu_co_enter_all_impl(CoQueue *queue, QemuLockable *lock) ++{ ++ while (qemu_co_enter_next_impl(queue, lock)) { ++ /* just loop */ ++ } ++} ++ ++void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue) ++{ ++ /* No unlock/lock needed in coroutine context. */ ++ qemu_co_enter_all_impl(queue, NULL); ++} ++ ++bool qemu_co_queue_empty(CoQueue *queue) ++{ ++ return QSIMPLEQ_FIRST(&queue->entries) == NULL; ++} ++ ++/* The wait records are handled with a multiple-producer, single-consumer ++ * lock-free queue. There cannot be two concurrent pop_waiter() calls ++ * because pop_waiter() can only be called while mutex->handoff is zero. ++ * This can happen in three cases: ++ * - in qemu_co_mutex_unlock, before the hand-off protocol has started. ++ * In this case, qemu_co_mutex_lock will see mutex->handoff == 0 and ++ * not take part in the handoff. ++ * - in qemu_co_mutex_lock, if it steals the hand-off responsibility from ++ * qemu_co_mutex_unlock. In this case, qemu_co_mutex_unlock will fail ++ * the cmpxchg (it will see either 0 or the next sequence value) and ++ * exit. The next hand-off cannot begin until qemu_co_mutex_lock has ++ * woken up someone. ++ * - in qemu_co_mutex_unlock, if it takes the hand-off token itself. ++ * In this case another iteration starts with mutex->handoff == 0; ++ * a concurrent qemu_co_mutex_lock will fail the cmpxchg, and ++ * qemu_co_mutex_unlock will go back to case (1). ++ * ++ * The following functions manage this queue. ++ */ ++typedef struct CoWaitRecord { ++ Coroutine *co; ++ QSLIST_ENTRY(CoWaitRecord) next; ++} CoWaitRecord; ++ ++static void coroutine_fn push_waiter(CoMutex *mutex, CoWaitRecord *w) ++{ ++ w->co = qemu_coroutine_self(); ++ QSLIST_INSERT_HEAD_ATOMIC(&mutex->from_push, w, next); ++} ++ ++static void move_waiters(CoMutex *mutex) ++{ ++ QSLIST_HEAD(, CoWaitRecord) reversed; ++ QSLIST_MOVE_ATOMIC(&reversed, &mutex->from_push); ++ while (!QSLIST_EMPTY(&reversed)) { ++ CoWaitRecord *w = QSLIST_FIRST(&reversed); ++ QSLIST_REMOVE_HEAD(&reversed, next); ++ QSLIST_INSERT_HEAD(&mutex->to_pop, w, next); ++ } ++} ++ ++static CoWaitRecord *pop_waiter(CoMutex *mutex) ++{ ++ CoWaitRecord *w; ++ ++ if (QSLIST_EMPTY(&mutex->to_pop)) { ++ move_waiters(mutex); ++ if (QSLIST_EMPTY(&mutex->to_pop)) { ++ return NULL; ++ } ++ } ++ w = QSLIST_FIRST(&mutex->to_pop); ++ QSLIST_REMOVE_HEAD(&mutex->to_pop, next); ++ return w; ++} ++ ++static bool has_waiters(CoMutex *mutex) ++{ ++ return QSLIST_EMPTY(&mutex->to_pop) || QSLIST_EMPTY(&mutex->from_push); ++} ++ ++void qemu_co_mutex_init(CoMutex *mutex) ++{ ++ memset(mutex, 0, sizeof(*mutex)); ++} ++ ++static void coroutine_fn qemu_co_mutex_wake(CoMutex *mutex, Coroutine *co) ++{ ++ /* Read co before co->ctx; pairs with smp_wmb() in ++ * qemu_coroutine_enter(). ++ */ ++ smp_read_barrier_depends(); ++ mutex->ctx = co->ctx; ++ aio_co_wake(co); ++} ++ ++static void coroutine_fn qemu_co_mutex_lock_slowpath(AioContext *ctx, ++ CoMutex *mutex) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ CoWaitRecord w; ++ unsigned old_handoff; ++ ++ trace_qemu_co_mutex_lock_entry(mutex, self); ++ push_waiter(mutex, &w); ++ ++ /* ++ * Add waiter before reading mutex->handoff. Pairs with qatomic_set_mb ++ * in qemu_co_mutex_unlock. ++ */ ++ smp_mb__after_rmw(); ++ ++ /* This is the "Responsibility Hand-Off" protocol; a lock() picks from ++ * a concurrent unlock() the responsibility of waking somebody up. ++ */ ++ old_handoff = qatomic_read(&mutex->handoff); ++ if (old_handoff && ++ has_waiters(mutex) && ++ qatomic_cmpxchg(&mutex->handoff, old_handoff, 0) == old_handoff) { ++ /* There can be no concurrent pops, because there can be only ++ * one active handoff at a time. ++ */ ++ CoWaitRecord *to_wake = pop_waiter(mutex); ++ Coroutine *co = to_wake->co; ++ if (co == self) { ++ /* We got the lock ourselves! */ ++ assert(to_wake == &w); ++ mutex->ctx = ctx; ++ return; ++ } ++ ++ qemu_co_mutex_wake(mutex, co); ++ } ++ ++ qemu_coroutine_yield(); ++ trace_qemu_co_mutex_lock_return(mutex, self); ++} ++ ++void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex) ++{ ++ AioContext *ctx = qemu_get_current_aio_context(); ++ Coroutine *self = qemu_coroutine_self(); ++ int waiters, i; ++ ++ /* Running a very small critical section on pthread_mutex_t and CoMutex ++ * shows that pthread_mutex_t is much faster because it doesn't actually ++ * go to sleep. What happens is that the critical section is shorter ++ * than the latency of entering the kernel and thus FUTEX_WAIT always ++ * fails. With CoMutex there is no such latency but you still want to ++ * avoid wait and wakeup. So introduce it artificially. ++ */ ++ i = 0; ++retry_fast_path: ++ waiters = qatomic_cmpxchg(&mutex->locked, 0, 1); ++ if (waiters != 0) { ++ while (waiters == 1 && ++i < 1000) { ++ if (qatomic_read(&mutex->ctx) == ctx) { ++ break; ++ } ++ if (qatomic_read(&mutex->locked) == 0) { ++ goto retry_fast_path; ++ } ++ cpu_relax(); ++ } ++ waiters = qatomic_fetch_inc(&mutex->locked); ++ } ++ ++ if (waiters == 0) { ++ /* Uncontended. */ ++ trace_qemu_co_mutex_lock_uncontended(mutex, self); ++ mutex->ctx = ctx; ++ } else { ++ qemu_co_mutex_lock_slowpath(ctx, mutex); ++ } ++ mutex->holder = self; ++ self->locks_held++; ++} ++ ++void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ ++ trace_qemu_co_mutex_unlock_entry(mutex, self); ++ ++ assert(mutex->locked); ++ assert(mutex->holder == self); ++ assert(qemu_in_coroutine()); ++ ++ mutex->ctx = NULL; ++ mutex->holder = NULL; ++ self->locks_held--; ++ if (qatomic_fetch_dec(&mutex->locked) == 1) { ++ /* No waiting qemu_co_mutex_lock(). Pfew, that was easy! */ ++ return; ++ } ++ ++ for (;;) { ++ CoWaitRecord *to_wake = pop_waiter(mutex); ++ unsigned our_handoff; ++ ++ if (to_wake) { ++ qemu_co_mutex_wake(mutex, to_wake->co); ++ break; ++ } ++ ++ /* Some concurrent lock() is in progress (we know this because ++ * mutex->locked was >1) but it hasn't yet put itself on the wait ++ * queue. Pick a sequence number for the handoff protocol (not 0). ++ */ ++ if (++mutex->sequence == 0) { ++ mutex->sequence = 1; ++ } ++ ++ our_handoff = mutex->sequence; ++ /* Set handoff before checking for waiters. */ ++ qatomic_set_mb(&mutex->handoff, our_handoff); ++ if (!has_waiters(mutex)) { ++ /* The concurrent lock has not added itself yet, so it ++ * will be able to pick our handoff. ++ */ ++ break; ++ } ++ ++ /* Try to do the handoff protocol ourselves; if somebody else has ++ * already taken it, however, we're done and they're responsible. ++ */ ++ if (qatomic_cmpxchg(&mutex->handoff, our_handoff, 0) != our_handoff) { ++ break; ++ } ++ } ++ ++ trace_qemu_co_mutex_unlock_return(mutex, self); ++} ++ ++struct CoRwTicket { ++ bool read; ++ Coroutine *co; ++ QSIMPLEQ_ENTRY(CoRwTicket) next; ++}; ++ ++void qemu_co_rwlock_init(CoRwlock *lock) ++{ ++ qemu_co_mutex_init(&lock->mutex); ++ lock->owners = 0; ++ QSIMPLEQ_INIT(&lock->tickets); ++} ++ ++/* Releases the internal CoMutex. */ ++static void coroutine_fn qemu_co_rwlock_maybe_wake_one(CoRwlock *lock) ++{ ++ CoRwTicket *tkt = QSIMPLEQ_FIRST(&lock->tickets); ++ Coroutine *co = NULL; ++ ++ /* ++ * Setting lock->owners here prevents rdlock and wrlock from ++ * sneaking in between unlock and wake. ++ */ ++ ++ if (tkt) { ++ if (tkt->read) { ++ if (lock->owners >= 0) { ++ lock->owners++; ++ co = tkt->co; ++ } ++ } else { ++ if (lock->owners == 0) { ++ lock->owners = -1; ++ co = tkt->co; ++ } ++ } ++ } ++ ++ if (co) { ++ QSIMPLEQ_REMOVE_HEAD(&lock->tickets, next); ++ qemu_co_mutex_unlock(&lock->mutex); ++ aio_co_wake(co); ++ } else { ++ qemu_co_mutex_unlock(&lock->mutex); ++ } ++} ++ ++void coroutine_fn qemu_co_rwlock_rdlock(CoRwlock *lock) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ ++ qemu_co_mutex_lock(&lock->mutex); ++ /* For fairness, wait if a writer is in line. */ ++ if (lock->owners == 0 || (lock->owners > 0 && QSIMPLEQ_EMPTY(&lock->tickets))) { ++ lock->owners++; ++ qemu_co_mutex_unlock(&lock->mutex); ++ } else { ++ CoRwTicket my_ticket = { true, self }; ++ ++ QSIMPLEQ_INSERT_TAIL(&lock->tickets, &my_ticket, next); ++ qemu_co_mutex_unlock(&lock->mutex); ++ qemu_coroutine_yield(); ++ assert(lock->owners >= 1); ++ ++ /* Possibly wake another reader, which will wake the next in line. */ ++ qemu_co_mutex_lock(&lock->mutex); ++ qemu_co_rwlock_maybe_wake_one(lock); ++ } ++ ++ self->locks_held++; ++} ++ ++void coroutine_fn qemu_co_rwlock_unlock(CoRwlock *lock) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ ++ assert(qemu_in_coroutine()); ++ self->locks_held--; ++ ++ qemu_co_mutex_lock(&lock->mutex); ++ if (lock->owners > 0) { ++ lock->owners--; ++ } else { ++ assert(lock->owners == -1); ++ lock->owners = 0; ++ } ++ ++ qemu_co_rwlock_maybe_wake_one(lock); ++} ++ ++void coroutine_fn qemu_co_rwlock_downgrade(CoRwlock *lock) ++{ ++ qemu_co_mutex_lock(&lock->mutex); ++ assert(lock->owners == -1); ++ lock->owners = 1; ++ ++ /* Possibly wake another reader, which will wake the next in line. */ ++ qemu_co_rwlock_maybe_wake_one(lock); ++} ++ ++void coroutine_fn qemu_co_rwlock_wrlock(CoRwlock *lock) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ ++ qemu_co_mutex_lock(&lock->mutex); ++ if (lock->owners == 0) { ++ lock->owners = -1; ++ qemu_co_mutex_unlock(&lock->mutex); ++ } else { ++ CoRwTicket my_ticket = { false, qemu_coroutine_self() }; ++ ++ QSIMPLEQ_INSERT_TAIL(&lock->tickets, &my_ticket, next); ++ qemu_co_mutex_unlock(&lock->mutex); ++ qemu_coroutine_yield(); ++ assert(lock->owners == -1); ++ } ++ ++ self->locks_held++; ++} ++ ++void coroutine_fn qemu_co_rwlock_upgrade(CoRwlock *lock) ++{ ++ qemu_co_mutex_lock(&lock->mutex); ++ assert(lock->owners > 0); ++ /* For fairness, wait if a writer is in line. */ ++ if (lock->owners == 1 && QSIMPLEQ_EMPTY(&lock->tickets)) { ++ lock->owners = -1; ++ qemu_co_mutex_unlock(&lock->mutex); ++ } else { ++ CoRwTicket my_ticket = { false, qemu_coroutine_self() }; ++ ++ lock->owners--; ++ QSIMPLEQ_INSERT_TAIL(&lock->tickets, &my_ticket, next); ++ qemu_co_rwlock_maybe_wake_one(lock); ++ qemu_coroutine_yield(); ++ assert(lock->owners == -1); ++ } ++} +diff --git a/qcow2/lib/util/qemu-coroutine.c b/qcow2/lib/util/qemu-coroutine.c +new file mode 100644 +index 00000000..eb4eebef +--- /dev/null ++++ b/qcow2/lib/util/qemu-coroutine.c +@@ -0,0 +1,401 @@ ++/* ++ * QEMU coroutines ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Stefan Hajnoczi ++ * Kevin Wolf ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "trace.h" ++#include "qemu/thread.h" ++#include "qemu/atomic.h" ++#include "qemu/coroutine_int.h" ++#include "qemu/coroutine-tls.h" ++#include "qemu/cutils.h" ++#include "block/aio.h" ++ ++enum { ++ COROUTINE_POOL_BATCH_MAX_SIZE = 128, ++}; ++ ++/* ++ * Coroutine creation and deletion is expensive so a pool of unused coroutines ++ * is kept as a cache. When the pool has coroutines available, they are ++ * recycled instead of creating new ones from scratch. Coroutines are added to ++ * the pool upon termination. ++ * ++ * The pool is global but each thread maintains a small local pool to avoid ++ * global pool contention. Threads fetch and return batches of coroutines from ++ * the global pool to maintain their local pool. The local pool holds up to two ++ * batches whereas the maximum size of the global pool is controlled by the ++ * qemu_coroutine_inc_pool_size() API. ++ * ++ * .-----------------------------------. ++ * | Batch 1 | Batch 2 | Batch 3 | ... | global_pool ++ * `-----------------------------------' ++ * ++ * .-------------------. ++ * | Batch 1 | Batch 2 | per-thread local_pool (maximum 2 batches) ++ * `-------------------' ++ */ ++typedef struct CoroutinePoolBatch { ++ /* Batches are kept in a list */ ++ QSLIST_ENTRY(CoroutinePoolBatch) next; ++ ++ /* This batch holds up to @COROUTINE_POOL_BATCH_MAX_SIZE coroutines */ ++ QSLIST_HEAD(, Coroutine) list; ++ unsigned int size; ++} CoroutinePoolBatch; ++ ++typedef QSLIST_HEAD(, CoroutinePoolBatch) CoroutinePool; ++ ++/* Host operating system limit on number of pooled coroutines */ ++static unsigned int global_pool_hard_max_size; ++ ++static QemuMutex global_pool_lock; /* protects the following variables */ ++static CoroutinePool global_pool = QSLIST_HEAD_INITIALIZER(global_pool); ++static unsigned int global_pool_size; ++static unsigned int global_pool_max_size = COROUTINE_POOL_BATCH_MAX_SIZE; ++ ++QEMU_DEFINE_STATIC_CO_TLS(CoroutinePool, local_pool); ++QEMU_DEFINE_STATIC_CO_TLS(Notifier, local_pool_cleanup_notifier); ++ ++static CoroutinePoolBatch *coroutine_pool_batch_new(void) ++{ ++ CoroutinePoolBatch *batch = g_new(CoroutinePoolBatch, 1); ++ ++ QSLIST_INIT(&batch->list); ++ batch->size = 0; ++ return batch; ++} ++ ++static void coroutine_pool_batch_delete(CoroutinePoolBatch *batch) ++{ ++ Coroutine *co; ++ Coroutine *tmp; ++ ++ QSLIST_FOREACH_SAFE(co, &batch->list, pool_next, tmp) { ++ QSLIST_REMOVE_HEAD(&batch->list, pool_next); ++ qemu_coroutine_delete(co); ++ } ++ g_free(batch); ++} ++ ++static void local_pool_cleanup(Notifier *n, void *value) ++{ ++ CoroutinePool *local_pool = get_ptr_local_pool(); ++ CoroutinePoolBatch *batch; ++ CoroutinePoolBatch *tmp; ++ ++ QSLIST_FOREACH_SAFE(batch, local_pool, next, tmp) { ++ QSLIST_REMOVE_HEAD(local_pool, next); ++ coroutine_pool_batch_delete(batch); ++ } ++} ++ ++/* Ensure the atexit notifier is registered */ ++static void local_pool_cleanup_init_once(void) ++{ ++ Notifier *notifier = get_ptr_local_pool_cleanup_notifier(); ++ if (!notifier->notify) { ++ notifier->notify = local_pool_cleanup; ++ qemu_thread_atexit_add(notifier); ++ } ++} ++ ++/* Helper to get the next unused coroutine from the local pool */ ++static Coroutine *coroutine_pool_get_local(void) ++{ ++ CoroutinePool *local_pool = get_ptr_local_pool(); ++ CoroutinePoolBatch *batch = QSLIST_FIRST(local_pool); ++ Coroutine *co; ++ ++ if (unlikely(!batch)) { ++ return NULL; ++ } ++ ++ co = QSLIST_FIRST(&batch->list); ++ QSLIST_REMOVE_HEAD(&batch->list, pool_next); ++ batch->size--; ++ ++ if (batch->size == 0) { ++ QSLIST_REMOVE_HEAD(local_pool, next); ++ coroutine_pool_batch_delete(batch); ++ } ++ return co; ++} ++ ++/* Get the next batch from the global pool */ ++static void coroutine_pool_refill_local(void) ++{ ++ CoroutinePool *local_pool = get_ptr_local_pool(); ++ CoroutinePoolBatch *batch; ++ ++ WITH_QEMU_LOCK_GUARD(&global_pool_lock) { ++ batch = QSLIST_FIRST(&global_pool); ++ ++ if (batch) { ++ QSLIST_REMOVE_HEAD(&global_pool, next); ++ global_pool_size -= batch->size; ++ } ++ } ++ ++ if (batch) { ++ QSLIST_INSERT_HEAD(local_pool, batch, next); ++ local_pool_cleanup_init_once(); ++ } ++} ++ ++/* Add a batch of coroutines to the global pool */ ++static void coroutine_pool_put_global(CoroutinePoolBatch *batch) ++{ ++ WITH_QEMU_LOCK_GUARD(&global_pool_lock) { ++ unsigned int max = MIN(global_pool_max_size, ++ global_pool_hard_max_size); ++ ++ if (global_pool_size < max) { ++ QSLIST_INSERT_HEAD(&global_pool, batch, next); ++ ++ /* Overshooting the max pool size is allowed */ ++ global_pool_size += batch->size; ++ return; ++ } ++ } ++ ++ /* The global pool was full, so throw away this batch */ ++ coroutine_pool_batch_delete(batch); ++} ++ ++/* Get the next unused coroutine from the pool or return NULL */ ++static Coroutine *coroutine_pool_get(void) ++{ ++ Coroutine *co; ++ ++ co = coroutine_pool_get_local(); ++ if (!co) { ++ coroutine_pool_refill_local(); ++ co = coroutine_pool_get_local(); ++ } ++ return co; ++} ++ ++static void coroutine_pool_put(Coroutine *co) ++{ ++ CoroutinePool *local_pool = get_ptr_local_pool(); ++ CoroutinePoolBatch *batch = QSLIST_FIRST(local_pool); ++ ++ if (unlikely(!batch)) { ++ batch = coroutine_pool_batch_new(); ++ QSLIST_INSERT_HEAD(local_pool, batch, next); ++ local_pool_cleanup_init_once(); ++ } ++ ++ if (unlikely(batch->size >= COROUTINE_POOL_BATCH_MAX_SIZE)) { ++ CoroutinePoolBatch *next = QSLIST_NEXT(batch, next); ++ ++ /* Is the local pool full? */ ++ if (next) { ++ QSLIST_REMOVE_HEAD(local_pool, next); ++ coroutine_pool_put_global(batch); ++ } ++ ++ batch = coroutine_pool_batch_new(); ++ QSLIST_INSERT_HEAD(local_pool, batch, next); ++ } ++ ++ QSLIST_INSERT_HEAD(&batch->list, co, pool_next); ++ batch->size++; ++} ++ ++Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque) ++{ ++ Coroutine *co = NULL; ++ ++ if (IS_ENABLED(CONFIG_COROUTINE_POOL)) { ++ co = coroutine_pool_get(); ++ } ++ ++ if (!co) { ++ co = qemu_coroutine_new(); ++ } ++ ++ co->entry = entry; ++ co->entry_arg = opaque; ++ QSIMPLEQ_INIT(&co->co_queue_wakeup); ++ return co; ++} ++ ++static void coroutine_delete(Coroutine *co) ++{ ++ co->caller = NULL; ++ ++ if (IS_ENABLED(CONFIG_COROUTINE_POOL)) { ++ coroutine_pool_put(co); ++ } else { ++ qemu_coroutine_delete(co); ++ } ++} ++ ++void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co) ++{ ++ QSIMPLEQ_HEAD(, Coroutine) pending = QSIMPLEQ_HEAD_INITIALIZER(pending); ++ Coroutine *from = qemu_coroutine_self(); ++ ++ QSIMPLEQ_INSERT_TAIL(&pending, co, co_queue_next); ++ ++ /* Run co and any queued coroutines */ ++ while (!QSIMPLEQ_EMPTY(&pending)) { ++ Coroutine *to = QSIMPLEQ_FIRST(&pending); ++ CoroutineAction ret; ++ ++ /* ++ * Read to before to->scheduled; pairs with qatomic_cmpxchg in ++ * qemu_co_sleep(), aio_co_schedule() etc. ++ */ ++ smp_read_barrier_depends(); ++ ++ const char *scheduled = qatomic_read(&to->scheduled); ++ ++ QSIMPLEQ_REMOVE_HEAD(&pending, co_queue_next); ++ ++ trace_qemu_aio_coroutine_enter(ctx, from, to, to->entry_arg); ++ ++ /* if the Coroutine has already been scheduled, entering it again will ++ * cause us to enter it twice, potentially even after the coroutine has ++ * been deleted */ ++ if (scheduled) { ++ fprintf(stderr, ++ "%s: Co-routine was already scheduled in '%s'\n", ++ __func__, scheduled); ++ abort(); ++ } ++ ++ if (to->caller) { ++ fprintf(stderr, "Co-routine re-entered recursively\n"); ++ abort(); ++ } ++ ++ to->caller = from; ++ to->ctx = ctx; ++ ++ /* Store to->ctx before anything that stores to. Matches ++ * barrier in aio_co_wake and qemu_co_mutex_wake. ++ */ ++ smp_wmb(); ++ ++ ret = qemu_coroutine_switch(from, to, COROUTINE_ENTER); ++ ++ /* Queued coroutines are run depth-first; previously pending coroutines ++ * run after those queued more recently. ++ */ ++ QSIMPLEQ_PREPEND(&pending, &to->co_queue_wakeup); ++ ++ switch (ret) { ++ case COROUTINE_YIELD: ++ break; ++ case COROUTINE_TERMINATE: ++ assert(!to->locks_held); ++ trace_qemu_coroutine_terminate(to); ++ coroutine_delete(to); ++ break; ++ default: ++ abort(); ++ } ++ } ++} ++ ++void qemu_coroutine_enter(Coroutine *co) ++{ ++ qemu_aio_coroutine_enter(qemu_get_current_aio_context(), co); ++} ++ ++void qemu_coroutine_enter_if_inactive(Coroutine *co) ++{ ++ if (!qemu_coroutine_entered(co)) { ++ qemu_coroutine_enter(co); ++ } ++} ++ ++void coroutine_fn qemu_coroutine_yield(void) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ Coroutine *to = self->caller; ++ ++ trace_qemu_coroutine_yield(self, to); ++ ++ if (!to) { ++ fprintf(stderr, "Co-routine is yielding to no one\n"); ++ abort(); ++ } ++ ++ self->caller = NULL; ++ qemu_coroutine_switch(self, to, COROUTINE_YIELD); ++} ++ ++bool qemu_coroutine_entered(Coroutine *co) ++{ ++ return co->caller; ++} ++ ++AioContext *qemu_coroutine_get_aio_context(Coroutine *co) ++{ ++ return co->ctx; ++} ++ ++void qemu_coroutine_inc_pool_size(unsigned int additional_pool_size) ++{ ++ QEMU_LOCK_GUARD(&global_pool_lock); ++ global_pool_max_size += additional_pool_size; ++} ++ ++void qemu_coroutine_dec_pool_size(unsigned int removing_pool_size) ++{ ++ QEMU_LOCK_GUARD(&global_pool_lock); ++ global_pool_max_size -= removing_pool_size; ++} ++ ++static unsigned int get_global_pool_hard_max_size(void) ++{ ++#ifdef __linux__ ++ g_autofree char *contents = NULL; ++ int max_map_count; ++ ++ /* ++ * Linux processes can have up to max_map_count virtual memory areas ++ * (VMAs). mmap(2), mprotect(2), etc fail with ENOMEM beyond this limit. We ++ * must limit the coroutine pool to a safe size to avoid running out of ++ * VMAs. ++ */ ++ if (g_file_get_contents("/proc/sys/vm/max_map_count", &contents, NULL, ++ NULL) && ++ qemu_strtoi(contents, NULL, 10, &max_map_count) == 0) { ++ /* ++ * This is an upper bound that avoids exceeding max_map_count. Leave a ++ * fixed amount for non-coroutine users like library dependencies, ++ * vhost-user, etc. Each coroutine takes up 2 VMAs so halve the ++ * remaining amount. ++ */ ++ if (max_map_count > 5000) { ++ return (max_map_count - 5000) / 2; ++ } else { ++ /* Disable the global pool but threads still have local pools */ ++ return 0; ++ } ++ } ++#endif ++ ++ return UINT_MAX; ++} ++ ++static void __attribute__((constructor)) qemu_coroutine_init(void) ++{ ++ qemu_mutex_init(&global_pool_lock); ++ global_pool_hard_max_size = get_global_pool_hard_max_size(); ++} +diff --git a/qcow2/lib/util/qemu-option.c b/qcow2/lib/util/qemu-option.c +new file mode 100644 +index 00000000..201f7a87 +--- /dev/null ++++ b/qcow2/lib/util/qemu-option.c +@@ -0,0 +1,1226 @@ ++/* ++ * Commandline option parsing functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2009 Kevin Wolf ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "qapi/error.h" ++#include "qemu/error-report.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qapi/qmp/qerror.h" ++#include "qemu/option_int.h" ++#include "qemu/cutils.h" ++#include "qemu/id.h" ++#include "qemu/help_option.h" ++ ++/* ++ * Extracts the name of an option from the parameter string (@p points at the ++ * first byte of the option name) ++ * ++ * The option name is @len characters long and is copied into @option. The ++ * caller is responsible for free'ing @option when no longer required. ++ * ++ * The return value is the position of the delimiter/zero byte after the option ++ * name in @p. ++ */ ++static const char *get_opt_name(const char *p, char **option, size_t len) ++{ ++ *option = g_strndup(p, len); ++ return p + len; ++} ++ ++/* ++ * Extracts the value of an option from the parameter string p (p points at the ++ * first byte of the option value) ++ * ++ * This function is comparable to get_opt_name with the difference that the ++ * delimiter is fixed to be comma which starts a new option. To specify an ++ * option value that contains commas, double each comma. ++ */ ++const char *get_opt_value(const char *p, char **value) ++{ ++ size_t capacity = 0, length; ++ const char *offset; ++ ++ *value = NULL; ++ while (1) { ++ offset = qemu_strchrnul(p, ','); ++ length = offset - p; ++ if (*offset != '\0' && *(offset + 1) == ',') { ++ length++; ++ } ++ *value = g_renew(char, *value, capacity + length + 1); ++ strncpy(*value + capacity, p, length); ++ (*value)[capacity + length] = '\0'; ++ capacity += length; ++ if (*offset == '\0' || ++ *(offset + 1) != ',') { ++ break; ++ } ++ ++ p += (offset - p) + 2; ++ } ++ ++ return offset; ++} ++ ++static bool parse_option_number(const char *name, const char *value, ++ uint64_t *ret, Error **errp) ++{ ++ uint64_t number; ++ int err; ++ ++ err = qemu_strtou64(value, NULL, 0, &number); ++ if (err == -ERANGE) { ++ error_setg(errp, "Value '%s' is too large for parameter '%s'", ++ value, name); ++ return false; ++ } ++ if (err) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number"); ++ return false; ++ } ++ *ret = number; ++ return true; ++} ++ ++static const QemuOptDesc *find_desc_by_name(const QemuOptDesc *desc, ++ const char *name) ++{ ++ int i; ++ ++ for (i = 0; desc[i].name != NULL; i++) { ++ if (strcmp(desc[i].name, name) == 0) { ++ return &desc[i]; ++ } ++ } ++ ++ return NULL; ++} ++ ++static const char *find_default_by_name(QemuOpts *opts, const char *name) ++{ ++ const QemuOptDesc *desc = find_desc_by_name(opts->list->desc, name); ++ ++ return desc ? desc->def_value_str : NULL; ++} ++ ++bool parse_option_size(const char *name, const char *value, ++ uint64_t *ret, Error **errp) ++{ ++ uint64_t size; ++ int err; ++ ++ err = qemu_strtosz(value, NULL, &size); ++ if (err == -ERANGE) { ++ error_setg(errp, "Value '%s' is out of range for parameter '%s'", ++ value, name); ++ return false; ++ } ++ if (err) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, ++ "a non-negative number below 2^64"); ++ error_append_hint(errp, "Optional suffix k, M, G, T, P or E means" ++ " kilo-, mega-, giga-, tera-, peta-\n" ++ "and exabytes, respectively.\n"); ++ return false; ++ } ++ *ret = size; ++ return true; ++} ++ ++static const char *opt_type_to_string(enum QemuOptType type) ++{ ++ switch (type) { ++ case QEMU_OPT_STRING: ++ return "str"; ++ case QEMU_OPT_BOOL: ++ return "bool (on/off)"; ++ case QEMU_OPT_NUMBER: ++ return "num"; ++ case QEMU_OPT_SIZE: ++ return "size"; ++ } ++ ++ g_assert_not_reached(); ++} ++ ++/** ++ * Print the list of options available in the given list. If ++ * @print_caption is true, a caption (including the list name, if it ++ * exists) is printed. The options itself will be indented, so ++ * @print_caption should only be set to false if the caller prints its ++ * own custom caption (so that the indentation makes sense). ++ */ ++void qemu_opts_print_help(QemuOptsList *list, bool print_caption) ++{ ++ QemuOptDesc *desc; ++ int i; ++ GPtrArray *array = g_ptr_array_new(); ++ ++ assert(list); ++ desc = list->desc; ++ while (desc && desc->name) { ++ GString *str = g_string_new(NULL); ++ g_string_append_printf(str, " %s=<%s>", desc->name, ++ opt_type_to_string(desc->type)); ++ if (desc->help) { ++ if (str->len < 24) { ++ g_string_append_printf(str, "%*s", 24 - (int)str->len, ""); ++ } ++ g_string_append_printf(str, " - %s", desc->help); ++ } ++ g_ptr_array_add(array, g_string_free(str, false)); ++ desc++; ++ } ++ ++ g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); ++ if (print_caption && array->len > 0) { ++ if (list->name) { ++ printf("%s options:\n", list->name); ++ } else { ++ printf("Options:\n"); ++ } ++ } else if (array->len == 0) { ++ if (list->name) { ++ printf("There are no options for %s.\n", list->name); ++ } else { ++ printf("No options available.\n"); ++ } ++ } ++ for (i = 0; i < array->len; i++) { ++ printf("%s\n", (char *)array->pdata[i]); ++ } ++ g_ptr_array_set_free_func(array, g_free); ++ g_ptr_array_free(array, true); ++ ++} ++/* ------------------------------------------------------------------ */ ++ ++QemuOpt *qemu_opt_find(QemuOpts *opts, const char *name) ++{ ++ QemuOpt *opt; ++ ++ QTAILQ_FOREACH_REVERSE(opt, &opts->head, next) { ++ if (strcmp(opt->name, name) != 0) ++ continue; ++ return opt; ++ } ++ return NULL; ++} ++ ++static void qemu_opt_del(QemuOpt *opt) ++{ ++ QTAILQ_REMOVE(&opt->opts->head, opt, next); ++ g_free(opt->name); ++ g_free(opt->str); ++ g_free(opt); ++} ++ ++/* qemu_opt_set allows many settings for the same option. ++ * This function deletes all settings for an option. ++ */ ++static void qemu_opt_del_all(QemuOpts *opts, const char *name) ++{ ++ QemuOpt *opt, *next_opt; ++ ++ QTAILQ_FOREACH_SAFE(opt, &opts->head, next, next_opt) { ++ if (!strcmp(opt->name, name)) { ++ qemu_opt_del(opt); ++ } ++ } ++} ++ ++const char *qemu_opt_get(QemuOpts *opts, const char *name) ++{ ++ QemuOpt *opt; ++ ++ if (opts == NULL) { ++ return NULL; ++ } ++ ++ opt = qemu_opt_find(opts, name); ++ if (!opt) { ++ return find_default_by_name(opts, name); ++ } ++ ++ return opt->str; ++} ++ ++void qemu_opt_iter_init(QemuOptsIter *iter, QemuOpts *opts, const char *name) ++{ ++ iter->opts = opts; ++ iter->opt = QTAILQ_FIRST(&opts->head); ++ iter->name = name; ++} ++ ++const char *qemu_opt_iter_next(QemuOptsIter *iter) ++{ ++ QemuOpt *ret = iter->opt; ++ if (iter->name) { ++ while (ret && !g_str_equal(iter->name, ret->name)) { ++ ret = QTAILQ_NEXT(ret, next); ++ } ++ } ++ iter->opt = ret ? QTAILQ_NEXT(ret, next) : NULL; ++ return ret ? ret->str : NULL; ++} ++ ++/* Get a known option (or its default) and remove it from the list ++ * all in one action. Return a malloced string of the option value. ++ * Result must be freed by caller with g_free(). ++ */ ++char *qemu_opt_get_del(QemuOpts *opts, const char *name) ++{ ++ QemuOpt *opt; ++ char *str; ++ ++ if (opts == NULL) { ++ return NULL; ++ } ++ ++ opt = qemu_opt_find(opts, name); ++ if (!opt) { ++ return g_strdup(find_default_by_name(opts, name)); ++ } ++ str = opt->str; ++ opt->str = NULL; ++ qemu_opt_del_all(opts, name); ++ return str; ++} ++ ++bool qemu_opt_has_help_opt(QemuOpts *opts) ++{ ++ QemuOpt *opt; ++ ++ QTAILQ_FOREACH_REVERSE(opt, &opts->head, next) { ++ if (is_help_option(opt->name)) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++static bool qemu_opt_get_bool_helper(QemuOpts *opts, const char *name, ++ bool defval, bool del) ++{ ++ QemuOpt *opt; ++ const char *def_val; ++ bool ret = defval; ++ ++ if (opts == NULL) { ++ return ret; ++ } ++ ++ opt = qemu_opt_find(opts, name); ++ if (opt == NULL) { ++ def_val = find_default_by_name(opts, name); ++ if (def_val) { ++ qapi_bool_parse(name, def_val, &ret, &error_abort); ++ } ++ return ret; ++ } ++ assert(opt->desc && opt->desc->type == QEMU_OPT_BOOL); ++ ret = opt->value.boolean; ++ if (del) { ++ qemu_opt_del_all(opts, name); ++ } ++ return ret; ++} ++ ++bool qemu_opt_get_bool(QemuOpts *opts, const char *name, bool defval) ++{ ++ return qemu_opt_get_bool_helper(opts, name, defval, false); ++} ++ ++bool qemu_opt_get_bool_del(QemuOpts *opts, const char *name, bool defval) ++{ ++ return qemu_opt_get_bool_helper(opts, name, defval, true); ++} ++ ++static uint64_t qemu_opt_get_number_helper(QemuOpts *opts, const char *name, ++ uint64_t defval, bool del) ++{ ++ QemuOpt *opt; ++ const char *def_val; ++ uint64_t ret = defval; ++ ++ if (opts == NULL) { ++ return ret; ++ } ++ ++ opt = qemu_opt_find(opts, name); ++ if (opt == NULL) { ++ def_val = find_default_by_name(opts, name); ++ if (def_val) { ++ parse_option_number(name, def_val, &ret, &error_abort); ++ } ++ return ret; ++ } ++ assert(opt->desc && opt->desc->type == QEMU_OPT_NUMBER); ++ ret = opt->value.uint; ++ if (del) { ++ qemu_opt_del_all(opts, name); ++ } ++ return ret; ++} ++ ++uint64_t qemu_opt_get_number(QemuOpts *opts, const char *name, uint64_t defval) ++{ ++ return qemu_opt_get_number_helper(opts, name, defval, false); ++} ++ ++uint64_t qemu_opt_get_number_del(QemuOpts *opts, const char *name, ++ uint64_t defval) ++{ ++ return qemu_opt_get_number_helper(opts, name, defval, true); ++} ++ ++static uint64_t qemu_opt_get_size_helper(QemuOpts *opts, const char *name, ++ uint64_t defval, bool del) ++{ ++ QemuOpt *opt; ++ const char *def_val; ++ uint64_t ret = defval; ++ ++ if (opts == NULL) { ++ return ret; ++ } ++ ++ opt = qemu_opt_find(opts, name); ++ if (opt == NULL) { ++ def_val = find_default_by_name(opts, name); ++ if (def_val) { ++ parse_option_size(name, def_val, &ret, &error_abort); ++ } ++ return ret; ++ } ++ assert(opt->desc && opt->desc->type == QEMU_OPT_SIZE); ++ ret = opt->value.uint; ++ if (del) { ++ qemu_opt_del_all(opts, name); ++ } ++ return ret; ++} ++ ++uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval) ++{ ++ return qemu_opt_get_size_helper(opts, name, defval, false); ++} ++ ++uint64_t qemu_opt_get_size_del(QemuOpts *opts, const char *name, ++ uint64_t defval) ++{ ++ return qemu_opt_get_size_helper(opts, name, defval, true); ++} ++ ++static bool qemu_opt_parse(QemuOpt *opt, Error **errp) ++{ ++ if (opt->desc == NULL) ++ return true; ++ ++ switch (opt->desc->type) { ++ case QEMU_OPT_STRING: ++ /* nothing */ ++ return true; ++ case QEMU_OPT_BOOL: ++ return qapi_bool_parse(opt->name, opt->str, &opt->value.boolean, errp); ++ case QEMU_OPT_NUMBER: ++ return parse_option_number(opt->name, opt->str, &opt->value.uint, ++ errp); ++ case QEMU_OPT_SIZE: ++ return parse_option_size(opt->name, opt->str, &opt->value.uint, ++ errp); ++ default: ++ abort(); ++ } ++} ++ ++static bool opts_accepts_any(const QemuOptsList *list) ++{ ++ return list->desc[0].name == NULL; ++} ++ ++int qemu_opt_unset(QemuOpts *opts, const char *name) ++{ ++ QemuOpt *opt = qemu_opt_find(opts, name); ++ ++ assert(opts_accepts_any(opts->list)); ++ ++ if (opt == NULL) { ++ return -1; ++ } else { ++ qemu_opt_del(opt); ++ return 0; ++ } ++} ++ ++static QemuOpt *opt_create(QemuOpts *opts, const char *name, char *value) ++{ ++ QemuOpt *opt = g_malloc0(sizeof(*opt)); ++ ++ opt->name = g_strdup(name); ++ opt->str = value; ++ opt->opts = opts; ++ QTAILQ_INSERT_TAIL(&opts->head, opt, next); ++ ++ return opt; ++} ++ ++static bool opt_validate(QemuOpt *opt, Error **errp) ++{ ++ const QemuOptDesc *desc; ++ const QemuOptsList *list = opt->opts->list; ++ ++ desc = find_desc_by_name(list->desc, opt->name); ++ if (!desc && !opts_accepts_any(list)) { ++ error_setg(errp, "Invalid parameter '%s'", opt->name); ++ return false; ++ } ++ ++ opt->desc = desc; ++ if (!qemu_opt_parse(opt, errp)) { ++ return false; ++ } ++ ++ return true; ++} ++ ++bool qemu_opt_set(QemuOpts *opts, const char *name, const char *value, ++ Error **errp) ++{ ++ QemuOpt *opt = opt_create(opts, name, g_strdup(value)); ++ ++ if (!opt_validate(opt, errp)) { ++ qemu_opt_del(opt); ++ return false; ++ } ++ return true; ++} ++ ++bool qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val, ++ Error **errp) ++{ ++ QemuOpt *opt; ++ const QemuOptDesc *desc; ++ const QemuOptsList *list = opts->list; ++ ++ desc = find_desc_by_name(list->desc, name); ++ if (!desc && !opts_accepts_any(list)) { ++ error_setg(errp, "Invalid parameter '%s'", name); ++ return false; ++ } ++ ++ opt = g_malloc0(sizeof(*opt)); ++ opt->name = g_strdup(name); ++ opt->opts = opts; ++ opt->desc = desc; ++ opt->value.boolean = !!val; ++ opt->str = g_strdup(val ? "on" : "off"); ++ QTAILQ_INSERT_TAIL(&opts->head, opt, next); ++ return true; ++} ++ ++bool qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val, ++ Error **errp) ++{ ++ QemuOpt *opt; ++ const QemuOptDesc *desc; ++ const QemuOptsList *list = opts->list; ++ ++ desc = find_desc_by_name(list->desc, name); ++ if (!desc && !opts_accepts_any(list)) { ++ error_setg(errp, "Invalid parameter '%s'", name); ++ return false; ++ } ++ ++ opt = g_malloc0(sizeof(*opt)); ++ opt->name = g_strdup(name); ++ opt->opts = opts; ++ opt->desc = desc; ++ opt->value.uint = val; ++ opt->str = g_strdup_printf("%" PRId64, val); ++ QTAILQ_INSERT_TAIL(&opts->head, opt, next); ++ return true; ++} ++ ++/** ++ * For each member of @opts, call @func(@opaque, name, value, @errp). ++ * @func() may store an Error through @errp, but must return non-zero then. ++ * When @func() returns non-zero, break the loop and return that value. ++ * Return zero when the loop completes. ++ */ ++int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque, ++ Error **errp) ++{ ++ QemuOpt *opt; ++ int rc; ++ ++ QTAILQ_FOREACH(opt, &opts->head, next) { ++ rc = func(opaque, opt->name, opt->str, errp); ++ if (rc) { ++ return rc; ++ } ++ assert(!errp || !*errp); ++ } ++ return 0; ++} ++ ++QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id) ++{ ++ QemuOpts *opts; ++ ++ QTAILQ_FOREACH(opts, &list->head, next) { ++ if (!opts->id && !id) { ++ return opts; ++ } ++ if (opts->id && id && !strcmp(opts->id, id)) { ++ return opts; ++ } ++ } ++ return NULL; ++} ++ ++QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, ++ int fail_if_exists, Error **errp) ++{ ++ QemuOpts *opts = NULL; ++ ++ if (list->merge_lists) { ++ if (id) { ++ error_setg(errp, "Invalid parameter 'id'"); ++ return NULL; ++ } ++ opts = qemu_opts_find(list, NULL); ++ if (opts) { ++ return opts; ++ } ++ } else if (id) { ++ assert(fail_if_exists); ++ if (!id_wellformed(id)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id", ++ "an identifier"); ++ error_append_hint(errp, "Identifiers consist of letters, digits, " ++ "'-', '.', '_', starting with a letter.\n"); ++ return NULL; ++ } ++ opts = qemu_opts_find(list, id); ++ if (opts != NULL) { ++ error_setg(errp, "Duplicate ID '%s' for %s", id, list->name); ++ return NULL; ++ } ++ } ++ opts = g_malloc0(sizeof(*opts)); ++ opts->id = g_strdup(id); ++ opts->list = list; ++ loc_save(&opts->loc); ++ QTAILQ_INIT(&opts->head); ++ QTAILQ_INSERT_TAIL(&list->head, opts, next); ++ return opts; ++} ++ ++void qemu_opts_reset(QemuOptsList *list) ++{ ++ QemuOpts *opts, *next_opts; ++ ++ QTAILQ_FOREACH_SAFE(opts, &list->head, next, next_opts) { ++ qemu_opts_del(opts); ++ } ++} ++ ++void qemu_opts_loc_restore(QemuOpts *opts) ++{ ++ loc_restore(&opts->loc); ++} ++ ++const char *qemu_opts_id(QemuOpts *opts) ++{ ++ return opts->id; ++} ++ ++/* The id string will be g_free()d by qemu_opts_del */ ++void qemu_opts_set_id(QemuOpts *opts, char *id) ++{ ++ opts->id = id; ++} ++ ++void qemu_opts_del(QemuOpts *opts) ++{ ++ QemuOpt *opt; ++ ++ if (opts == NULL) { ++ return; ++ } ++ ++ for (;;) { ++ opt = QTAILQ_FIRST(&opts->head); ++ if (opt == NULL) ++ break; ++ qemu_opt_del(opt); ++ } ++ QTAILQ_REMOVE(&opts->list->head, opts, next); ++ g_free(opts->id); ++ g_free(opts); ++} ++ ++/* print value, escaping any commas in value */ ++static void escaped_print(const char *value) ++{ ++ const char *ptr; ++ ++ for (ptr = value; *ptr; ++ptr) { ++ if (*ptr == ',') { ++ putchar(','); ++ } ++ putchar(*ptr); ++ } ++} ++ ++void qemu_opts_print(QemuOpts *opts, const char *separator) ++{ ++ QemuOpt *opt; ++ QemuOptDesc *desc = opts->list->desc; ++ const char *sep = ""; ++ ++ if (opts->id) { ++ printf("id=%s", opts->id); /* passed id_wellformed -> no commas */ ++ sep = separator; ++ } ++ ++ if (desc[0].name == NULL) { ++ QTAILQ_FOREACH(opt, &opts->head, next) { ++ printf("%s%s=", sep, opt->name); ++ escaped_print(opt->str); ++ sep = separator; ++ } ++ return; ++ } ++ for (; desc && desc->name; desc++) { ++ const char *value; ++ opt = qemu_opt_find(opts, desc->name); ++ ++ value = opt ? opt->str : desc->def_value_str; ++ if (!value) { ++ continue; ++ } ++ if (desc->type == QEMU_OPT_STRING) { ++ printf("%s%s=", sep, desc->name); ++ escaped_print(value); ++ } else if ((desc->type == QEMU_OPT_SIZE || ++ desc->type == QEMU_OPT_NUMBER) && opt) { ++ printf("%s%s=%" PRId64, sep, desc->name, opt->value.uint); ++ } else { ++ printf("%s%s=%s", sep, desc->name, value); ++ } ++ sep = separator; ++ } ++} ++ ++static const char *get_opt_name_value(const char *params, ++ const char *firstname, ++ bool warn_on_flag, ++ bool *help_wanted, ++ char **name, char **value) ++{ ++ const char *p; ++ const char *prefix = ""; ++ size_t len; ++ bool is_help = false; ++ ++ len = strcspn(params, "=,"); ++ if (params[len] != '=') { ++ /* found "foo,more" */ ++ if (firstname) { ++ /* implicitly named first option */ ++ *name = g_strdup(firstname); ++ p = get_opt_value(params, value); ++ } else { ++ /* option without value, must be a flag */ ++ p = get_opt_name(params, name, len); ++ if (strncmp(*name, "no", 2) == 0) { ++ memmove(*name, *name + 2, strlen(*name + 2) + 1); ++ *value = g_strdup("off"); ++ prefix = "no"; ++ } else { ++ *value = g_strdup("on"); ++ is_help = is_help_option(*name); ++ } ++ if (!is_help && warn_on_flag) { ++ warn_report("short-form boolean option '%s%s' deprecated", prefix, *name); ++ if (g_str_equal(*name, "delay")) { ++ error_printf("Please use nodelay=%s instead\n", prefix[0] ? "on" : "off"); ++ } else { ++ error_printf("Please use %s=%s instead\n", *name, *value); ++ } ++ } ++ } ++ } else { ++ /* found "foo=bar,more" */ ++ p = get_opt_name(params, name, len); ++ assert(*p == '='); ++ p++; ++ p = get_opt_value(p, value); ++ } ++ ++ assert(!*p || *p == ','); ++ if (help_wanted && is_help) { ++ *help_wanted = true; ++ } ++ if (*p == ',') { ++ p++; ++ } ++ return p; ++} ++ ++static bool opts_do_parse(QemuOpts *opts, const char *params, ++ const char *firstname, ++ bool warn_on_flag, bool *help_wanted, Error **errp) ++{ ++ char *option, *value; ++ const char *p; ++ QemuOpt *opt; ++ ++ for (p = params; *p;) { ++ p = get_opt_name_value(p, firstname, warn_on_flag, help_wanted, &option, &value); ++ if (help_wanted && *help_wanted) { ++ g_free(option); ++ g_free(value); ++ return false; ++ } ++ firstname = NULL; ++ ++ if (!strcmp(option, "id")) { ++ g_free(option); ++ g_free(value); ++ continue; ++ } ++ ++ opt = opt_create(opts, option, value); ++ g_free(option); ++ if (!opt_validate(opt, errp)) { ++ qemu_opt_del(opt); ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++static char *opts_parse_id(const char *params) ++{ ++ const char *p; ++ char *name, *value; ++ ++ for (p = params; *p;) { ++ p = get_opt_name_value(p, NULL, false, NULL, &name, &value); ++ if (!strcmp(name, "id")) { ++ g_free(name); ++ return value; ++ } ++ g_free(name); ++ g_free(value); ++ } ++ ++ return NULL; ++} ++ ++bool has_help_option(const char *params) ++{ ++ const char *p; ++ char *name, *value; ++ bool ret = false; ++ ++ for (p = params; *p;) { ++ p = get_opt_name_value(p, NULL, false, &ret, &name, &value); ++ g_free(name); ++ g_free(value); ++ if (ret) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++/** ++ * Store options parsed from @params into @opts. ++ * If @firstname is non-null, the first key=value in @params may omit ++ * key=, and is treated as if key was @firstname. ++ * On error, store an error object through @errp if non-null. ++ */ ++bool qemu_opts_do_parse(QemuOpts *opts, const char *params, ++ const char *firstname, Error **errp) ++{ ++ return opts_do_parse(opts, params, firstname, false, NULL, errp); ++} ++ ++static QemuOpts *opts_parse(QemuOptsList *list, const char *params, ++ bool permit_abbrev, ++ bool warn_on_flag, bool *help_wanted, Error **errp) ++{ ++ const char *firstname; ++ char *id = opts_parse_id(params); ++ QemuOpts *opts; ++ ++ assert(!permit_abbrev || list->implied_opt_name); ++ firstname = permit_abbrev ? list->implied_opt_name : NULL; ++ ++ opts = qemu_opts_create(list, id, !list->merge_lists, errp); ++ g_free(id); ++ if (opts == NULL) { ++ return NULL; ++ } ++ ++ if (!opts_do_parse(opts, params, firstname, ++ warn_on_flag, help_wanted, errp)) { ++ qemu_opts_del(opts); ++ return NULL; ++ } ++ ++ return opts; ++} ++ ++/** ++ * Create a QemuOpts in @list and with options parsed from @params. ++ * If @permit_abbrev, the first key=value in @params may omit key=, ++ * and is treated as if key was @list->implied_opt_name. ++ * On error, store an error object through @errp if non-null. ++ * Return the new QemuOpts on success, null pointer on error. ++ */ ++QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, ++ bool permit_abbrev, Error **errp) ++{ ++ return opts_parse(list, params, permit_abbrev, false, NULL, errp); ++} ++ ++/** ++ * Create a QemuOpts in @list and with options parsed from @params. ++ * If @permit_abbrev, the first key=value in @params may omit key=, ++ * and is treated as if key was @list->implied_opt_name. ++ * Report errors with error_report_err(). This is inappropriate in ++ * QMP context. Do not use this function there! ++ * Return the new QemuOpts on success, null pointer on error. ++ */ ++QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params, ++ bool permit_abbrev) ++{ ++ Error *err = NULL; ++ QemuOpts *opts; ++ bool help_wanted = false; ++ ++ opts = opts_parse(list, params, permit_abbrev, true, ++ opts_accepts_any(list) ? NULL : &help_wanted, ++ &err); ++ if (!opts) { ++ assert(!!err + !!help_wanted == 1); ++ if (help_wanted) { ++ qemu_opts_print_help(list, true); ++ } else { ++ error_report_err(err); ++ } ++ } ++ return opts; ++} ++ ++static bool qemu_opts_from_qdict_entry(QemuOpts *opts, ++ const QDictEntry *entry, ++ Error **errp) ++{ ++ const char *key = qdict_entry_key(entry); ++ QObject *obj = qdict_entry_value(entry); ++ char buf[32]; ++ g_autofree char *tmp = NULL; ++ const char *value; ++ ++ if (!strcmp(key, "id")) { ++ return true; ++ } ++ ++ switch (qobject_type(obj)) { ++ case QTYPE_QSTRING: ++ value = qstring_get_str(qobject_to(QString, obj)); ++ break; ++ case QTYPE_QNUM: ++ tmp = qnum_to_string(qobject_to(QNum, obj)); ++ value = tmp; ++ break; ++ case QTYPE_QBOOL: ++ pstrcpy(buf, sizeof(buf), ++ qbool_get_bool(qobject_to(QBool, obj)) ? "on" : "off"); ++ value = buf; ++ break; ++ default: ++ return true; ++ } ++ ++ return qemu_opt_set(opts, key, value, errp); ++} ++ ++/* ++ * Create QemuOpts from a QDict. ++ * Use value of key "id" as ID if it exists and is a QString. Only ++ * QStrings, QNums and QBools are copied. Entries with other types ++ * are silently ignored. ++ */ ++QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict, ++ Error **errp) ++{ ++ QemuOpts *opts; ++ const QDictEntry *entry; ++ ++ opts = qemu_opts_create(list, qdict_get_try_str(qdict, "id"), 1, errp); ++ if (!opts) { ++ return NULL; ++ } ++ ++ for (entry = qdict_first(qdict); ++ entry; ++ entry = qdict_next(qdict, entry)) { ++ if (!qemu_opts_from_qdict_entry(opts, entry, errp)) { ++ qemu_opts_del(opts); ++ return NULL; ++ } ++ } ++ ++ return opts; ++} ++ ++/* ++ * Adds all QDict entries to the QemuOpts that can be added and removes them ++ * from the QDict. When this function returns, the QDict contains only those ++ * entries that couldn't be added to the QemuOpts. ++ */ ++bool qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp) ++{ ++ const QDictEntry *entry, *next; ++ ++ entry = qdict_first(qdict); ++ ++ while (entry != NULL) { ++ next = qdict_next(qdict, entry); ++ ++ if (opts_accepts_any(opts->list) || ++ find_desc_by_name(opts->list->desc, entry->key)) { ++ if (!qemu_opts_from_qdict_entry(opts, entry, errp)) { ++ return false; ++ } ++ qdict_del(qdict, entry->key); ++ } ++ ++ entry = next; ++ } ++ ++ return true; ++} ++ ++/* ++ * Convert from QemuOpts to QDict. The QDict values are of type QString. ++ * ++ * If @list is given, only add those options to the QDict that are contained in ++ * the list. If @del is true, any options added to the QDict are removed from ++ * the QemuOpts, otherwise they remain there. ++ * ++ * If two options in @opts have the same name, they are processed in order ++ * so that the last one wins (consistent with the reverse iteration in ++ * qemu_opt_find()), but all of them are deleted if @del is true. ++ * ++ * TODO We'll want to use types appropriate for opt->desc->type, but ++ * this is enough for now. ++ */ ++QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict, ++ QemuOptsList *list, bool del) ++{ ++ QemuOpt *opt, *next; ++ ++ if (!qdict) { ++ qdict = qdict_new(); ++ } ++ if (opts->id) { ++ qdict_put_str(qdict, "id", opts->id); ++ } ++ QTAILQ_FOREACH_SAFE(opt, &opts->head, next, next) { ++ if (list) { ++ QemuOptDesc *desc; ++ bool found = false; ++ for (desc = list->desc; desc->name; desc++) { ++ if (!strcmp(desc->name, opt->name)) { ++ found = true; ++ break; ++ } ++ } ++ if (!found) { ++ continue; ++ } ++ } ++ qdict_put_str(qdict, opt->name, opt->str); ++ if (del) { ++ qemu_opt_del(opt); ++ } ++ } ++ return qdict; ++} ++ ++/* Copy all options in a QemuOpts to the given QDict. See ++ * qemu_opts_to_qdict_filtered() for details. */ ++QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict) ++{ ++ return qemu_opts_to_qdict_filtered(opts, qdict, NULL, false); ++} ++ ++/* Validate parsed opts against descriptions where no ++ * descriptions were provided in the QemuOptsList. ++ */ ++bool qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp) ++{ ++ QemuOpt *opt; ++ ++ assert(opts_accepts_any(opts->list)); ++ ++ QTAILQ_FOREACH(opt, &opts->head, next) { ++ opt->desc = find_desc_by_name(desc, opt->name); ++ if (!opt->desc) { ++ error_setg(errp, "Invalid parameter '%s'", opt->name); ++ return false; ++ } ++ ++ if (!qemu_opt_parse(opt, errp)) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/** ++ * For each member of @list, call @func(@opaque, member, @errp). ++ * Call it with the current location temporarily set to the member's. ++ * @func() may store an Error through @errp, but must return non-zero then. ++ * When @func() returns non-zero, break the loop and return that value. ++ * Return zero when the loop completes. ++ */ ++int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, ++ void *opaque, Error **errp) ++{ ++ Location loc; ++ QemuOpts *opts, *next; ++ int rc = 0; ++ ++ loc_push_none(&loc); ++ QTAILQ_FOREACH_SAFE(opts, &list->head, next, next) { ++ loc_restore(&opts->loc); ++ rc = func(opaque, opts, errp); ++ if (rc) { ++ break; ++ } ++ assert(!errp || !*errp); ++ } ++ loc_pop(&loc); ++ return rc; ++} ++ ++static size_t count_opts_list(QemuOptsList *list) ++{ ++ QemuOptDesc *desc = NULL; ++ size_t num_opts = 0; ++ ++ if (!list) { ++ return 0; ++ } ++ ++ desc = list->desc; ++ while (desc && desc->name) { ++ num_opts++; ++ desc++; ++ } ++ ++ return num_opts; ++} ++ ++void qemu_opts_free(QemuOptsList *list) ++{ ++ g_free(list); ++} ++ ++/* Realloc dst option list and append options from an option list (list) ++ * to it. dst could be NULL or a malloced list. ++ * The lifetime of dst must be shorter than the input list because the ++ * QemuOptDesc->name, ->help, and ->def_value_str strings are shared. ++ */ ++QemuOptsList *qemu_opts_append(QemuOptsList *dst, ++ QemuOptsList *list) ++{ ++ size_t num_opts, num_dst_opts; ++ QemuOptDesc *desc; ++ bool need_init = false; ++ bool need_head_update; ++ ++ if (!list) { ++ return dst; ++ } ++ ++ /* If dst is NULL, after realloc, some area of dst should be initialized ++ * before adding options to it. ++ */ ++ if (!dst) { ++ need_init = true; ++ need_head_update = true; ++ } else { ++ /* Moreover, even if dst is not NULL, the realloc may move it to a ++ * different address in which case we may get a stale tail pointer ++ * in dst->head. */ ++ need_head_update = QTAILQ_EMPTY(&dst->head); ++ } ++ ++ num_opts = count_opts_list(dst); ++ num_dst_opts = num_opts; ++ num_opts += count_opts_list(list); ++ dst = g_realloc(dst, sizeof(QemuOptsList) + ++ (num_opts + 1) * sizeof(QemuOptDesc)); ++ if (need_init) { ++ dst->name = NULL; ++ dst->implied_opt_name = NULL; ++ dst->merge_lists = false; ++ } ++ if (need_head_update) { ++ QTAILQ_INIT(&dst->head); ++ } ++ dst->desc[num_dst_opts].name = NULL; ++ ++ /* append list->desc to dst->desc */ ++ if (list) { ++ desc = list->desc; ++ while (desc && desc->name) { ++ if (find_desc_by_name(dst->desc, desc->name) == NULL) { ++ dst->desc[num_dst_opts++] = *desc; ++ dst->desc[num_dst_opts].name = NULL; ++ } ++ desc++; ++ } ++ } ++ ++ return dst; ++} +diff --git a/qcow2/lib/util/qemu-print.c b/qcow2/lib/util/qemu-print.c +new file mode 100644 +index 00000000..69ba612f +--- /dev/null ++++ b/qcow2/lib/util/qemu-print.c +@@ -0,0 +1,70 @@ ++/* ++ * Print to stream or current monitor ++ * ++ * Copyright (C) 2019 Red Hat Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "monitor/monitor.h" ++#include "qemu/qemu-print.h" ++ ++/* ++ * Print like vprintf(). ++ * Print to current monitor if we have one, else to stdout. ++ */ ++int qemu_vprintf(const char *fmt, va_list ap) ++{ ++ Monitor *cur_mon = monitor_cur(); ++ if (cur_mon) { ++ return monitor_vprintf(cur_mon, fmt, ap); ++ } ++ return vprintf(fmt, ap); ++} ++ ++/* ++ * Print like printf(). ++ * Print to current monitor if we have one, else to stdout. ++ */ ++int qemu_printf(const char *fmt, ...) ++{ ++ va_list ap; ++ int ret; ++ ++ va_start(ap, fmt); ++ ret = qemu_vprintf(fmt, ap); ++ va_end(ap); ++ return ret; ++} ++ ++/* ++ * Print like vfprintf() ++ * Print to @stream if non-null, else to current monitor. ++ */ ++int qemu_vfprintf(FILE *stream, const char *fmt, va_list ap) ++{ ++ if (!stream) { ++ return monitor_vprintf(monitor_cur(), fmt, ap); ++ } ++ return vfprintf(stream, fmt, ap); ++} ++ ++/* ++ * Print like fprintf(). ++ * Print to @stream if non-null, else to current monitor. ++ */ ++int qemu_fprintf(FILE *stream, const char *fmt, ...) ++{ ++ va_list ap; ++ int ret; ++ ++ va_start(ap, fmt); ++ ret = qemu_vfprintf(stream, fmt, ap); ++ va_end(ap); ++ return ret; ++} +diff --git a/qcow2/lib/util/qemu-sockets.c b/qcow2/lib/util/qemu-sockets.c +new file mode 100644 +index 00000000..60c44b2b +--- /dev/null ++++ b/qcow2/lib/util/qemu-sockets.c +@@ -0,0 +1,1475 @@ ++/* ++ * inet and unix socket functions for qemu ++ * ++ * (c) 2008 Gerd Hoffmann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; under version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++#include "qemu/osdep.h" ++ ++#ifdef CONFIG_AF_VSOCK ++#include ++#endif /* CONFIG_AF_VSOCK */ ++ ++#include "monitor/monitor.h" ++#include "qapi/clone-visitor.h" ++#include "qapi/error.h" ++#include "qapi/qapi-visit-sockets.h" ++#include "qemu/sockets.h" ++#include "qemu/main-loop.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qapi/qobject-output-visitor.h" ++#include "qemu/cutils.h" ++#include "trace.h" ++ ++#ifndef AI_ADDRCONFIG ++# define AI_ADDRCONFIG 0 ++#endif ++ ++#ifndef AI_V4MAPPED ++# define AI_V4MAPPED 0 ++#endif ++ ++#ifndef AI_NUMERICSERV ++# define AI_NUMERICSERV 0 ++#endif ++ ++ ++static int inet_getport(struct addrinfo *e) ++{ ++ struct sockaddr_in *i4; ++ struct sockaddr_in6 *i6; ++ ++ switch (e->ai_family) { ++ case PF_INET6: ++ i6 = (void*)e->ai_addr; ++ return ntohs(i6->sin6_port); ++ case PF_INET: ++ i4 = (void*)e->ai_addr; ++ return ntohs(i4->sin_port); ++ default: ++ return 0; ++ } ++} ++ ++static void inet_setport(struct addrinfo *e, int port) ++{ ++ struct sockaddr_in *i4; ++ struct sockaddr_in6 *i6; ++ ++ switch (e->ai_family) { ++ case PF_INET6: ++ i6 = (void*)e->ai_addr; ++ i6->sin6_port = htons(port); ++ break; ++ case PF_INET: ++ i4 = (void*)e->ai_addr; ++ i4->sin_port = htons(port); ++ break; ++ } ++} ++ ++NetworkAddressFamily inet_netfamily(int family) ++{ ++ switch (family) { ++ case PF_INET6: return NETWORK_ADDRESS_FAMILY_IPV6; ++ case PF_INET: return NETWORK_ADDRESS_FAMILY_IPV4; ++ case PF_UNIX: return NETWORK_ADDRESS_FAMILY_UNIX; ++#ifdef CONFIG_AF_VSOCK ++ case PF_VSOCK: return NETWORK_ADDRESS_FAMILY_VSOCK; ++#endif /* CONFIG_AF_VSOCK */ ++ } ++ return NETWORK_ADDRESS_FAMILY_UNKNOWN; ++} ++ ++bool fd_is_socket(int fd) ++{ ++ int optval; ++ socklen_t optlen = sizeof(optval); ++ return !getsockopt(fd, SOL_SOCKET, SO_TYPE, &optval, &optlen); ++} ++ ++ ++/* ++ * Matrix we're trying to apply ++ * ++ * ipv4 ipv6 family ++ * - - PF_UNSPEC ++ * - f PF_INET ++ * - t PF_INET6 ++ * f - PF_INET6 ++ * f f ++ * f t PF_INET6 ++ * t - PF_INET ++ * t f PF_INET ++ * t t PF_INET6/PF_UNSPEC ++ * ++ * NB, this matrix is only about getting the necessary results ++ * from getaddrinfo(). Some of the cases require further work ++ * after reading results from getaddrinfo in order to fully ++ * apply the logic the end user wants. ++ * ++ * In the first and last cases, we must set IPV6_V6ONLY=0 ++ * when binding, to allow a single listener to potentially ++ * accept both IPv4+6 addresses. ++ */ ++int inet_ai_family_from_address(InetSocketAddress *addr, ++ Error **errp) ++{ ++ if (addr->has_ipv6 && addr->has_ipv4 && ++ !addr->ipv6 && !addr->ipv4) { ++ error_setg(errp, "Cannot disable IPv4 and IPv6 at same time"); ++ return PF_UNSPEC; ++ } ++ if ((addr->has_ipv6 && addr->ipv6) && (addr->has_ipv4 && addr->ipv4)) { ++ /* ++ * Some backends can only do a single listener. In that case ++ * we want empty hostname to resolve to "::" and then use the ++ * flag IPV6_V6ONLY==0 to get both protocols on 1 socket. This ++ * doesn't work for addresses other than "", so they're just ++ * inevitably broken until multiple listeners can be used, ++ * and thus we honour getaddrinfo automatic protocol detection ++ * Once all backends do multi-listener, remove the PF_INET6 ++ * branch entirely. ++ */ ++ if (!addr->host || g_str_equal(addr->host, "")) { ++ return PF_INET6; ++ } else { ++ return PF_UNSPEC; ++ } ++ } ++ if ((addr->has_ipv6 && addr->ipv6) || (addr->has_ipv4 && !addr->ipv4)) { ++ return PF_INET6; ++ } ++ if ((addr->has_ipv4 && addr->ipv4) || (addr->has_ipv6 && !addr->ipv6)) { ++ return PF_INET; ++ } ++ return PF_UNSPEC; ++} ++ ++static int create_fast_reuse_socket(struct addrinfo *e) ++{ ++ int slisten = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol); ++ if (slisten < 0) { ++ return -1; ++ } ++ socket_set_fast_reuse(slisten); ++ return slisten; ++} ++ ++static int try_bind(int socket, InetSocketAddress *saddr, struct addrinfo *e) ++{ ++#ifndef IPV6_V6ONLY ++ return bind(socket, e->ai_addr, e->ai_addrlen); ++#else ++ /* ++ * Deals with first & last cases in matrix in comment ++ * for inet_ai_family_from_address(). ++ */ ++ int v6only = ++ ((!saddr->has_ipv4 && !saddr->has_ipv6) || ++ (saddr->has_ipv4 && saddr->ipv4 && ++ saddr->has_ipv6 && saddr->ipv6)) ? 0 : 1; ++ int stat; ++ ++ rebind: ++ if (e->ai_family == PF_INET6) { ++ setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, ++ sizeof(v6only)); ++ } ++ ++ stat = bind(socket, e->ai_addr, e->ai_addrlen); ++ if (!stat) { ++ return 0; ++ } ++ ++ /* If we got EADDRINUSE from an IPv6 bind & v6only is unset, ++ * it could be that the IPv4 port is already claimed, so retry ++ * with v6only set ++ */ ++ if (e->ai_family == PF_INET6 && errno == EADDRINUSE && !v6only) { ++ v6only = 1; ++ goto rebind; ++ } ++ return stat; ++#endif ++} ++ ++static int inet_listen_saddr(InetSocketAddress *saddr, ++ int port_offset, ++ int num, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ struct addrinfo ai, *res, *e; ++ char port[33]; ++ char uaddr[INET6_ADDRSTRLEN+1]; ++ char uport[33]; ++ int rc, port_min, port_max, p; ++ int slisten = -1; ++ int saved_errno = 0; ++ bool socket_created = false; ++ ++ if (saddr->keep_alive) { ++ error_setg(errp, "keep-alive option is not supported for passive " ++ "sockets"); ++ return -1; ++ } ++ ++ memset(&ai,0, sizeof(ai)); ++ ai.ai_flags = AI_PASSIVE; ++ if (saddr->has_numeric && saddr->numeric) { ++ ai.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; ++ } ++ ai.ai_socktype = SOCK_STREAM; ++ ai.ai_family = inet_ai_family_from_address(saddr, errp); ++ if (*errp) { ++ return -1; ++ } ++ ++ if (saddr->host == NULL) { ++ error_setg(errp, "host not specified"); ++ return -1; ++ } ++ if (saddr->port != NULL) { ++ pstrcpy(port, sizeof(port), saddr->port); ++ } else { ++ port[0] = '\0'; ++ } ++ ++ /* lookup */ ++ if (port_offset) { ++ uint64_t baseport; ++ if (strlen(port) == 0) { ++ error_setg(errp, "port not specified"); ++ return -1; ++ } ++ if (parse_uint_full(port, 10, &baseport) < 0) { ++ error_setg(errp, "can't convert to a number: %s", port); ++ return -1; ++ } ++ if (baseport > 65535 || ++ baseport + port_offset > 65535) { ++ error_setg(errp, "port %s out of range", port); ++ return -1; ++ } ++ snprintf(port, sizeof(port), "%d", (int)baseport + port_offset); ++ } ++ rc = getaddrinfo(strlen(saddr->host) ? saddr->host : NULL, ++ strlen(port) ? port : NULL, &ai, &res); ++ if (rc != 0) { ++ error_setg(errp, "address resolution failed for %s:%s: %s", ++ saddr->host, port, gai_strerror(rc)); ++ return -1; ++ } ++ ++ /* create socket + bind/listen */ ++ for (e = res; e != NULL; e = e->ai_next) { ++#ifdef HAVE_IPPROTO_MPTCP ++ if (saddr->has_mptcp && saddr->mptcp) { ++ e->ai_protocol = IPPROTO_MPTCP; ++ } ++#endif ++ getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen, ++ uaddr,INET6_ADDRSTRLEN,uport,32, ++ NI_NUMERICHOST | NI_NUMERICSERV); ++ ++ port_min = inet_getport(e); ++ port_max = saddr->has_to ? saddr->to + port_offset : port_min; ++ for (p = port_min; p <= port_max; p++) { ++ inet_setport(e, p); ++ ++ slisten = create_fast_reuse_socket(e); ++ if (slisten < 0) { ++ /* First time we expect we might fail to create the socket ++ * eg if 'e' has AF_INET6 but ipv6 kmod is not loaded. ++ * Later iterations should always succeed if first iteration ++ * worked though, so treat that as fatal. ++ */ ++ if (p == port_min) { ++ continue; ++ } else { ++ error_setg_errno(errp, errno, ++ "Failed to recreate failed listening socket"); ++ goto listen_failed; ++ } ++ } ++ socket_created = true; ++ ++ rc = try_bind(slisten, saddr, e); ++ if (rc < 0) { ++ if (errno != EADDRINUSE) { ++ error_setg_errno(errp, errno, "Failed to bind socket"); ++ goto listen_failed; ++ } ++ } else { ++ if (!listen(slisten, num)) { ++ goto listen_ok; ++ } ++ if (errno != EADDRINUSE) { ++ error_setg_errno(errp, errno, "Failed to listen on socket"); ++ goto listen_failed; ++ } ++ } ++ /* Someone else managed to bind to the same port and beat us ++ * to listen on it! Socket semantics does not allow us to ++ * recover from this situation, so we need to recreate the ++ * socket to allow bind attempts for subsequent ports: ++ */ ++ close(slisten); ++ slisten = -1; ++ } ++ } ++ error_setg_errno(errp, errno, ++ socket_created ? ++ "Failed to find an available port" : ++ "Failed to create a socket"); ++listen_failed: ++ saved_errno = errno; ++ if (slisten >= 0) { ++ close(slisten); ++ } ++ freeaddrinfo(res); ++ errno = saved_errno; ++ return -1; ++ ++listen_ok: ++ freeaddrinfo(res); ++ return slisten; ++} ++ ++#ifdef _WIN32 ++#define QEMU_SOCKET_RC_INPROGRESS(rc) \ ++ ((rc) == -EINPROGRESS || (rc) == -EWOULDBLOCK || (rc) == -WSAEALREADY) ++#else ++#define QEMU_SOCKET_RC_INPROGRESS(rc) \ ++ ((rc) == -EINPROGRESS) ++#endif ++ ++static int inet_connect_addr(const InetSocketAddress *saddr, ++ struct addrinfo *addr, Error **errp) ++{ ++ int sock, rc; ++ ++ sock = qemu_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); ++ if (sock < 0) { ++ error_setg_errno(errp, errno, "Failed to create socket family %d", ++ addr->ai_family); ++ return -1; ++ } ++ socket_set_fast_reuse(sock); ++ ++ /* connect to peer */ ++ do { ++ rc = 0; ++ if (connect(sock, addr->ai_addr, addr->ai_addrlen) < 0) { ++ rc = -errno; ++ } ++ } while (rc == -EINTR); ++ ++ if (rc < 0) { ++ error_setg_errno(errp, errno, "Failed to connect to '%s:%s'", ++ saddr->host, saddr->port); ++ close(sock); ++ return -1; ++ } ++ ++ return sock; ++} ++ ++static struct addrinfo *inet_parse_connect_saddr(InetSocketAddress *saddr, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ struct addrinfo ai, *res; ++ int rc; ++ static int useV4Mapped = 1; ++ ++ memset(&ai, 0, sizeof(ai)); ++ ++ ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; ++ if (qatomic_read(&useV4Mapped)) { ++ ai.ai_flags |= AI_V4MAPPED; ++ } ++ ai.ai_socktype = SOCK_STREAM; ++ ai.ai_family = inet_ai_family_from_address(saddr, errp); ++ if (*errp) { ++ return NULL; ++ } ++ ++ if (saddr->host == NULL || saddr->port == NULL) { ++ error_setg(errp, "host and/or port not specified"); ++ return NULL; ++ } ++ ++ /* lookup */ ++ rc = getaddrinfo(saddr->host, saddr->port, &ai, &res); ++ ++ /* At least FreeBSD and OS-X 10.6 declare AI_V4MAPPED but ++ * then don't implement it in their getaddrinfo(). Detect ++ * this and retry without the flag since that's preferable ++ * to a fatal error ++ */ ++ if (rc == EAI_BADFLAGS && ++ (ai.ai_flags & AI_V4MAPPED)) { ++ qatomic_set(&useV4Mapped, 0); ++ ai.ai_flags &= ~AI_V4MAPPED; ++ rc = getaddrinfo(saddr->host, saddr->port, &ai, &res); ++ } ++ if (rc != 0) { ++ error_setg(errp, "address resolution failed for %s:%s: %s", ++ saddr->host, saddr->port, gai_strerror(rc)); ++ return NULL; ++ } ++ return res; ++} ++ ++/** ++ * Create a socket and connect it to an address. ++ * ++ * @saddr: Inet socket address specification ++ * @errp: set on error ++ * ++ * Returns: -1 on error, file descriptor on success. ++ */ ++int inet_connect_saddr(InetSocketAddress *saddr, Error **errp) ++{ ++ Error *local_err = NULL; ++ struct addrinfo *res, *e; ++ int sock = -1; ++ ++ res = inet_parse_connect_saddr(saddr, errp); ++ if (!res) { ++ return -1; ++ } ++ ++ for (e = res; e != NULL; e = e->ai_next) { ++ error_free(local_err); ++ local_err = NULL; ++ ++#ifdef HAVE_IPPROTO_MPTCP ++ if (saddr->has_mptcp && saddr->mptcp) { ++ e->ai_protocol = IPPROTO_MPTCP; ++ } ++#endif ++ ++ sock = inet_connect_addr(saddr, e, &local_err); ++ if (sock >= 0) { ++ break; ++ } ++ } ++ ++ freeaddrinfo(res); ++ ++ if (sock < 0) { ++ error_propagate(errp, local_err); ++ return sock; ++ } ++ ++ if (saddr->keep_alive) { ++ int val = 1; ++ int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, ++ &val, sizeof(val)); ++ ++ if (ret < 0) { ++ error_setg_errno(errp, errno, "Unable to set KEEPALIVE"); ++ close(sock); ++ return -1; ++ } ++ } ++ ++ return sock; ++} ++ ++static int inet_dgram_saddr(InetSocketAddress *sraddr, ++ InetSocketAddress *sladdr, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ struct addrinfo ai, *peer = NULL, *local = NULL; ++ const char *addr; ++ const char *port; ++ int sock = -1, rc; ++ ++ /* lookup peer addr */ ++ memset(&ai,0, sizeof(ai)); ++ ai.ai_flags = AI_CANONNAME | AI_V4MAPPED | AI_ADDRCONFIG; ++ ai.ai_socktype = SOCK_DGRAM; ++ ai.ai_family = inet_ai_family_from_address(sraddr, errp); ++ if (*errp) { ++ goto err; ++ } ++ ++ addr = sraddr->host; ++ port = sraddr->port; ++ if (addr == NULL || strlen(addr) == 0) { ++ addr = "localhost"; ++ } ++ if (port == NULL || strlen(port) == 0) { ++ error_setg(errp, "remote port not specified"); ++ goto err; ++ } ++ ++ if ((rc = getaddrinfo(addr, port, &ai, &peer)) != 0) { ++ error_setg(errp, "address resolution failed for %s:%s: %s", addr, port, ++ gai_strerror(rc)); ++ goto err; ++ } ++ ++ /* lookup local addr */ ++ memset(&ai,0, sizeof(ai)); ++ ai.ai_flags = AI_PASSIVE; ++ ai.ai_family = peer->ai_family; ++ ai.ai_socktype = SOCK_DGRAM; ++ ++ if (sladdr) { ++ addr = sladdr->host; ++ port = sladdr->port; ++ if (addr == NULL || strlen(addr) == 0) { ++ addr = NULL; ++ } ++ if (!port || strlen(port) == 0) { ++ port = "0"; ++ } ++ } else { ++ addr = NULL; ++ port = "0"; ++ } ++ ++ if ((rc = getaddrinfo(addr, port, &ai, &local)) != 0) { ++ error_setg(errp, "address resolution failed for %s:%s: %s", addr, port, ++ gai_strerror(rc)); ++ goto err; ++ } ++ ++ /* create socket */ ++ sock = qemu_socket(peer->ai_family, peer->ai_socktype, peer->ai_protocol); ++ if (sock < 0) { ++ error_setg_errno(errp, errno, "Failed to create socket family %d", ++ peer->ai_family); ++ goto err; ++ } ++ socket_set_fast_reuse(sock); ++ ++ /* bind socket */ ++ if (bind(sock, local->ai_addr, local->ai_addrlen) < 0) { ++ error_setg_errno(errp, errno, "Failed to bind socket"); ++ goto err; ++ } ++ ++ /* connect to peer */ ++ if (connect(sock,peer->ai_addr,peer->ai_addrlen) < 0) { ++ error_setg_errno(errp, errno, "Failed to connect to '%s:%s'", ++ addr, port); ++ goto err; ++ } ++ ++ freeaddrinfo(local); ++ freeaddrinfo(peer); ++ return sock; ++ ++err: ++ if (sock != -1) { ++ close(sock); ++ } ++ if (local) { ++ freeaddrinfo(local); ++ } ++ if (peer) { ++ freeaddrinfo(peer); ++ } ++ ++ return -1; ++} ++ ++/* compatibility wrapper */ ++static int inet_parse_flag(const char *flagname, const char *optstr, bool *val, ++ Error **errp) ++{ ++ char *end; ++ size_t len; ++ ++ end = strstr(optstr, ","); ++ if (end) { ++ if (end[1] == ',') { /* Reject 'ipv6=on,,foo' */ ++ error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr); ++ return -1; ++ } ++ len = end - optstr; ++ } else { ++ len = strlen(optstr); ++ } ++ if (len == 0 || (len == 3 && strncmp(optstr, "=on", len) == 0)) { ++ *val = true; ++ } else if (len == 4 && strncmp(optstr, "=off", len) == 0) { ++ *val = false; ++ } else { ++ error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr); ++ return -1; ++ } ++ return 0; ++} ++ ++int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) ++{ ++ const char *optstr, *h; ++ char host[65]; ++ char port[33]; ++ int to; ++ int pos; ++ char *begin; ++ ++ memset(addr, 0, sizeof(*addr)); ++ ++ /* parse address */ ++ if (str[0] == ':') { ++ /* no host given */ ++ host[0] = '\0'; ++ if (sscanf(str, ":%32[^,]%n", port, &pos) != 1) { ++ error_setg(errp, "error parsing port in address '%s'", str); ++ return -1; ++ } ++ } else if (str[0] == '[') { ++ /* IPv6 addr */ ++ if (sscanf(str, "[%64[^]]]:%32[^,]%n", host, port, &pos) != 2) { ++ error_setg(errp, "error parsing IPv6 address '%s'", str); ++ return -1; ++ } ++ } else { ++ /* hostname or IPv4 addr */ ++ if (sscanf(str, "%64[^:]:%32[^,]%n", host, port, &pos) != 2) { ++ error_setg(errp, "error parsing address '%s'", str); ++ return -1; ++ } ++ } ++ ++ addr->host = g_strdup(host); ++ addr->port = g_strdup(port); ++ ++ /* parse options */ ++ optstr = str + pos; ++ h = strstr(optstr, ",to="); ++ if (h) { ++ h += 4; ++ if (sscanf(h, "%d%n", &to, &pos) != 1 || ++ (h[pos] != '\0' && h[pos] != ',')) { ++ error_setg(errp, "error parsing to= argument"); ++ return -1; ++ } ++ addr->has_to = true; ++ addr->to = to; ++ } ++ begin = strstr(optstr, ",ipv4"); ++ if (begin) { ++ if (inet_parse_flag("ipv4", begin + 5, &addr->ipv4, errp) < 0) { ++ return -1; ++ } ++ addr->has_ipv4 = true; ++ } ++ begin = strstr(optstr, ",ipv6"); ++ if (begin) { ++ if (inet_parse_flag("ipv6", begin + 5, &addr->ipv6, errp) < 0) { ++ return -1; ++ } ++ addr->has_ipv6 = true; ++ } ++ begin = strstr(optstr, ",keep-alive"); ++ if (begin) { ++ if (inet_parse_flag("keep-alive", begin + strlen(",keep-alive"), ++ &addr->keep_alive, errp) < 0) ++ { ++ return -1; ++ } ++ addr->has_keep_alive = true; ++ } ++#ifdef HAVE_IPPROTO_MPTCP ++ begin = strstr(optstr, ",mptcp"); ++ if (begin) { ++ if (inet_parse_flag("mptcp", begin + strlen(",mptcp"), ++ &addr->mptcp, errp) < 0) ++ { ++ return -1; ++ } ++ addr->has_mptcp = true; ++ } ++#endif ++ return 0; ++} ++ ++ ++/** ++ * Create a blocking socket and connect it to an address. ++ * ++ * @str: address string ++ * @errp: set in case of an error ++ * ++ * Returns -1 in case of error, file descriptor on success ++ **/ ++int inet_connect(const char *str, Error **errp) ++{ ++ int sock = -1; ++ InetSocketAddress *addr = g_new(InetSocketAddress, 1); ++ ++ if (!inet_parse(addr, str, errp)) { ++ sock = inet_connect_saddr(addr, errp); ++ } ++ qapi_free_InetSocketAddress(addr); ++ return sock; ++} ++ ++#ifdef CONFIG_AF_VSOCK ++static bool vsock_parse_vaddr_to_sockaddr(const VsockSocketAddress *vaddr, ++ struct sockaddr_vm *svm, ++ Error **errp) ++{ ++ uint64_t val; ++ ++ memset(svm, 0, sizeof(*svm)); ++ svm->svm_family = AF_VSOCK; ++ ++ if (parse_uint_full(vaddr->cid, 10, &val) < 0 || ++ val > UINT32_MAX) { ++ error_setg(errp, "Failed to parse cid '%s'", vaddr->cid); ++ return false; ++ } ++ svm->svm_cid = val; ++ ++ if (parse_uint_full(vaddr->port, 10, &val) < 0 || ++ val > UINT32_MAX) { ++ error_setg(errp, "Failed to parse port '%s'", vaddr->port); ++ return false; ++ } ++ svm->svm_port = val; ++ ++ return true; ++} ++ ++static int vsock_connect_addr(const VsockSocketAddress *vaddr, ++ const struct sockaddr_vm *svm, Error **errp) ++{ ++ int sock, rc; ++ ++ sock = qemu_socket(AF_VSOCK, SOCK_STREAM, 0); ++ if (sock < 0) { ++ error_setg_errno(errp, errno, "Failed to create socket family %d", ++ AF_VSOCK); ++ return -1; ++ } ++ ++ /* connect to peer */ ++ do { ++ rc = 0; ++ if (connect(sock, (const struct sockaddr *)svm, sizeof(*svm)) < 0) { ++ rc = -errno; ++ } ++ } while (rc == -EINTR); ++ ++ if (rc < 0) { ++ error_setg_errno(errp, errno, "Failed to connect to '%s:%s'", ++ vaddr->cid, vaddr->port); ++ close(sock); ++ return -1; ++ } ++ ++ return sock; ++} ++ ++static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp) ++{ ++ struct sockaddr_vm svm; ++ ++ if (!vsock_parse_vaddr_to_sockaddr(vaddr, &svm, errp)) { ++ return -1; ++ } ++ ++ return vsock_connect_addr(vaddr, &svm, errp); ++} ++ ++static int vsock_listen_saddr(VsockSocketAddress *vaddr, ++ int num, ++ Error **errp) ++{ ++ struct sockaddr_vm svm; ++ int slisten; ++ ++ if (!vsock_parse_vaddr_to_sockaddr(vaddr, &svm, errp)) { ++ return -1; ++ } ++ ++ slisten = qemu_socket(AF_VSOCK, SOCK_STREAM, 0); ++ if (slisten < 0) { ++ error_setg_errno(errp, errno, "Failed to create socket"); ++ return -1; ++ } ++ ++ if (bind(slisten, (const struct sockaddr *)&svm, sizeof(svm)) != 0) { ++ error_setg_errno(errp, errno, "Failed to bind socket"); ++ close(slisten); ++ return -1; ++ } ++ ++ if (listen(slisten, num) != 0) { ++ error_setg_errno(errp, errno, "Failed to listen on socket"); ++ close(slisten); ++ return -1; ++ } ++ return slisten; ++} ++ ++static int vsock_parse(VsockSocketAddress *addr, const char *str, ++ Error **errp) ++{ ++ char cid[33]; ++ char port[33]; ++ int n; ++ ++ if (sscanf(str, "%32[^:]:%32[^,]%n", cid, port, &n) != 2) { ++ error_setg(errp, "error parsing address '%s'", str); ++ return -1; ++ } ++ if (str[n] != '\0') { ++ error_setg(errp, "trailing characters in address '%s'", str); ++ return -1; ++ } ++ ++ addr->cid = g_strdup(cid); ++ addr->port = g_strdup(port); ++ return 0; ++} ++#else ++static void vsock_unsupported(Error **errp) ++{ ++ error_setg(errp, "socket family AF_VSOCK unsupported"); ++} ++ ++static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp) ++{ ++ vsock_unsupported(errp); ++ return -1; ++} ++ ++static int vsock_listen_saddr(VsockSocketAddress *vaddr, ++ int num, ++ Error **errp) ++{ ++ vsock_unsupported(errp); ++ return -1; ++} ++ ++static int vsock_parse(VsockSocketAddress *addr, const char *str, ++ Error **errp) ++{ ++ vsock_unsupported(errp); ++ return -1; ++} ++#endif /* CONFIG_AF_VSOCK */ ++ ++static bool saddr_is_abstract(UnixSocketAddress *saddr) ++{ ++#ifdef CONFIG_LINUX ++ return saddr->abstract; ++#else ++ return false; ++#endif ++} ++ ++static bool saddr_is_tight(UnixSocketAddress *saddr) ++{ ++#ifdef CONFIG_LINUX ++ return !saddr->has_tight || saddr->tight; ++#else ++ return false; ++#endif ++} ++ ++static int unix_listen_saddr(UnixSocketAddress *saddr, ++ int num, ++ Error **errp) ++{ ++ bool abstract = saddr_is_abstract(saddr); ++ struct sockaddr_un un; ++ int sock, fd; ++ char *pathbuf = NULL; ++ const char *path; ++ size_t pathlen; ++ size_t addrlen; ++ ++ sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); ++ if (sock < 0) { ++ error_setg_errno(errp, errno, "Failed to create Unix socket"); ++ return -1; ++ } ++ ++ if (saddr->path[0] || abstract) { ++ path = saddr->path; ++ } else { ++ path = pathbuf = g_strdup_printf("%s/qemu-socket-XXXXXX", ++ g_get_tmp_dir()); ++ } ++ ++ pathlen = strlen(path); ++ if (pathlen > sizeof(un.sun_path) || ++ (abstract && pathlen > (sizeof(un.sun_path) - 1))) { ++ error_setg(errp, "UNIX socket path '%s' is too long", path); ++ error_append_hint(errp, "Path must be less than %zu bytes\n", ++ abstract ? sizeof(un.sun_path) - 1 : ++ sizeof(un.sun_path)); ++ goto err; ++ } ++ ++ if (pathbuf != NULL) { ++ /* ++ * This dummy fd usage silences the mktemp() insecure warning. ++ * Using mkstemp() doesn't make things more secure here ++ * though. bind() complains about existing files, so we have ++ * to unlink first and thus re-open the race window. The ++ * worst case possible is bind() failing, i.e. a DoS attack. ++ */ ++ fd = mkstemp(pathbuf); ++ if (fd < 0) { ++ error_setg_errno(errp, errno, ++ "Failed to make a temporary socket %s", pathbuf); ++ goto err; ++ } ++ close(fd); ++ } ++ ++ if (!abstract && unlink(path) < 0 && errno != ENOENT) { ++ error_setg_errno(errp, errno, ++ "Failed to unlink socket %s", path); ++ goto err; ++ } ++ ++ memset(&un, 0, sizeof(un)); ++ un.sun_family = AF_UNIX; ++ addrlen = sizeof(un); ++ ++ if (abstract) { ++ un.sun_path[0] = '\0'; ++ memcpy(&un.sun_path[1], path, pathlen); ++ if (saddr_is_tight(saddr)) { ++ addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + pathlen; ++ } ++ } else { ++ memcpy(un.sun_path, path, pathlen); ++ } ++ ++ if (bind(sock, (struct sockaddr *) &un, addrlen) < 0) { ++ error_setg_errno(errp, errno, "Failed to bind socket to %s", path); ++ goto err; ++ } ++ if (listen(sock, num) < 0) { ++ error_setg_errno(errp, errno, "Failed to listen on socket"); ++ goto err; ++ } ++ ++ g_free(pathbuf); ++ return sock; ++ ++err: ++ g_free(pathbuf); ++ close(sock); ++ return -1; ++} ++ ++static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) ++{ ++ bool abstract = saddr_is_abstract(saddr); ++ struct sockaddr_un un; ++ int sock, rc; ++ size_t pathlen; ++ size_t addrlen; ++ ++ if (saddr->path == NULL) { ++ error_setg(errp, "unix connect: no path specified"); ++ return -1; ++ } ++ ++ sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); ++ if (sock < 0) { ++ error_setg_errno(errp, errno, "Failed to create socket"); ++ return -1; ++ } ++ ++ pathlen = strlen(saddr->path); ++ if (pathlen > sizeof(un.sun_path) || ++ (abstract && pathlen > (sizeof(un.sun_path) - 1))) { ++ error_setg(errp, "UNIX socket path '%s' is too long", saddr->path); ++ error_append_hint(errp, "Path must be less than %zu bytes\n", ++ abstract ? sizeof(un.sun_path) - 1 : ++ sizeof(un.sun_path)); ++ goto err; ++ } ++ ++ memset(&un, 0, sizeof(un)); ++ un.sun_family = AF_UNIX; ++ addrlen = sizeof(un); ++ ++ if (abstract) { ++ un.sun_path[0] = '\0'; ++ memcpy(&un.sun_path[1], saddr->path, pathlen); ++ if (saddr_is_tight(saddr)) { ++ addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + pathlen; ++ } ++ } else { ++ memcpy(un.sun_path, saddr->path, pathlen); ++ } ++ /* connect to peer */ ++ do { ++ rc = 0; ++ if (connect(sock, (struct sockaddr *) &un, addrlen) < 0) { ++ rc = -errno; ++ } ++ } while (rc == -EINTR); ++ ++ if (rc < 0) { ++ error_setg_errno(errp, -rc, "Failed to connect to '%s'", ++ saddr->path); ++ goto err; ++ } ++ ++ return sock; ++ ++ err: ++ close(sock); ++ return -1; ++} ++ ++/* compatibility wrapper */ ++int unix_listen(const char *str, Error **errp) ++{ ++ UnixSocketAddress *saddr; ++ int sock; ++ ++ saddr = g_new0(UnixSocketAddress, 1); ++ saddr->path = g_strdup(str); ++ sock = unix_listen_saddr(saddr, 1, errp); ++ qapi_free_UnixSocketAddress(saddr); ++ return sock; ++} ++ ++int unix_connect(const char *path, Error **errp) ++{ ++ UnixSocketAddress *saddr; ++ int sock; ++ ++ saddr = g_new0(UnixSocketAddress, 1); ++ saddr->path = g_strdup(path); ++ sock = unix_connect_saddr(saddr, errp); ++ qapi_free_UnixSocketAddress(saddr); ++ return sock; ++} ++ ++char *socket_uri(SocketAddress *addr) ++{ ++ switch (addr->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ return g_strdup_printf("tcp:%s:%s", ++ addr->u.inet.host, ++ addr->u.inet.port); ++ case SOCKET_ADDRESS_TYPE_UNIX: ++ return g_strdup_printf("unix:%s", ++ addr->u.q_unix.path); ++ case SOCKET_ADDRESS_TYPE_FD: ++ return g_strdup_printf("fd:%s", addr->u.fd.str); ++ case SOCKET_ADDRESS_TYPE_VSOCK: ++ return g_strdup_printf("vsock:%s:%s", ++ addr->u.vsock.cid, ++ addr->u.vsock.port); ++ default: ++ return g_strdup("unknown address type"); ++ } ++} ++ ++SocketAddress *socket_parse(const char *str, Error **errp) ++{ ++ SocketAddress *addr; ++ ++ addr = g_new0(SocketAddress, 1); ++ if (strstart(str, "unix:", NULL)) { ++ if (str[5] == '\0') { ++ error_setg(errp, "invalid Unix socket address"); ++ goto fail; ++ } else { ++ addr->type = SOCKET_ADDRESS_TYPE_UNIX; ++ addr->u.q_unix.path = g_strdup(str + 5); ++ } ++ } else if (strstart(str, "fd:", NULL)) { ++ if (str[3] == '\0') { ++ error_setg(errp, "invalid file descriptor address"); ++ goto fail; ++ } else { ++ addr->type = SOCKET_ADDRESS_TYPE_FD; ++ addr->u.fd.str = g_strdup(str + 3); ++ } ++ } else if (strstart(str, "vsock:", NULL)) { ++ addr->type = SOCKET_ADDRESS_TYPE_VSOCK; ++ if (vsock_parse(&addr->u.vsock, str + strlen("vsock:"), errp)) { ++ goto fail; ++ } ++ } else if (strstart(str, "tcp:", NULL)) { ++ addr->type = SOCKET_ADDRESS_TYPE_INET; ++ if (inet_parse(&addr->u.inet, str + strlen("tcp:"), errp)) { ++ goto fail; ++ } ++ } else { ++ addr->type = SOCKET_ADDRESS_TYPE_INET; ++ if (inet_parse(&addr->u.inet, str, errp)) { ++ goto fail; ++ } ++ } ++ return addr; ++ ++fail: ++ qapi_free_SocketAddress(addr); ++ return NULL; ++} ++ ++static int socket_get_fd(const char *fdstr, Error **errp) ++{ ++ Monitor *cur_mon = monitor_cur(); ++ int fd; ++ if (cur_mon) { ++ fd = monitor_get_fd(cur_mon, fdstr, errp); ++ if (fd < 0) { ++ return -1; ++ } ++ } else { ++ if (qemu_strtoi(fdstr, NULL, 10, &fd) < 0) { ++ error_setg_errno(errp, errno, ++ "Unable to parse FD number %s", ++ fdstr); ++ return -1; ++ } ++ } ++ if (!fd_is_socket(fd)) { ++ error_setg(errp, "File descriptor '%s' is not a socket", fdstr); ++ close(fd); ++ return -1; ++ } ++ return fd; ++} ++ ++int socket_address_parse_named_fd(SocketAddress *addr, Error **errp) ++{ ++ int fd; ++ ++ if (addr->type != SOCKET_ADDRESS_TYPE_FD) { ++ return 0; ++ } ++ ++ fd = socket_get_fd(addr->u.fd.str, errp); ++ if (fd < 0) { ++ return fd; ++ } ++ ++ g_free(addr->u.fd.str); ++ addr->u.fd.str = g_strdup_printf("%d", fd); ++ ++ return 0; ++} ++ ++int socket_connect(SocketAddress *addr, Error **errp) ++{ ++ int fd; ++ ++ switch (addr->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ fd = inet_connect_saddr(&addr->u.inet, errp); ++ break; ++ ++ case SOCKET_ADDRESS_TYPE_UNIX: ++ fd = unix_connect_saddr(&addr->u.q_unix, errp); ++ break; ++ ++ case SOCKET_ADDRESS_TYPE_FD: ++ fd = socket_get_fd(addr->u.fd.str, errp); ++ break; ++ ++ case SOCKET_ADDRESS_TYPE_VSOCK: ++ fd = vsock_connect_saddr(&addr->u.vsock, errp); ++ break; ++ ++ default: ++ abort(); ++ } ++ return fd; ++} ++ ++int socket_listen(SocketAddress *addr, int num, Error **errp) ++{ ++ int fd; ++ ++ trace_socket_listen(num); ++ switch (addr->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ fd = inet_listen_saddr(&addr->u.inet, 0, num, errp); ++ break; ++ ++ case SOCKET_ADDRESS_TYPE_UNIX: ++ fd = unix_listen_saddr(&addr->u.q_unix, num, errp); ++ break; ++ ++ case SOCKET_ADDRESS_TYPE_FD: ++ fd = socket_get_fd(addr->u.fd.str, errp); ++ if (fd < 0) { ++ return -1; ++ } ++ ++ /* ++ * If the socket is not yet in the listen state, then transition it to ++ * the listen state now. ++ * ++ * If it's already listening then this updates the backlog value as ++ * requested. ++ * ++ * If this socket cannot listen because it's already in another state ++ * (e.g. unbound or connected) then we'll catch the error here. ++ */ ++ if (listen(fd, num) != 0) { ++ error_setg_errno(errp, errno, "Failed to listen on fd socket"); ++ close(fd); ++ return -1; ++ } ++ break; ++ ++ case SOCKET_ADDRESS_TYPE_VSOCK: ++ fd = vsock_listen_saddr(&addr->u.vsock, num, errp); ++ break; ++ ++ default: ++ abort(); ++ } ++ return fd; ++} ++ ++void socket_listen_cleanup(int fd, Error **errp) ++{ ++ SocketAddress *addr; ++ ++ addr = socket_local_address(fd, errp); ++ if (!addr) { ++ return; ++ } ++ ++ if (addr->type == SOCKET_ADDRESS_TYPE_UNIX ++ && addr->u.q_unix.path) { ++ if (unlink(addr->u.q_unix.path) < 0 && errno != ENOENT) { ++ error_setg_errno(errp, errno, ++ "Failed to unlink socket %s", ++ addr->u.q_unix.path); ++ } ++ } ++ ++ qapi_free_SocketAddress(addr); ++} ++ ++int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp) ++{ ++ int fd; ++ ++ /* ++ * TODO SOCKET_ADDRESS_TYPE_FD when fd is AF_INET or AF_INET6 ++ * (although other address families can do SOCK_DGRAM, too) ++ */ ++ switch (remote->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ fd = inet_dgram_saddr(&remote->u.inet, ++ local ? &local->u.inet : NULL, errp); ++ break; ++ ++ default: ++ error_setg(errp, "socket type unsupported for datagram"); ++ fd = -1; ++ } ++ return fd; ++} ++ ++ ++static SocketAddress * ++socket_sockaddr_to_address_inet(struct sockaddr_storage *sa, ++ socklen_t salen, ++ Error **errp) ++{ ++ char host[NI_MAXHOST]; ++ char serv[NI_MAXSERV]; ++ SocketAddress *addr; ++ InetSocketAddress *inet; ++ int ret; ++ ++ ret = getnameinfo((struct sockaddr *)sa, salen, ++ host, sizeof(host), ++ serv, sizeof(serv), ++ NI_NUMERICHOST | NI_NUMERICSERV); ++ if (ret != 0) { ++ error_setg(errp, "Cannot format numeric socket address: %s", ++ gai_strerror(ret)); ++ return NULL; ++ } ++ ++ addr = g_new0(SocketAddress, 1); ++ addr->type = SOCKET_ADDRESS_TYPE_INET; ++ inet = &addr->u.inet; ++ inet->host = g_strdup(host); ++ inet->port = g_strdup(serv); ++ if (sa->ss_family == AF_INET) { ++ inet->has_ipv4 = inet->ipv4 = true; ++ } else { ++ inet->has_ipv6 = inet->ipv6 = true; ++ } ++ ++ return addr; ++} ++ ++ ++static SocketAddress * ++socket_sockaddr_to_address_unix(struct sockaddr_storage *sa, ++ socklen_t salen, ++ Error **errp) ++{ ++ SocketAddress *addr; ++ struct sockaddr_un *su = (struct sockaddr_un *)sa; ++ ++ addr = g_new0(SocketAddress, 1); ++ addr->type = SOCKET_ADDRESS_TYPE_UNIX; ++ salen -= offsetof(struct sockaddr_un, sun_path); ++#ifdef CONFIG_LINUX ++ if (salen > 0 && !su->sun_path[0]) { ++ /* Linux abstract socket */ ++ addr->u.q_unix.path = g_strndup(su->sun_path + 1, salen - 1); ++ addr->u.q_unix.has_abstract = true; ++ addr->u.q_unix.abstract = true; ++ addr->u.q_unix.has_tight = true; ++ addr->u.q_unix.tight = salen < sizeof(su->sun_path); ++ return addr; ++ } ++#endif ++ ++ addr->u.q_unix.path = g_strndup(su->sun_path, salen); ++ return addr; ++} ++ ++#ifdef CONFIG_AF_VSOCK ++static SocketAddress * ++socket_sockaddr_to_address_vsock(struct sockaddr_storage *sa, ++ socklen_t salen, ++ Error **errp) ++{ ++ SocketAddress *addr; ++ VsockSocketAddress *vaddr; ++ struct sockaddr_vm *svm = (struct sockaddr_vm *)sa; ++ ++ addr = g_new0(SocketAddress, 1); ++ addr->type = SOCKET_ADDRESS_TYPE_VSOCK; ++ vaddr = &addr->u.vsock; ++ vaddr->cid = g_strdup_printf("%u", svm->svm_cid); ++ vaddr->port = g_strdup_printf("%u", svm->svm_port); ++ ++ return addr; ++} ++#endif /* CONFIG_AF_VSOCK */ ++ ++SocketAddress * ++socket_sockaddr_to_address(struct sockaddr_storage *sa, ++ socklen_t salen, ++ Error **errp) ++{ ++ switch (sa->ss_family) { ++ case AF_INET: ++ case AF_INET6: ++ return socket_sockaddr_to_address_inet(sa, salen, errp); ++ ++ case AF_UNIX: ++ return socket_sockaddr_to_address_unix(sa, salen, errp); ++ ++#ifdef CONFIG_AF_VSOCK ++ case AF_VSOCK: ++ return socket_sockaddr_to_address_vsock(sa, salen, errp); ++#endif ++ ++ default: ++ error_setg(errp, "socket family %d unsupported", ++ sa->ss_family); ++ return NULL; ++ } ++ return 0; ++} ++ ++ ++SocketAddress *socket_local_address(int fd, Error **errp) ++{ ++ struct sockaddr_storage ss; ++ socklen_t sslen = sizeof(ss); ++ ++ if (getsockname(fd, (struct sockaddr *)&ss, &sslen) < 0) { ++ error_setg_errno(errp, errno, "%s", ++ "Unable to query local socket address"); ++ return NULL; ++ } ++ ++ return socket_sockaddr_to_address(&ss, sslen, errp); ++} ++ ++ ++SocketAddress *socket_remote_address(int fd, Error **errp) ++{ ++ struct sockaddr_storage ss; ++ socklen_t sslen = sizeof(ss); ++ ++ if (getpeername(fd, (struct sockaddr *)&ss, &sslen) < 0) { ++ error_setg_errno(errp, errno, "%s", ++ "Unable to query remote socket address"); ++ return NULL; ++ } ++ ++ return socket_sockaddr_to_address(&ss, sslen, errp); ++} ++ ++ ++SocketAddress *socket_address_flatten(SocketAddressLegacy *addr_legacy) ++{ ++ SocketAddress *addr; ++ ++ if (!addr_legacy) { ++ return NULL; ++ } ++ ++ addr = g_new(SocketAddress, 1); ++ ++ switch (addr_legacy->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ addr->type = SOCKET_ADDRESS_TYPE_INET; ++ QAPI_CLONE_MEMBERS(InetSocketAddress, &addr->u.inet, ++ addr_legacy->u.inet.data); ++ break; ++ case SOCKET_ADDRESS_TYPE_UNIX: ++ addr->type = SOCKET_ADDRESS_TYPE_UNIX; ++ QAPI_CLONE_MEMBERS(UnixSocketAddress, &addr->u.q_unix, ++ addr_legacy->u.q_unix.data); ++ break; ++ case SOCKET_ADDRESS_TYPE_VSOCK: ++ addr->type = SOCKET_ADDRESS_TYPE_VSOCK; ++ QAPI_CLONE_MEMBERS(VsockSocketAddress, &addr->u.vsock, ++ addr_legacy->u.vsock.data); ++ break; ++ case SOCKET_ADDRESS_TYPE_FD: ++ addr->type = SOCKET_ADDRESS_TYPE_FD; ++ QAPI_CLONE_MEMBERS(FdSocketAddress, &addr->u.fd, ++ addr_legacy->u.fd.data); ++ break; ++ default: ++ abort(); ++ } ++ ++ return addr; ++} +diff --git a/qcow2/lib/util/qemu-thread-common.h b/qcow2/lib/util/qemu-thread-common.h +new file mode 100644 +index 00000000..2af6b120 +--- /dev/null ++++ b/qcow2/lib/util/qemu-thread-common.h +@@ -0,0 +1,54 @@ ++/* ++ * Common qemu-thread implementation header file. ++ * ++ * Copyright Red Hat, Inc. 2018 ++ * ++ * Authors: ++ * Peter Xu , ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_THREAD_COMMON_H ++#define QEMU_THREAD_COMMON_H ++ ++#include "qemu/thread.h" ++#include "trace.h" ++ ++static inline void qemu_mutex_post_init(QemuMutex *mutex) ++{ ++#ifdef CONFIG_DEBUG_MUTEX ++ mutex->file = NULL; ++ mutex->line = 0; ++#endif ++ mutex->initialized = true; ++} ++ ++static inline void qemu_mutex_pre_lock(QemuMutex *mutex, ++ const char *file, int line) ++{ ++ trace_qemu_mutex_lock(mutex, file, line); ++} ++ ++static inline void qemu_mutex_post_lock(QemuMutex *mutex, ++ const char *file, int line) ++{ ++#ifdef CONFIG_DEBUG_MUTEX ++ mutex->file = file; ++ mutex->line = line; ++#endif ++ trace_qemu_mutex_locked(mutex, file, line); ++} ++ ++static inline void qemu_mutex_pre_unlock(QemuMutex *mutex, ++ const char *file, int line) ++{ ++#ifdef CONFIG_DEBUG_MUTEX ++ mutex->file = NULL; ++ mutex->line = 0; ++#endif ++ trace_qemu_mutex_unlock(mutex, file, line); ++} ++ ++#endif +diff --git a/qcow2/lib/util/qemu-thread-posix.c b/qcow2/lib/util/qemu-thread-posix.c +new file mode 100644 +index 00000000..b2e26e21 +--- /dev/null ++++ b/qcow2/lib/util/qemu-thread-posix.c +@@ -0,0 +1,686 @@ ++/* ++ * Wrappers around mutex/cond/thread functions ++ * ++ * Copyright Red Hat, Inc. 2009 ++ * ++ * Author: ++ * Marcelo Tosatti ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++#include "qemu/osdep.h" ++#include "qemu/thread.h" ++#include "qemu/atomic.h" ++#include "qemu/notify.h" ++#include "qemu-thread-common.h" ++#include "qemu/tsan.h" ++#include "qemu/bitmap.h" ++ ++#ifdef CONFIG_PTHREAD_SET_NAME_NP ++#include ++#endif ++ ++static bool name_threads; ++ ++void qemu_thread_naming(bool enable) ++{ ++ name_threads = enable; ++ ++#if !defined CONFIG_PTHREAD_SETNAME_NP_W_TID && \ ++ !defined CONFIG_PTHREAD_SETNAME_NP_WO_TID && \ ++ !defined CONFIG_PTHREAD_SET_NAME_NP ++ /* This is a debugging option, not fatal */ ++ if (enable) { ++ fprintf(stderr, "qemu: thread naming not supported on this host\n"); ++ } ++#endif ++} ++ ++static void error_exit(int err, const char *msg) ++{ ++ fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err)); ++ abort(); ++} ++ ++static inline clockid_t qemu_timedwait_clockid(void) ++{ ++#ifdef CONFIG_PTHREAD_CONDATTR_SETCLOCK ++ return CLOCK_MONOTONIC; ++#else ++ return CLOCK_REALTIME; ++#endif ++} ++ ++static void compute_abs_deadline(struct timespec *ts, int ms) ++{ ++ clock_gettime(qemu_timedwait_clockid(), ts); ++ ts->tv_nsec += (ms % 1000) * 1000000; ++ ts->tv_sec += ms / 1000; ++ if (ts->tv_nsec >= 1000000000) { ++ ts->tv_sec++; ++ ts->tv_nsec -= 1000000000; ++ } ++} ++ ++void qemu_mutex_init(QemuMutex *mutex) ++{ ++ int err; ++ ++ err = pthread_mutex_init(&mutex->lock, NULL); ++ if (err) ++ error_exit(err, __func__); ++ qemu_mutex_post_init(mutex); ++} ++ ++void qemu_mutex_destroy(QemuMutex *mutex) ++{ ++ int err; ++ ++ assert(mutex->initialized); ++ mutex->initialized = false; ++ err = pthread_mutex_destroy(&mutex->lock); ++ if (err) ++ error_exit(err, __func__); ++} ++ ++void qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, const int line) ++{ ++ int err; ++ ++ assert(mutex->initialized); ++ qemu_mutex_pre_lock(mutex, file, line); ++ err = pthread_mutex_lock(&mutex->lock); ++ if (err) ++ error_exit(err, __func__); ++ qemu_mutex_post_lock(mutex, file, line); ++} ++ ++int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line) ++{ ++ int err; ++ ++ assert(mutex->initialized); ++ err = pthread_mutex_trylock(&mutex->lock); ++ if (err == 0) { ++ qemu_mutex_post_lock(mutex, file, line); ++ return 0; ++ } ++ if (err != EBUSY) { ++ error_exit(err, __func__); ++ } ++ return -EBUSY; ++} ++ ++void qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, const int line) ++{ ++ int err; ++ ++ assert(mutex->initialized); ++ qemu_mutex_pre_unlock(mutex, file, line); ++ err = pthread_mutex_unlock(&mutex->lock); ++ if (err) ++ error_exit(err, __func__); ++} ++ ++void qemu_rec_mutex_init(QemuRecMutex *mutex) ++{ ++ int err; ++ pthread_mutexattr_t attr; ++ ++ pthread_mutexattr_init(&attr); ++ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); ++ err = pthread_mutex_init(&mutex->m.lock, &attr); ++ pthread_mutexattr_destroy(&attr); ++ if (err) { ++ error_exit(err, __func__); ++ } ++ mutex->m.initialized = true; ++} ++ ++void qemu_rec_mutex_destroy(QemuRecMutex *mutex) ++{ ++ qemu_mutex_destroy(&mutex->m); ++} ++ ++void qemu_rec_mutex_lock_impl(QemuRecMutex *mutex, const char *file, int line) ++{ ++ qemu_mutex_lock_impl(&mutex->m, file, line); ++} ++ ++int qemu_rec_mutex_trylock_impl(QemuRecMutex *mutex, const char *file, int line) ++{ ++ return qemu_mutex_trylock_impl(&mutex->m, file, line); ++} ++ ++void qemu_rec_mutex_unlock_impl(QemuRecMutex *mutex, const char *file, int line) ++{ ++ qemu_mutex_unlock_impl(&mutex->m, file, line); ++} ++ ++void qemu_cond_init(QemuCond *cond) ++{ ++ pthread_condattr_t attr; ++ int err; ++ ++ err = pthread_condattr_init(&attr); ++ if (err) { ++ error_exit(err, __func__); ++ } ++#ifdef CONFIG_PTHREAD_CONDATTR_SETCLOCK ++ err = pthread_condattr_setclock(&attr, qemu_timedwait_clockid()); ++ if (err) { ++ error_exit(err, __func__); ++ } ++#endif ++ err = pthread_cond_init(&cond->cond, &attr); ++ if (err) { ++ error_exit(err, __func__); ++ } ++ err = pthread_condattr_destroy(&attr); ++ if (err) { ++ error_exit(err, __func__); ++ } ++ cond->initialized = true; ++} ++ ++void qemu_cond_destroy(QemuCond *cond) ++{ ++ int err; ++ ++ assert(cond->initialized); ++ cond->initialized = false; ++ err = pthread_cond_destroy(&cond->cond); ++ if (err) ++ error_exit(err, __func__); ++} ++ ++void qemu_cond_signal(QemuCond *cond) ++{ ++ int err; ++ ++ assert(cond->initialized); ++ err = pthread_cond_signal(&cond->cond); ++ if (err) ++ error_exit(err, __func__); ++} ++ ++void qemu_cond_broadcast(QemuCond *cond) ++{ ++ int err; ++ ++ assert(cond->initialized); ++ err = pthread_cond_broadcast(&cond->cond); ++ if (err) ++ error_exit(err, __func__); ++} ++ ++void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, const char *file, const int line) ++{ ++ int err; ++ ++ assert(cond->initialized); ++ qemu_mutex_pre_unlock(mutex, file, line); ++ err = pthread_cond_wait(&cond->cond, &mutex->lock); ++ qemu_mutex_post_lock(mutex, file, line); ++ if (err) ++ error_exit(err, __func__); ++} ++ ++static bool TSA_NO_TSA ++qemu_cond_timedwait_ts(QemuCond *cond, QemuMutex *mutex, struct timespec *ts, ++ const char *file, const int line) ++{ ++ int err; ++ ++ assert(cond->initialized); ++ trace_qemu_mutex_unlock(mutex, file, line); ++ err = pthread_cond_timedwait(&cond->cond, &mutex->lock, ts); ++ trace_qemu_mutex_locked(mutex, file, line); ++ if (err && err != ETIMEDOUT) { ++ error_exit(err, __func__); ++ } ++ return err != ETIMEDOUT; ++} ++ ++bool qemu_cond_timedwait_impl(QemuCond *cond, QemuMutex *mutex, int ms, ++ const char *file, const int line) ++{ ++ struct timespec ts; ++ ++ compute_abs_deadline(&ts, ms); ++ return qemu_cond_timedwait_ts(cond, mutex, &ts, file, line); ++} ++ ++void qemu_sem_init(QemuSemaphore *sem, int init) ++{ ++ qemu_mutex_init(&sem->mutex); ++ qemu_cond_init(&sem->cond); ++ ++ if (init < 0) { ++ error_exit(EINVAL, __func__); ++ } ++ sem->count = init; ++} ++ ++void qemu_sem_destroy(QemuSemaphore *sem) ++{ ++ qemu_cond_destroy(&sem->cond); ++ qemu_mutex_destroy(&sem->mutex); ++} ++ ++void qemu_sem_post(QemuSemaphore *sem) ++{ ++ qemu_mutex_lock(&sem->mutex); ++ if (sem->count == UINT_MAX) { ++ error_exit(EINVAL, __func__); ++ } else { ++ sem->count++; ++ qemu_cond_signal(&sem->cond); ++ } ++ qemu_mutex_unlock(&sem->mutex); ++} ++ ++int qemu_sem_timedwait(QemuSemaphore *sem, int ms) ++{ ++ bool rc = true; ++ struct timespec ts; ++ ++ compute_abs_deadline(&ts, ms); ++ qemu_mutex_lock(&sem->mutex); ++ while (sem->count == 0) { ++ if (ms == 0) { ++ rc = false; ++ } else { ++ rc = qemu_cond_timedwait_ts(&sem->cond, &sem->mutex, &ts, ++ __FILE__, __LINE__); ++ } ++ if (!rc) { /* timeout */ ++ break; ++ } ++ } ++ if (rc) { ++ --sem->count; ++ } ++ qemu_mutex_unlock(&sem->mutex); ++ return (rc ? 0 : -1); ++} ++ ++void qemu_sem_wait(QemuSemaphore *sem) ++{ ++ qemu_mutex_lock(&sem->mutex); ++ while (sem->count == 0) { ++ qemu_cond_wait(&sem->cond, &sem->mutex); ++ } ++ --sem->count; ++ qemu_mutex_unlock(&sem->mutex); ++} ++ ++#ifdef __linux__ ++#include "qemu/futex.h" ++#else ++static inline void qemu_futex_wake(QemuEvent *ev, int n) ++{ ++ assert(ev->initialized); ++ pthread_mutex_lock(&ev->lock); ++ if (n == 1) { ++ pthread_cond_signal(&ev->cond); ++ } else { ++ pthread_cond_broadcast(&ev->cond); ++ } ++ pthread_mutex_unlock(&ev->lock); ++} ++ ++static inline void qemu_futex_wait(QemuEvent *ev, unsigned val) ++{ ++ assert(ev->initialized); ++ pthread_mutex_lock(&ev->lock); ++ if (ev->value == val) { ++ pthread_cond_wait(&ev->cond, &ev->lock); ++ } ++ pthread_mutex_unlock(&ev->lock); ++} ++#endif ++ ++/* Valid transitions: ++ * - free->set, when setting the event ++ * - busy->set, when setting the event, followed by qemu_futex_wake ++ * - set->free, when resetting the event ++ * - free->busy, when waiting ++ * ++ * set->busy does not happen (it can be observed from the outside but ++ * it really is set->free->busy). ++ * ++ * busy->free provably cannot happen; to enforce it, the set->free transition ++ * is done with an OR, which becomes a no-op if the event has concurrently ++ * transitioned to free or busy. ++ */ ++ ++#define EV_SET 0 ++#define EV_FREE 1 ++#define EV_BUSY -1 ++ ++void qemu_event_init(QemuEvent *ev, bool init) ++{ ++#ifndef __linux__ ++ pthread_mutex_init(&ev->lock, NULL); ++ pthread_cond_init(&ev->cond, NULL); ++#endif ++ ++ ev->value = (init ? EV_SET : EV_FREE); ++ ev->initialized = true; ++} ++ ++void qemu_event_destroy(QemuEvent *ev) ++{ ++ assert(ev->initialized); ++ ev->initialized = false; ++#ifndef __linux__ ++ pthread_mutex_destroy(&ev->lock); ++ pthread_cond_destroy(&ev->cond); ++#endif ++} ++ ++void qemu_event_set(QemuEvent *ev) ++{ ++ assert(ev->initialized); ++ ++ /* ++ * Pairs with both qemu_event_reset() and qemu_event_wait(). ++ * ++ * qemu_event_set has release semantics, but because it *loads* ++ * ev->value we need a full memory barrier here. ++ */ ++ smp_mb(); ++ if (qatomic_read(&ev->value) != EV_SET) { ++ int old = qatomic_xchg(&ev->value, EV_SET); ++ ++ /* Pairs with memory barrier in kernel futex_wait system call. */ ++ smp_mb__after_rmw(); ++ if (old == EV_BUSY) { ++ /* There were waiters, wake them up. */ ++ qemu_futex_wake(ev, INT_MAX); ++ } ++ } ++} ++ ++void qemu_event_reset(QemuEvent *ev) ++{ ++ assert(ev->initialized); ++ ++ /* ++ * If there was a concurrent reset (or even reset+wait), ++ * do nothing. Otherwise change EV_SET->EV_FREE. ++ */ ++ qatomic_or(&ev->value, EV_FREE); ++ ++ /* ++ * Order reset before checking the condition in the caller. ++ * Pairs with the first memory barrier in qemu_event_set(). ++ */ ++ smp_mb__after_rmw(); ++} ++ ++void qemu_event_wait(QemuEvent *ev) ++{ ++ unsigned value; ++ ++ assert(ev->initialized); ++ ++ /* ++ * qemu_event_wait must synchronize with qemu_event_set even if it does ++ * not go down the slow path, so this load-acquire is needed that ++ * synchronizes with the first memory barrier in qemu_event_set(). ++ * ++ * If we do go down the slow path, there is no requirement at all: we ++ * might miss a qemu_event_set() here but ultimately the memory barrier in ++ * qemu_futex_wait() will ensure the check is done correctly. ++ */ ++ value = qatomic_load_acquire(&ev->value); ++ if (value != EV_SET) { ++ if (value == EV_FREE) { ++ /* ++ * Leave the event reset and tell qemu_event_set that there are ++ * waiters. No need to retry, because there cannot be a concurrent ++ * busy->free transition. After the CAS, the event will be either ++ * set or busy. ++ * ++ * This cmpxchg doesn't have particular ordering requirements if it ++ * succeeds (moving the store earlier can only cause qemu_event_set() ++ * to issue _more_ wakeups), the failing case needs acquire semantics ++ * like the load above. ++ */ ++ if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) { ++ return; ++ } ++ } ++ ++ /* ++ * This is the final check for a concurrent set, so it does need ++ * a smp_mb() pairing with the second barrier of qemu_event_set(). ++ * The barrier is inside the FUTEX_WAIT system call. ++ */ ++ qemu_futex_wait(ev, EV_BUSY); ++ } ++} ++ ++static __thread NotifierList thread_exit; ++ ++/* ++ * Note that in this implementation you can register a thread-exit ++ * notifier for the main thread, but it will never be called. ++ * This is OK because main thread exit can only happen when the ++ * entire process is exiting, and the API allows notifiers to not ++ * be called on process exit. ++ */ ++void qemu_thread_atexit_add(Notifier *notifier) ++{ ++ notifier_list_add(&thread_exit, notifier); ++} ++ ++void qemu_thread_atexit_remove(Notifier *notifier) ++{ ++ notifier_remove(notifier); ++} ++ ++static void qemu_thread_atexit_notify(void *arg) ++{ ++ /* ++ * Called when non-main thread exits (via qemu_thread_exit() ++ * or by returning from its start routine.) ++ */ ++ notifier_list_notify(&thread_exit, NULL); ++} ++ ++typedef struct { ++ void *(*start_routine)(void *); ++ void *arg; ++ char *name; ++} QemuThreadArgs; ++ ++static void *qemu_thread_start(void *args) ++{ ++ QemuThreadArgs *qemu_thread_args = args; ++ void *(*start_routine)(void *) = qemu_thread_args->start_routine; ++ void *arg = qemu_thread_args->arg; ++ void *r; ++ ++ /* Attempt to set the threads name; note that this is for debug, so ++ * we're not going to fail if we can't set it. ++ */ ++ if (name_threads && qemu_thread_args->name) { ++# if defined(CONFIG_PTHREAD_SETNAME_NP_W_TID) ++ pthread_setname_np(pthread_self(), qemu_thread_args->name); ++# elif defined(CONFIG_PTHREAD_SETNAME_NP_WO_TID) ++ pthread_setname_np(qemu_thread_args->name); ++# elif defined(CONFIG_PTHREAD_SET_NAME_NP) ++ pthread_set_name_np(pthread_self(), qemu_thread_args->name); ++# endif ++ } ++ QEMU_TSAN_ANNOTATE_THREAD_NAME(qemu_thread_args->name); ++ g_free(qemu_thread_args->name); ++ g_free(qemu_thread_args); ++ ++ /* ++ * GCC 11 with glibc 2.17 on PowerPC reports ++ * ++ * qemu-thread-posix.c:540:5: error: ‘__sigsetjmp’ accessing 656 bytes ++ * in a region of size 528 [-Werror=stringop-overflow=] ++ * 540 | pthread_cleanup_push(qemu_thread_atexit_notify, NULL); ++ * | ^~~~~~~~~~~~~~~~~~~~ ++ * ++ * which is clearly nonsense. ++ */ ++#pragma GCC diagnostic push ++#ifndef __clang__ ++#pragma GCC diagnostic ignored "-Wstringop-overflow" ++#endif ++ ++ pthread_cleanup_push(qemu_thread_atexit_notify, NULL); ++ r = start_routine(arg); ++ pthread_cleanup_pop(1); ++ ++#pragma GCC diagnostic pop ++ ++ return r; ++} ++ ++void qemu_thread_create(QemuThread *thread, const char *name, ++ void *(*start_routine)(void*), ++ void *arg, int mode) ++{ ++ sigset_t set, oldset; ++ int err; ++ pthread_attr_t attr; ++ QemuThreadArgs *qemu_thread_args; ++ ++ err = pthread_attr_init(&attr); ++ if (err) { ++ error_exit(err, __func__); ++ } ++ ++ if (mode == QEMU_THREAD_DETACHED) { ++ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ++ } ++ ++ /* Leave signal handling to the iothread. */ ++ sigfillset(&set); ++ /* Blocking the signals can result in undefined behaviour. */ ++ sigdelset(&set, SIGSEGV); ++ sigdelset(&set, SIGFPE); ++ sigdelset(&set, SIGILL); ++ /* TODO avoid SIGBUS loss on macOS */ ++ pthread_sigmask(SIG_SETMASK, &set, &oldset); ++ ++ qemu_thread_args = g_new0(QemuThreadArgs, 1); ++ qemu_thread_args->name = g_strdup(name); ++ qemu_thread_args->start_routine = start_routine; ++ qemu_thread_args->arg = arg; ++ ++ err = pthread_create(&thread->thread, &attr, ++ qemu_thread_start, qemu_thread_args); ++ ++ if (err) ++ error_exit(err, __func__); ++ ++ pthread_sigmask(SIG_SETMASK, &oldset, NULL); ++ ++ pthread_attr_destroy(&attr); ++} ++ ++int qemu_thread_set_affinity(QemuThread *thread, unsigned long *host_cpus, ++ unsigned long nbits) ++{ ++#if defined(CONFIG_PTHREAD_AFFINITY_NP) ++ const size_t setsize = CPU_ALLOC_SIZE(nbits); ++ unsigned long value; ++ cpu_set_t *cpuset; ++ int err; ++ ++ cpuset = CPU_ALLOC(nbits); ++ g_assert(cpuset); ++ ++ CPU_ZERO_S(setsize, cpuset); ++ value = find_first_bit(host_cpus, nbits); ++ while (value < nbits) { ++ CPU_SET_S(value, setsize, cpuset); ++ value = find_next_bit(host_cpus, nbits, value + 1); ++ } ++ ++ err = pthread_setaffinity_np(thread->thread, setsize, cpuset); ++ CPU_FREE(cpuset); ++ return err; ++#else ++ return -ENOSYS; ++#endif ++} ++ ++int qemu_thread_get_affinity(QemuThread *thread, unsigned long **host_cpus, ++ unsigned long *nbits) ++{ ++#if defined(CONFIG_PTHREAD_AFFINITY_NP) ++ unsigned long tmpbits; ++ cpu_set_t *cpuset; ++ size_t setsize; ++ int i, err; ++ ++ tmpbits = CPU_SETSIZE; ++ while (true) { ++ setsize = CPU_ALLOC_SIZE(tmpbits); ++ cpuset = CPU_ALLOC(tmpbits); ++ g_assert(cpuset); ++ ++ err = pthread_getaffinity_np(thread->thread, setsize, cpuset); ++ if (err) { ++ CPU_FREE(cpuset); ++ if (err != -EINVAL) { ++ return err; ++ } ++ tmpbits *= 2; ++ } else { ++ break; ++ } ++ } ++ ++ /* Convert the result into a proper bitmap. */ ++ *nbits = tmpbits; ++ *host_cpus = bitmap_new(tmpbits); ++ for (i = 0; i < tmpbits; i++) { ++ if (CPU_ISSET(i, cpuset)) { ++ set_bit(i, *host_cpus); ++ } ++ } ++ CPU_FREE(cpuset); ++ return 0; ++#else ++ return -ENOSYS; ++#endif ++} ++ ++void qemu_thread_get_self(QemuThread *thread) ++{ ++ thread->thread = pthread_self(); ++} ++ ++bool qemu_thread_is_self(QemuThread *thread) ++{ ++ return pthread_equal(pthread_self(), thread->thread); ++} ++ ++void qemu_thread_exit(void *retval) ++{ ++ pthread_exit(retval); ++} ++ ++void *qemu_thread_join(QemuThread *thread) ++{ ++ int err; ++ void *ret; ++ ++ err = pthread_join(thread->thread, &ret); ++ if (err) { ++ error_exit(err, __func__); ++ } ++ return ret; ++} +diff --git a/qcow2/lib/util/qemu-timer-common.c b/qcow2/lib/util/qemu-timer-common.c +new file mode 100644 +index 00000000..cc1326f7 +--- /dev/null ++++ b/qcow2/lib/util/qemu-timer-common.c +@@ -0,0 +1,63 @@ ++/* ++ * QEMU System Emulator ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#include "qemu/osdep.h" ++#include "qemu/timer.h" ++ ++/***********************************************************/ ++/* real time host monotonic timer */ ++ ++int64_t clock_start; ++ ++#ifdef _WIN32 ++ ++int64_t clock_freq; ++ ++static void __attribute__((constructor)) init_get_clock(void) ++{ ++ LARGE_INTEGER freq; ++ int ret; ++ ret = QueryPerformanceFrequency(&freq); ++ if (ret == 0) { ++ fprintf(stderr, "Could not calibrate ticks\n"); ++ exit(1); ++ } ++ clock_freq = freq.QuadPart; ++ clock_start = get_clock(); ++} ++ ++#else ++ ++int use_rt_clock; ++ ++static void __attribute__((constructor)) init_get_clock(void) ++{ ++ struct timespec ts; ++ ++ use_rt_clock = 0; ++ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { ++ use_rt_clock = 1; ++ } ++ clock_start = get_clock(); ++} ++#endif +diff --git a/qcow2/lib/util/qemu-timer.c b/qcow2/lib/util/qemu-timer.c +new file mode 100644 +index 00000000..6b1533bc +--- /dev/null ++++ b/qcow2/lib/util/qemu-timer.c +@@ -0,0 +1,713 @@ ++/* ++ * QEMU System Emulator ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/main-loop.h" ++#include "qemu/timer.h" ++#include "qemu/lockable.h" ++#include "sysemu/cpu-timers.h" ++#include "sysemu/replay.h" ++#include "sysemu/cpus.h" ++ ++#ifdef CONFIG_POSIX ++#include ++#endif ++ ++#ifdef CONFIG_PPOLL ++#include ++#endif ++ ++#ifdef CONFIG_PRCTL_PR_SET_TIMERSLACK ++#include ++#endif ++ ++/***********************************************************/ ++/* timers */ ++ ++typedef struct QEMUClock { ++ /* We rely on BQL to protect the timerlists */ ++ QLIST_HEAD(, QEMUTimerList) timerlists; ++ ++ QEMUClockType type; ++ bool enabled; ++} QEMUClock; ++ ++QEMUTimerListGroup main_loop_tlg; ++static QEMUClock qemu_clocks[QEMU_CLOCK_MAX]; ++ ++/* A QEMUTimerList is a list of timers attached to a clock. More ++ * than one QEMUTimerList can be attached to each clock, for instance ++ * used by different AioContexts / threads. Each clock also has ++ * a list of the QEMUTimerLists associated with it, in order that ++ * reenabling the clock can call all the notifiers. ++ */ ++ ++struct QEMUTimerList { ++ QEMUClock *clock; ++ QemuMutex active_timers_lock; ++ QEMUTimer *active_timers; ++ QLIST_ENTRY(QEMUTimerList) list; ++ QEMUTimerListNotifyCB *notify_cb; ++ void *notify_opaque; ++ ++ /* lightweight method to mark the end of timerlist's running */ ++ QemuEvent timers_done_ev; ++}; ++ ++/** ++ * qemu_clock_ptr: ++ * @type: type of clock ++ * ++ * Translate a clock type into a pointer to QEMUClock object. ++ * ++ * Returns: a pointer to the QEMUClock object ++ */ ++static inline QEMUClock *qemu_clock_ptr(QEMUClockType type) ++{ ++ return &qemu_clocks[type]; ++} ++ ++static bool timer_expired_ns(QEMUTimer *timer_head, int64_t current_time) ++{ ++ return timer_head && (timer_head->expire_time <= current_time); ++} ++ ++QEMUTimerList *timerlist_new(QEMUClockType type, ++ QEMUTimerListNotifyCB *cb, ++ void *opaque) ++{ ++ QEMUTimerList *timer_list; ++ QEMUClock *clock = qemu_clock_ptr(type); ++ ++ timer_list = g_new0(QEMUTimerList, 1); ++ qemu_event_init(&timer_list->timers_done_ev, true); ++ timer_list->clock = clock; ++ timer_list->notify_cb = cb; ++ timer_list->notify_opaque = opaque; ++ qemu_mutex_init(&timer_list->active_timers_lock); ++ QLIST_INSERT_HEAD(&clock->timerlists, timer_list, list); ++ return timer_list; ++} ++ ++void timerlist_free(QEMUTimerList *timer_list) ++{ ++ assert(!timerlist_has_timers(timer_list)); ++ if (timer_list->clock) { ++ QLIST_REMOVE(timer_list, list); ++ } ++ qemu_mutex_destroy(&timer_list->active_timers_lock); ++ g_free(timer_list); ++} ++ ++static void qemu_clock_init(QEMUClockType type, QEMUTimerListNotifyCB *notify_cb) ++{ ++ QEMUClock *clock = qemu_clock_ptr(type); ++ ++ /* Assert that the clock of type TYPE has not been initialized yet. */ ++ assert(main_loop_tlg.tl[type] == NULL); ++ ++ clock->type = type; ++ clock->enabled = (type == QEMU_CLOCK_VIRTUAL ? false : true); ++ QLIST_INIT(&clock->timerlists); ++ main_loop_tlg.tl[type] = timerlist_new(type, notify_cb, NULL); ++} ++ ++bool qemu_clock_use_for_deadline(QEMUClockType type) ++{ ++ return !(icount_enabled() && (type == QEMU_CLOCK_VIRTUAL)); ++} ++ ++void qemu_clock_notify(QEMUClockType type) ++{ ++ QEMUTimerList *timer_list; ++ QEMUClock *clock = qemu_clock_ptr(type); ++ QLIST_FOREACH(timer_list, &clock->timerlists, list) { ++ timerlist_notify(timer_list); ++ } ++} ++ ++/* Disabling the clock will wait for related timerlists to stop ++ * executing qemu_run_timers. Thus, this functions should not ++ * be used from the callback of a timer that is based on @clock. ++ * Doing so would cause a deadlock. ++ * ++ * Caller should hold BQL. ++ */ ++void qemu_clock_enable(QEMUClockType type, bool enabled) ++{ ++ QEMUClock *clock = qemu_clock_ptr(type); ++ QEMUTimerList *tl; ++ bool old = clock->enabled; ++ clock->enabled = enabled; ++ if (enabled && !old) { ++ qemu_clock_notify(type); ++ } else if (!enabled && old) { ++ QLIST_FOREACH(tl, &clock->timerlists, list) { ++ qemu_event_wait(&tl->timers_done_ev); ++ } ++ } ++} ++ ++bool timerlist_has_timers(QEMUTimerList *timer_list) ++{ ++ return !!qatomic_read(&timer_list->active_timers); ++} ++ ++bool qemu_clock_has_timers(QEMUClockType type) ++{ ++ return timerlist_has_timers( ++ main_loop_tlg.tl[type]); ++} ++ ++bool timerlist_expired(QEMUTimerList *timer_list) ++{ ++ int64_t expire_time; ++ ++ if (!qatomic_read(&timer_list->active_timers)) { ++ return false; ++ } ++ ++ WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { ++ if (!timer_list->active_timers) { ++ return false; ++ } ++ expire_time = timer_list->active_timers->expire_time; ++ } ++ ++ return expire_time <= qemu_clock_get_ns(timer_list->clock->type); ++} ++ ++bool qemu_clock_expired(QEMUClockType type) ++{ ++ return timerlist_expired( ++ main_loop_tlg.tl[type]); ++} ++ ++/* ++ * As above, but return -1 for no deadline, and do not cap to 2^32 ++ * as we know the result is always positive. ++ */ ++ ++int64_t timerlist_deadline_ns(QEMUTimerList *timer_list) ++{ ++ int64_t delta; ++ int64_t expire_time; ++ ++ if (!qatomic_read(&timer_list->active_timers)) { ++ return -1; ++ } ++ ++ if (!timer_list->clock->enabled) { ++ return -1; ++ } ++ ++ /* The active timers list may be modified before the caller uses our return ++ * value but ->notify_cb() is called when the deadline changes. Therefore ++ * the caller should notice the change and there is no race condition. ++ */ ++ WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { ++ if (!timer_list->active_timers) { ++ return -1; ++ } ++ expire_time = timer_list->active_timers->expire_time; ++ } ++ ++ delta = expire_time - qemu_clock_get_ns(timer_list->clock->type); ++ ++ if (delta <= 0) { ++ return 0; ++ } ++ ++ return delta; ++} ++ ++/* Calculate the soonest deadline across all timerlists attached ++ * to the clock. This is used for the icount timeout so we ++ * ignore whether or not the clock should be used in deadline ++ * calculations. ++ */ ++int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask) ++{ ++ int64_t deadline = -1; ++ int64_t delta; ++ int64_t expire_time; ++ QEMUTimer *ts; ++ QEMUTimerList *timer_list; ++ QEMUClock *clock = qemu_clock_ptr(type); ++ ++ if (!clock->enabled) { ++ return -1; ++ } ++ ++ QLIST_FOREACH(timer_list, &clock->timerlists, list) { ++ if (!qatomic_read(&timer_list->active_timers)) { ++ continue; ++ } ++ qemu_mutex_lock(&timer_list->active_timers_lock); ++ ts = timer_list->active_timers; ++ /* Skip all external timers */ ++ while (ts && (ts->attributes & ~attr_mask)) { ++ ts = ts->next; ++ } ++ if (!ts) { ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ continue; ++ } ++ expire_time = ts->expire_time; ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ ++ delta = expire_time - qemu_clock_get_ns(type); ++ if (delta <= 0) { ++ delta = 0; ++ } ++ deadline = qemu_soonest_timeout(deadline, delta); ++ } ++ return deadline; ++} ++ ++QEMUClockType timerlist_get_clock(QEMUTimerList *timer_list) ++{ ++ return timer_list->clock->type; ++} ++ ++QEMUTimerList *qemu_clock_get_main_loop_timerlist(QEMUClockType type) ++{ ++ return main_loop_tlg.tl[type]; ++} ++ ++void timerlist_notify(QEMUTimerList *timer_list) ++{ ++ if (timer_list->notify_cb) { ++ timer_list->notify_cb(timer_list->notify_opaque, timer_list->clock->type); ++ } else { ++ qemu_notify_event(); ++ } ++} ++ ++/* Transition function to convert a nanosecond timeout to ms ++ * This is used where a system does not support ppoll ++ */ ++int qemu_timeout_ns_to_ms(int64_t ns) ++{ ++ int64_t ms; ++ if (ns < 0) { ++ return -1; ++ } ++ ++ if (!ns) { ++ return 0; ++ } ++ ++ /* Always round up, because it's better to wait too long than to wait too ++ * little and effectively busy-wait ++ */ ++ ms = DIV_ROUND_UP(ns, SCALE_MS); ++ ++ /* To avoid overflow problems, limit this to 2^31, i.e. approx 25 days */ ++ return MIN(ms, INT32_MAX); ++} ++ ++ ++/* qemu implementation of g_poll which uses a nanosecond timeout but is ++ * otherwise identical to g_poll ++ */ ++int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t timeout) ++{ ++#ifdef CONFIG_PPOLL ++ if (timeout < 0) { ++ return ppoll((struct pollfd *)fds, nfds, NULL, NULL); ++ } else { ++ struct timespec ts; ++ int64_t tvsec = timeout / 1000000000LL; ++ /* Avoid possibly overflowing and specifying a negative number of ++ * seconds, which would turn a very long timeout into a busy-wait. ++ */ ++ if (tvsec > (int64_t)INT32_MAX) { ++ tvsec = INT32_MAX; ++ } ++ ts.tv_sec = tvsec; ++ ts.tv_nsec = timeout % 1000000000LL; ++ return ppoll((struct pollfd *)fds, nfds, &ts, NULL); ++ } ++#else ++ return g_poll(fds, nfds, qemu_timeout_ns_to_ms(timeout)); ++#endif ++} ++ ++ ++void timer_init_full(QEMUTimer *ts, ++ QEMUTimerListGroup *timer_list_group, QEMUClockType type, ++ int scale, int attributes, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ if (!timer_list_group) { ++ timer_list_group = &main_loop_tlg; ++ } ++ ts->timer_list = timer_list_group->tl[type]; ++ ts->cb = cb; ++ ts->opaque = opaque; ++ ts->scale = scale; ++ ts->attributes = attributes; ++ ts->expire_time = -1; ++} ++ ++void timer_deinit(QEMUTimer *ts) ++{ ++ assert(ts->expire_time == -1); ++ ts->timer_list = NULL; ++} ++ ++static void timer_del_locked(QEMUTimerList *timer_list, QEMUTimer *ts) ++{ ++ QEMUTimer **pt, *t; ++ ++ ts->expire_time = -1; ++ pt = &timer_list->active_timers; ++ for(;;) { ++ t = *pt; ++ if (!t) ++ break; ++ if (t == ts) { ++ qatomic_set(pt, t->next); ++ break; ++ } ++ pt = &t->next; ++ } ++} ++ ++static bool timer_mod_ns_locked(QEMUTimerList *timer_list, ++ QEMUTimer *ts, int64_t expire_time) ++{ ++ QEMUTimer **pt, *t; ++ ++ /* add the timer in the sorted list */ ++ pt = &timer_list->active_timers; ++ for (;;) { ++ t = *pt; ++ if (!timer_expired_ns(t, expire_time)) { ++ break; ++ } ++ pt = &t->next; ++ } ++ ts->expire_time = MAX(expire_time, 0); ++ ts->next = *pt; ++ qatomic_set(pt, ts); ++ ++ return pt == &timer_list->active_timers; ++} ++ ++static void timerlist_rearm(QEMUTimerList *timer_list) ++{ ++ /* Interrupt execution to force deadline recalculation. */ ++ if (icount_enabled() && timer_list->clock->type == QEMU_CLOCK_VIRTUAL) { ++ icount_start_warp_timer(); ++ } ++ timerlist_notify(timer_list); ++} ++ ++/* stop a timer, but do not dealloc it */ ++void timer_del(QEMUTimer *ts) ++{ ++ QEMUTimerList *timer_list = ts->timer_list; ++ ++ if (timer_list) { ++ qemu_mutex_lock(&timer_list->active_timers_lock); ++ timer_del_locked(timer_list, ts); ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ } ++} ++ ++/* modify the current timer so that it will be fired when current_time ++ >= expire_time. The corresponding callback will be called. */ ++void timer_mod_ns(QEMUTimer *ts, int64_t expire_time) ++{ ++ QEMUTimerList *timer_list = ts->timer_list; ++ bool rearm; ++ ++ qemu_mutex_lock(&timer_list->active_timers_lock); ++ timer_del_locked(timer_list, ts); ++ rearm = timer_mod_ns_locked(timer_list, ts, expire_time); ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ ++ if (rearm) { ++ timerlist_rearm(timer_list); ++ } ++} ++ ++/* modify the current timer so that it will be fired when current_time ++ >= expire_time or the current deadline, whichever comes earlier. ++ The corresponding callback will be called. */ ++void timer_mod_anticipate_ns(QEMUTimer *ts, int64_t expire_time) ++{ ++ QEMUTimerList *timer_list = ts->timer_list; ++ bool rearm; ++ ++ WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { ++ if (ts->expire_time == -1 || ts->expire_time > expire_time) { ++ if (ts->expire_time != -1) { ++ timer_del_locked(timer_list, ts); ++ } ++ rearm = timer_mod_ns_locked(timer_list, ts, expire_time); ++ } else { ++ rearm = false; ++ } ++ } ++ if (rearm) { ++ timerlist_rearm(timer_list); ++ } ++} ++ ++void timer_mod(QEMUTimer *ts, int64_t expire_time) ++{ ++ timer_mod_ns(ts, expire_time * ts->scale); ++} ++ ++void timer_mod_anticipate(QEMUTimer *ts, int64_t expire_time) ++{ ++ timer_mod_anticipate_ns(ts, expire_time * ts->scale); ++} ++ ++bool timer_pending(QEMUTimer *ts) ++{ ++ return ts->expire_time >= 0; ++} ++ ++bool timer_expired(QEMUTimer *timer_head, int64_t current_time) ++{ ++ return timer_expired_ns(timer_head, current_time * timer_head->scale); ++} ++ ++bool timerlist_run_timers(QEMUTimerList *timer_list) ++{ ++ QEMUTimer *ts; ++ int64_t current_time; ++ bool progress = false; ++ QEMUTimerCB *cb; ++ void *opaque; ++ ++ if (!qatomic_read(&timer_list->active_timers)) { ++ return false; ++ } ++ ++ qemu_event_reset(&timer_list->timers_done_ev); ++ if (!timer_list->clock->enabled) { ++ goto out; ++ } ++ ++ switch (timer_list->clock->type) { ++ case QEMU_CLOCK_REALTIME: ++ break; ++ default: ++ case QEMU_CLOCK_VIRTUAL: ++ break; ++ case QEMU_CLOCK_HOST: ++ if (!replay_checkpoint(CHECKPOINT_CLOCK_HOST)) { ++ goto out; ++ } ++ break; ++ case QEMU_CLOCK_VIRTUAL_RT: ++ if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL_RT)) { ++ goto out; ++ } ++ break; ++ } ++ ++ /* ++ * Extract expired timers from active timers list and process them. ++ * ++ * In rr mode we need "filtered" checkpointing for virtual clock. The ++ * checkpoint must be recorded/replayed before processing any non-EXTERNAL timer, ++ * and that must only be done once since the clock value stays the same. Because ++ * non-EXTERNAL timers may appear in the timers list while it being processed, ++ * the checkpoint can be issued at a time until no timers are left and we are ++ * done". ++ */ ++ current_time = qemu_clock_get_ns(timer_list->clock->type); ++ qemu_mutex_lock(&timer_list->active_timers_lock); ++ while ((ts = timer_list->active_timers)) { ++ if (!timer_expired_ns(ts, current_time)) { ++ /* No expired timers left. The checkpoint can be skipped ++ * if no timers fired or they were all external. ++ */ ++ break; ++ } ++ /* Checkpoint for virtual clock is redundant in cases where ++ * it's being triggered with only non-EXTERNAL timers, because ++ * these timers don't change guest state directly. ++ */ ++ if (replay_mode != REPLAY_MODE_NONE ++ && timer_list->clock->type == QEMU_CLOCK_VIRTUAL ++ && !(ts->attributes & QEMU_TIMER_ATTR_EXTERNAL) ++ && !replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) { ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ goto out; ++ } ++ ++ /* remove timer from the list before calling the callback */ ++ timer_list->active_timers = ts->next; ++ ts->next = NULL; ++ ts->expire_time = -1; ++ cb = ts->cb; ++ opaque = ts->opaque; ++ ++ /* run the callback (the timer list can be modified) */ ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ cb(opaque); ++ qemu_mutex_lock(&timer_list->active_timers_lock); ++ ++ progress = true; ++ } ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ ++out: ++ qemu_event_set(&timer_list->timers_done_ev); ++ return progress; ++} ++ ++bool qemu_clock_run_timers(QEMUClockType type) ++{ ++ return timerlist_run_timers(main_loop_tlg.tl[type]); ++} ++ ++void timerlistgroup_init(QEMUTimerListGroup *tlg, ++ QEMUTimerListNotifyCB *cb, void *opaque) ++{ ++ QEMUClockType type; ++ for (type = 0; type < QEMU_CLOCK_MAX; type++) { ++ tlg->tl[type] = timerlist_new(type, cb, opaque); ++ } ++} ++ ++void timerlistgroup_deinit(QEMUTimerListGroup *tlg) ++{ ++ QEMUClockType type; ++ for (type = 0; type < QEMU_CLOCK_MAX; type++) { ++ timerlist_free(tlg->tl[type]); ++ } ++} ++ ++bool timerlistgroup_run_timers(QEMUTimerListGroup *tlg) ++{ ++ QEMUClockType type; ++ bool progress = false; ++ for (type = 0; type < QEMU_CLOCK_MAX; type++) { ++ progress |= timerlist_run_timers(tlg->tl[type]); ++ } ++ return progress; ++} ++ ++int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg) ++{ ++ int64_t deadline = -1; ++ QEMUClockType type; ++ for (type = 0; type < QEMU_CLOCK_MAX; type++) { ++ if (qemu_clock_use_for_deadline(type)) { ++ deadline = qemu_soonest_timeout(deadline, ++ timerlist_deadline_ns(tlg->tl[type])); ++ } ++ } ++ return deadline; ++} ++ ++int64_t qemu_clock_get_ns(QEMUClockType type) ++{ ++ switch (type) { ++ case QEMU_CLOCK_REALTIME: ++ return get_clock(); ++ default: ++ case QEMU_CLOCK_VIRTUAL: ++ return cpus_get_virtual_clock(); ++ case QEMU_CLOCK_HOST: ++ return REPLAY_CLOCK(REPLAY_CLOCK_HOST, get_clock_realtime()); ++ case QEMU_CLOCK_VIRTUAL_RT: ++ return REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, cpu_get_clock()); ++ } ++} ++ ++static void qemu_virtual_clock_set_ns(int64_t time) ++{ ++ return cpus_set_virtual_clock(time); ++} ++ ++void init_clocks(QEMUTimerListNotifyCB *notify_cb) ++{ ++ QEMUClockType type; ++ for (type = 0; type < QEMU_CLOCK_MAX; type++) { ++ qemu_clock_init(type, notify_cb); ++ } ++ ++#ifdef CONFIG_PRCTL_PR_SET_TIMERSLACK ++ prctl(PR_SET_TIMERSLACK, 1, 0, 0, 0); ++#endif ++} ++ ++uint64_t timer_expire_time_ns(QEMUTimer *ts) ++{ ++ return timer_pending(ts) ? ts->expire_time : -1; ++} ++ ++bool qemu_clock_run_all_timers(void) ++{ ++ bool progress = false; ++ QEMUClockType type; ++ ++ for (type = 0; type < QEMU_CLOCK_MAX; type++) { ++ if (qemu_clock_use_for_deadline(type)) { ++ progress |= qemu_clock_run_timers(type); ++ } ++ } ++ ++ return progress; ++} ++ ++int64_t qemu_clock_advance_virtual_time(int64_t dest) ++{ ++ int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ++ AioContext *aio_context; ++ int64_t deadline; ++ ++ aio_context = qemu_get_aio_context(); ++ ++ deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, ++ QEMU_TIMER_ATTR_ALL); ++ /* ++ * A deadline of < 0 indicates this timer is not enabled, so we ++ * won't get far trying to run it forward. ++ */ ++ while (deadline >= 0 && clock < dest) { ++ int64_t warp = qemu_soonest_timeout(dest - clock, deadline); ++ ++ qemu_virtual_clock_set_ns(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + warp); ++ ++ qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); ++ timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); ++ clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ++ ++ deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, ++ QEMU_TIMER_ATTR_ALL); ++ } ++ qemu_clock_notify(QEMU_CLOCK_VIRTUAL); ++ ++ return clock; ++} +diff --git a/qcow2/lib/util/qsp.c b/qcow2/lib/util/qsp.c +new file mode 100644 +index 00000000..6b783e2e +--- /dev/null ++++ b/qcow2/lib/util/qsp.c +@@ -0,0 +1,813 @@ ++/* ++ * qsp.c - QEMU Synchronization Profiler ++ * ++ * Copyright (C) 2018, Emilio G. Cota ++ * ++ * License: GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ * QSP profiles the time spent in synchronization primitives, which can ++ * help diagnose performance problems, e.g. scalability issues when ++ * contention is high. ++ * ++ * The primitives currently supported are mutexes, recursive mutexes and ++ * condition variables. Note that not all related functions are intercepted; ++ * instead we profile only those functions that can have a performance impact, ++ * either due to blocking (e.g. cond_wait, mutex_lock) or cache line ++ * contention (e.g. mutex_lock, mutex_trylock). ++ * ++ * QSP's design focuses on speed and scalability. This is achieved ++ * by having threads do their profiling entirely on thread-local data. ++ * The appropriate thread-local data is found via a QHT, i.e. a concurrent hash ++ * table. To aggregate data in order to generate a report, we iterate over ++ * all entries in the hash table. Depending on the number of threads and ++ * synchronization objects this might be expensive, but note that it is ++ * very rarely called -- reports are generated only when requested by users. ++ * ++ * Reports are generated as a table where each row represents a call site. A ++ * call site is the triplet formed by the __file__ and __LINE__ of the caller ++ * as well as the address of the "object" (i.e. mutex, rec. mutex or condvar) ++ * being operated on. Optionally, call sites that operate on different objects ++ * of the same type can be coalesced, which can be particularly useful when ++ * profiling dynamically-allocated objects. ++ * ++ * Alternative designs considered: ++ * ++ * - Use an off-the-shelf profiler such as mutrace. This is not a viable option ++ * for us because QEMU has __malloc_hook set (by one of the libraries it ++ * uses); leaving this hook unset is required to avoid deadlock in mutrace. ++ * ++ * - Use a glib HT for each thread, protecting each HT with its own lock. ++ * This isn't simpler than the current design, and is 10% slower in the ++ * atomic_add-bench microbenchmark (-m option). ++ * ++ * - For reports, just use a binary tree as we aggregate data, instead of having ++ * an intermediate hash table. This would simplify the code only slightly, but ++ * would perform badly if there were many threads and objects to track. ++ * ++ * - Wrap operations on qsp entries with RCU read-side critical sections, so ++ * that qsp_reset() can delete entries. Unfortunately, the overhead of calling ++ * rcu_read_lock/unlock slows down atomic_add-bench -m by 24%. Having ++ * a snapshot that is updated on qsp_reset() avoids this overhead. ++ * ++ * Related Work: ++ * - Lennart Poettering's mutrace: http://0pointer.de/blog/projects/mutrace.html ++ * - Lozi, David, Thomas, Lawall and Muller. "Remote Core Locking: Migrating ++ * Critical-Section Execution to Improve the Performance of Multithreaded ++ * Applications", USENIX ATC'12. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/qemu-print.h" ++#include "qemu/thread.h" ++#include "qemu/timer.h" ++#include "qemu/qht.h" ++#include "qemu/rcu.h" ++#include "qemu/xxhash.h" ++ ++enum QSPType { ++ QSP_MUTEX, ++ QSP_BQL_MUTEX, ++ QSP_REC_MUTEX, ++ QSP_CONDVAR, ++}; ++ ++struct QSPCallSite { ++ const void *obj; ++ const char *file; /* i.e. __FILE__; shortened later */ ++ int line; ++ enum QSPType type; ++}; ++typedef struct QSPCallSite QSPCallSite; ++ ++struct QSPEntry { ++ void *thread_ptr; ++ const QSPCallSite *callsite; ++ aligned_uint64_t n_acqs; ++ aligned_uint64_t ns; ++ unsigned int n_objs; /* count of coalesced objs; only used for reporting */ ++}; ++typedef struct QSPEntry QSPEntry; ++ ++struct QSPSnapshot { ++ struct rcu_head rcu; ++ struct qht ht; ++}; ++typedef struct QSPSnapshot QSPSnapshot; ++ ++/* initial sizing for hash tables */ ++#define QSP_INITIAL_SIZE 64 ++ ++/* If this file is moved, QSP_REL_PATH should be updated accordingly */ ++#define QSP_REL_PATH "util/qsp.c" ++ ++/* this file's full path. Used to present all call sites with relative paths */ ++static size_t qsp_qemu_path_len; ++ ++/* the address of qsp_thread gives us a unique 'thread ID' */ ++static __thread int qsp_thread; ++ ++/* ++ * Call sites are the same for all threads, so we track them in a separate hash ++ * table to save memory. ++ */ ++static struct qht qsp_callsite_ht; ++ ++static struct qht qsp_ht; ++static QSPSnapshot *qsp_snapshot; ++static bool qsp_initialized, qsp_initializing; ++ ++static const char * const qsp_typenames[] = { ++ [QSP_MUTEX] = "mutex", ++ [QSP_BQL_MUTEX] = "BQL mutex", ++ [QSP_REC_MUTEX] = "rec_mutex", ++ [QSP_CONDVAR] = "condvar", ++}; ++ ++QemuMutexLockFunc bql_mutex_lock_func = qemu_mutex_lock_impl; ++QemuMutexLockFunc qemu_mutex_lock_func = qemu_mutex_lock_impl; ++QemuMutexTrylockFunc qemu_mutex_trylock_func = qemu_mutex_trylock_impl; ++QemuRecMutexLockFunc qemu_rec_mutex_lock_func = qemu_rec_mutex_lock_impl; ++QemuRecMutexTrylockFunc qemu_rec_mutex_trylock_func = ++ qemu_rec_mutex_trylock_impl; ++QemuCondWaitFunc qemu_cond_wait_func = qemu_cond_wait_impl; ++QemuCondTimedWaitFunc qemu_cond_timedwait_func = qemu_cond_timedwait_impl; ++ ++/* ++ * It pays off to _not_ hash callsite->file; hashing a string is slow, and ++ * without it we still get a pretty unique hash. ++ */ ++static inline ++uint32_t do_qsp_callsite_hash(const QSPCallSite *callsite, uint64_t ab) ++{ ++ uint64_t cd = (uint64_t)(uintptr_t)callsite->obj; ++ uint32_t e = callsite->line; ++ uint32_t f = callsite->type; ++ ++ return qemu_xxhash8(ab, cd, 0, e, f); ++} ++ ++static inline ++uint32_t qsp_callsite_hash(const QSPCallSite *callsite) ++{ ++ return do_qsp_callsite_hash(callsite, 0); ++} ++ ++static inline uint32_t do_qsp_entry_hash(const QSPEntry *entry, uint64_t a) ++{ ++ return do_qsp_callsite_hash(entry->callsite, a); ++} ++ ++static uint32_t qsp_entry_hash(const QSPEntry *entry) ++{ ++ return do_qsp_entry_hash(entry, (uint64_t)(uintptr_t)entry->thread_ptr); ++} ++ ++static uint32_t qsp_entry_no_thread_hash(const QSPEntry *entry) ++{ ++ return do_qsp_entry_hash(entry, 0); ++} ++ ++/* without the objects we need to hash the file name to get a decent hash */ ++static uint32_t qsp_entry_no_thread_obj_hash(const QSPEntry *entry) ++{ ++ const QSPCallSite *callsite = entry->callsite; ++ uint64_t ab = g_str_hash(callsite->file); ++ uint64_t cd = callsite->line; ++ uint32_t e = callsite->type; ++ ++ return qemu_xxhash5(ab, cd, e); ++} ++ ++static bool qsp_callsite_cmp(const void *ap, const void *bp) ++{ ++ const QSPCallSite *a = ap; ++ const QSPCallSite *b = bp; ++ ++ return a == b || ++ (a->obj == b->obj && ++ a->line == b->line && ++ a->type == b->type && ++ (a->file == b->file || !strcmp(a->file, b->file))); ++} ++ ++static bool qsp_callsite_no_obj_cmp(const void *ap, const void *bp) ++{ ++ const QSPCallSite *a = ap; ++ const QSPCallSite *b = bp; ++ ++ return a == b || ++ (a->line == b->line && ++ a->type == b->type && ++ (a->file == b->file || !strcmp(a->file, b->file))); ++} ++ ++static bool qsp_entry_no_thread_cmp(const void *ap, const void *bp) ++{ ++ const QSPEntry *a = ap; ++ const QSPEntry *b = bp; ++ ++ return qsp_callsite_cmp(a->callsite, b->callsite); ++} ++ ++static bool qsp_entry_no_thread_obj_cmp(const void *ap, const void *bp) ++{ ++ const QSPEntry *a = ap; ++ const QSPEntry *b = bp; ++ ++ return qsp_callsite_no_obj_cmp(a->callsite, b->callsite); ++} ++ ++static bool qsp_entry_cmp(const void *ap, const void *bp) ++{ ++ const QSPEntry *a = ap; ++ const QSPEntry *b = bp; ++ ++ return a->thread_ptr == b->thread_ptr && ++ qsp_callsite_cmp(a->callsite, b->callsite); ++} ++ ++/* ++ * Normally we'd call this from a constructor function, but we want it to work ++ * via libutil as well. ++ */ ++static void qsp_do_init(void) ++{ ++ /* make sure this file's path in the tree is up to date with QSP_REL_PATH */ ++ g_assert(strstr(__FILE__, QSP_REL_PATH)); ++ qsp_qemu_path_len = strlen(__FILE__) - strlen(QSP_REL_PATH); ++ ++ qht_init(&qsp_ht, qsp_entry_cmp, QSP_INITIAL_SIZE, ++ QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); ++ qht_init(&qsp_callsite_ht, qsp_callsite_cmp, QSP_INITIAL_SIZE, ++ QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); ++} ++ ++static __attribute__((noinline)) void qsp_init__slowpath(void) ++{ ++ if (qatomic_cmpxchg(&qsp_initializing, false, true) == false) { ++ qsp_do_init(); ++ qatomic_set(&qsp_initialized, true); ++ } else { ++ while (!qatomic_read(&qsp_initialized)) { ++ cpu_relax(); ++ } ++ } ++} ++ ++/* qsp_init() must be called from _all_ exported functions */ ++static inline void qsp_init(void) ++{ ++ if (likely(qatomic_read(&qsp_initialized))) { ++ return; ++ } ++ qsp_init__slowpath(); ++} ++ ++static QSPCallSite *qsp_callsite_find(const QSPCallSite *orig) ++{ ++ QSPCallSite *callsite; ++ uint32_t hash; ++ ++ hash = qsp_callsite_hash(orig); ++ callsite = qht_lookup(&qsp_callsite_ht, orig, hash); ++ if (callsite == NULL) { ++ void *existing = NULL; ++ ++ callsite = g_new(QSPCallSite, 1); ++ memcpy(callsite, orig, sizeof(*callsite)); ++ qht_insert(&qsp_callsite_ht, callsite, hash, &existing); ++ if (unlikely(existing)) { ++ g_free(callsite); ++ callsite = existing; ++ } ++ } ++ return callsite; ++} ++ ++static QSPEntry * ++qsp_entry_create(struct qht *ht, const QSPEntry *entry, uint32_t hash) ++{ ++ QSPEntry *e; ++ void *existing = NULL; ++ ++ e = g_new0(QSPEntry, 1); ++ e->thread_ptr = entry->thread_ptr; ++ e->callsite = qsp_callsite_find(entry->callsite); ++ ++ qht_insert(ht, e, hash, &existing); ++ if (unlikely(existing)) { ++ g_free(e); ++ e = existing; ++ } ++ return e; ++} ++ ++static QSPEntry * ++qsp_entry_find(struct qht *ht, const QSPEntry *entry, uint32_t hash) ++{ ++ QSPEntry *e; ++ ++ e = qht_lookup(ht, entry, hash); ++ if (e == NULL) { ++ e = qsp_entry_create(ht, entry, hash); ++ } ++ return e; ++} ++ ++/* ++ * Note: Entries are never removed, so callers do not have to be in an RCU ++ * read-side critical section. ++ */ ++static QSPEntry *qsp_entry_get(const void *obj, const char *file, int line, ++ enum QSPType type) ++{ ++ QSPCallSite callsite = { ++ .obj = obj, ++ .file = file, ++ .line = line, ++ .type = type, ++ }; ++ QSPEntry orig; ++ uint32_t hash; ++ ++ qsp_init(); ++ ++ orig.thread_ptr = &qsp_thread; ++ orig.callsite = &callsite; ++ ++ hash = qsp_entry_hash(&orig); ++ return qsp_entry_find(&qsp_ht, &orig, hash); ++} ++ ++/* ++ * @e is in the global hash table; it is only written to by the current thread, ++ * so we write to it atomically (as in "write once") to prevent torn reads. ++ */ ++static inline void do_qsp_entry_record(QSPEntry *e, int64_t delta, bool acq) ++{ ++ qatomic_set_u64(&e->ns, e->ns + delta); ++ if (acq) { ++ qatomic_set_u64(&e->n_acqs, e->n_acqs + 1); ++ } ++} ++ ++static inline void qsp_entry_record(QSPEntry *e, int64_t delta) ++{ ++ do_qsp_entry_record(e, delta, true); ++} ++ ++#define QSP_GEN_VOID(type_, qsp_t_, func_, impl_) \ ++ static void func_(type_ *obj, const char *file, int line) \ ++ { \ ++ QSPEntry *e; \ ++ int64_t t0, t1; \ ++ \ ++ t0 = get_clock(); \ ++ impl_(obj, file, line); \ ++ t1 = get_clock(); \ ++ \ ++ e = qsp_entry_get(obj, file, line, qsp_t_); \ ++ qsp_entry_record(e, t1 - t0); \ ++ } ++ ++#define QSP_GEN_RET1(type_, qsp_t_, func_, impl_) \ ++ static int func_(type_ *obj, const char *file, int line) \ ++ { \ ++ QSPEntry *e; \ ++ int64_t t0, t1; \ ++ int err; \ ++ \ ++ t0 = get_clock(); \ ++ err = impl_(obj, file, line); \ ++ t1 = get_clock(); \ ++ \ ++ e = qsp_entry_get(obj, file, line, qsp_t_); \ ++ do_qsp_entry_record(e, t1 - t0, !err); \ ++ return err; \ ++ } ++ ++QSP_GEN_VOID(QemuMutex, QSP_BQL_MUTEX, qsp_bql_mutex_lock, qemu_mutex_lock_impl) ++QSP_GEN_VOID(QemuMutex, QSP_MUTEX, qsp_mutex_lock, qemu_mutex_lock_impl) ++QSP_GEN_RET1(QemuMutex, QSP_MUTEX, qsp_mutex_trylock, qemu_mutex_trylock_impl) ++ ++QSP_GEN_VOID(QemuRecMutex, QSP_REC_MUTEX, qsp_rec_mutex_lock, ++ qemu_rec_mutex_lock_impl) ++QSP_GEN_RET1(QemuRecMutex, QSP_REC_MUTEX, qsp_rec_mutex_trylock, ++ qemu_rec_mutex_trylock_impl) ++ ++#undef QSP_GEN_RET1 ++#undef QSP_GEN_VOID ++ ++static void ++qsp_cond_wait(QemuCond *cond, QemuMutex *mutex, const char *file, int line) ++{ ++ QSPEntry *e; ++ int64_t t0, t1; ++ ++ t0 = get_clock(); ++ qemu_cond_wait_impl(cond, mutex, file, line); ++ t1 = get_clock(); ++ ++ e = qsp_entry_get(cond, file, line, QSP_CONDVAR); ++ qsp_entry_record(e, t1 - t0); ++} ++ ++static bool ++qsp_cond_timedwait(QemuCond *cond, QemuMutex *mutex, int ms, ++ const char *file, int line) ++{ ++ QSPEntry *e; ++ int64_t t0, t1; ++ bool ret; ++ ++ t0 = get_clock(); ++ ret = qemu_cond_timedwait_impl(cond, mutex, ms, file, line); ++ t1 = get_clock(); ++ ++ e = qsp_entry_get(cond, file, line, QSP_CONDVAR); ++ qsp_entry_record(e, t1 - t0); ++ return ret; ++} ++ ++bool qsp_is_enabled(void) ++{ ++ return qatomic_read(&qemu_mutex_lock_func) == qsp_mutex_lock; ++} ++ ++void qsp_enable(void) ++{ ++ qatomic_set(&qemu_mutex_lock_func, qsp_mutex_lock); ++ qatomic_set(&qemu_mutex_trylock_func, qsp_mutex_trylock); ++ qatomic_set(&bql_mutex_lock_func, qsp_bql_mutex_lock); ++ qatomic_set(&qemu_rec_mutex_lock_func, qsp_rec_mutex_lock); ++ qatomic_set(&qemu_rec_mutex_trylock_func, qsp_rec_mutex_trylock); ++ qatomic_set(&qemu_cond_wait_func, qsp_cond_wait); ++ qatomic_set(&qemu_cond_timedwait_func, qsp_cond_timedwait); ++} ++ ++void qsp_disable(void) ++{ ++ qatomic_set(&qemu_mutex_lock_func, qemu_mutex_lock_impl); ++ qatomic_set(&qemu_mutex_trylock_func, qemu_mutex_trylock_impl); ++ qatomic_set(&bql_mutex_lock_func, qemu_mutex_lock_impl); ++ qatomic_set(&qemu_rec_mutex_lock_func, qemu_rec_mutex_lock_impl); ++ qatomic_set(&qemu_rec_mutex_trylock_func, qemu_rec_mutex_trylock_impl); ++ qatomic_set(&qemu_cond_wait_func, qemu_cond_wait_impl); ++ qatomic_set(&qemu_cond_timedwait_func, qemu_cond_timedwait_impl); ++} ++ ++static gint qsp_tree_cmp(gconstpointer ap, gconstpointer bp, gpointer up) ++{ ++ const QSPEntry *a = ap; ++ const QSPEntry *b = bp; ++ enum QSPSortBy sort_by = *(enum QSPSortBy *)up; ++ const QSPCallSite *ca; ++ const QSPCallSite *cb; ++ ++ switch (sort_by) { ++ case QSP_SORT_BY_TOTAL_WAIT_TIME: ++ if (a->ns > b->ns) { ++ return -1; ++ } else if (a->ns < b->ns) { ++ return 1; ++ } ++ break; ++ case QSP_SORT_BY_AVG_WAIT_TIME: ++ { ++ double avg_a = a->n_acqs ? a->ns / a->n_acqs : 0; ++ double avg_b = b->n_acqs ? b->ns / b->n_acqs : 0; ++ ++ if (avg_a > avg_b) { ++ return -1; ++ } else if (avg_a < avg_b) { ++ return 1; ++ } ++ break; ++ } ++ default: ++ g_assert_not_reached(); ++ } ++ ++ ca = a->callsite; ++ cb = b->callsite; ++ /* Break the tie with the object's address */ ++ if (ca->obj < cb->obj) { ++ return -1; ++ } else if (ca->obj > cb->obj) { ++ return 1; ++ } else { ++ int cmp; ++ ++ /* same obj. Break the tie with the callsite's file */ ++ cmp = strcmp(ca->file, cb->file); ++ if (cmp) { ++ return cmp; ++ } ++ /* same callsite file. Break the tie with the callsite's line */ ++ g_assert(ca->line != cb->line); ++ if (ca->line < cb->line) { ++ return -1; ++ } else if (ca->line > cb->line) { ++ return 1; ++ } else { ++ /* break the tie with the callsite's type */ ++ return cb->type - ca->type; ++ } ++ } ++} ++ ++static void qsp_sort(void *p, uint32_t h, void *userp) ++{ ++ QSPEntry *e = p; ++ GTree *tree = userp; ++ ++ g_tree_insert(tree, e, NULL); ++} ++ ++static void qsp_aggregate(void *p, uint32_t h, void *up) ++{ ++ struct qht *ht = up; ++ const QSPEntry *e = p; ++ QSPEntry *agg; ++ uint32_t hash; ++ ++ hash = qsp_entry_no_thread_hash(e); ++ agg = qsp_entry_find(ht, e, hash); ++ /* ++ * The entry is in the global hash table; read from it atomically (as in ++ * "read once"). ++ */ ++ agg->ns += qatomic_read_u64(&e->ns); ++ agg->n_acqs += qatomic_read_u64(&e->n_acqs); ++} ++ ++static void qsp_iter_diff(void *p, uint32_t hash, void *htp) ++{ ++ struct qht *ht = htp; ++ QSPEntry *old = p; ++ QSPEntry *new; ++ ++ new = qht_lookup(ht, old, hash); ++ /* entries are never deleted, so we must have this one */ ++ g_assert(new != NULL); ++ /* our reading of the stats happened after the snapshot was taken */ ++ g_assert(new->n_acqs >= old->n_acqs); ++ g_assert(new->ns >= old->ns); ++ ++ new->n_acqs -= old->n_acqs; ++ new->ns -= old->ns; ++ ++ /* No point in reporting an empty entry */ ++ if (new->n_acqs == 0 && new->ns == 0) { ++ bool removed = qht_remove(ht, new, hash); ++ ++ g_assert(removed); ++ g_free(new); ++ } ++} ++ ++static void qsp_diff(struct qht *orig, struct qht *new) ++{ ++ qht_iter(orig, qsp_iter_diff, new); ++} ++ ++static void qsp_iter_callsite_coalesce(void *p, uint32_t h, void *htp) ++{ ++ struct qht *ht = htp; ++ QSPEntry *old = p; ++ QSPEntry *e; ++ uint32_t hash; ++ ++ hash = qsp_entry_no_thread_obj_hash(old); ++ e = qht_lookup(ht, old, hash); ++ if (e == NULL) { ++ e = qsp_entry_create(ht, old, hash); ++ e->n_objs = 1; ++ } else if (e->callsite->obj != old->callsite->obj) { ++ e->n_objs++; ++ } ++ e->ns += old->ns; ++ e->n_acqs += old->n_acqs; ++} ++ ++static void qsp_ht_delete(void *p, uint32_t h, void *htp) ++{ ++ g_free(p); ++} ++ ++static void qsp_mktree(GTree *tree, bool callsite_coalesce) ++{ ++ struct qht ht, coalesce_ht; ++ struct qht *htp; ++ ++ /* ++ * First, see if there's a prior snapshot, so that we read the global hash ++ * table _after_ the snapshot has been created, which guarantees that ++ * the entries we'll read will be a superset of the snapshot's entries. ++ * ++ * We must remain in an RCU read-side critical section until we're done ++ * with the snapshot. ++ */ ++ WITH_RCU_READ_LOCK_GUARD() { ++ QSPSnapshot *snap = qatomic_rcu_read(&qsp_snapshot); ++ ++ /* Aggregate all results from the global hash table into a local one */ ++ qht_init(&ht, qsp_entry_no_thread_cmp, QSP_INITIAL_SIZE, ++ QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); ++ qht_iter(&qsp_ht, qsp_aggregate, &ht); ++ ++ /* compute the difference wrt the snapshot, if any */ ++ if (snap) { ++ qsp_diff(&snap->ht, &ht); ++ } ++ } ++ ++ htp = &ht; ++ if (callsite_coalesce) { ++ qht_init(&coalesce_ht, qsp_entry_no_thread_obj_cmp, QSP_INITIAL_SIZE, ++ QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); ++ qht_iter(&ht, qsp_iter_callsite_coalesce, &coalesce_ht); ++ ++ /* free the previous hash table, and point htp to coalesce_ht */ ++ qht_iter(&ht, qsp_ht_delete, NULL); ++ qht_destroy(&ht); ++ htp = &coalesce_ht; ++ } ++ ++ /* sort the hash table elements by using a tree */ ++ qht_iter(htp, qsp_sort, tree); ++ ++ /* free the hash table, but keep the elements (those are in the tree now) */ ++ qht_destroy(htp); ++} ++ ++/* free string with g_free */ ++static char *qsp_at(const QSPCallSite *callsite) ++{ ++ GString *s = g_string_new(NULL); ++ const char *shortened; ++ ++ /* remove the absolute path to qemu */ ++ if (unlikely(strlen(callsite->file) < qsp_qemu_path_len)) { ++ shortened = callsite->file; ++ } else { ++ shortened = callsite->file + qsp_qemu_path_len; ++ } ++ g_string_append_printf(s, "%s:%u", shortened, callsite->line); ++ return g_string_free(s, FALSE); ++} ++ ++struct QSPReportEntry { ++ const void *obj; ++ char *callsite_at; ++ const char *typename; ++ double time_s; ++ double ns_avg; ++ uint64_t n_acqs; ++ unsigned int n_objs; ++}; ++typedef struct QSPReportEntry QSPReportEntry; ++ ++struct QSPReport { ++ QSPReportEntry *entries; ++ size_t n_entries; ++ size_t max_n_entries; ++}; ++typedef struct QSPReport QSPReport; ++ ++static gboolean qsp_tree_report(gpointer key, gpointer value, gpointer udata) ++{ ++ const QSPEntry *e = key; ++ QSPReport *report = udata; ++ QSPReportEntry *entry; ++ ++ if (report->n_entries == report->max_n_entries) { ++ return TRUE; ++ } ++ entry = &report->entries[report->n_entries]; ++ report->n_entries++; ++ ++ entry->obj = e->callsite->obj; ++ entry->n_objs = e->n_objs; ++ entry->callsite_at = qsp_at(e->callsite); ++ entry->typename = qsp_typenames[e->callsite->type]; ++ entry->time_s = e->ns * 1e-9; ++ entry->n_acqs = e->n_acqs; ++ entry->ns_avg = e->n_acqs ? e->ns / e->n_acqs : 0; ++ return FALSE; ++} ++ ++static void pr_report(const QSPReport *rep) ++{ ++ char *dashes; ++ size_t max_len = 0; ++ int callsite_len = 0; ++ int callsite_rspace; ++ int n_dashes; ++ size_t i; ++ ++ /* find out the maximum length of all 'callsite' fields */ ++ for (i = 0; i < rep->n_entries; i++) { ++ const QSPReportEntry *e = &rep->entries[i]; ++ size_t len = strlen(e->callsite_at); ++ ++ if (len > max_len) { ++ max_len = len; ++ } ++ } ++ ++ callsite_len = MAX(max_len, strlen("Call site")); ++ /* white space to leave to the right of "Call site" */ ++ callsite_rspace = callsite_len - strlen("Call site"); ++ ++ qemu_printf("Type Object Call site%*s Wait Time (s) " ++ " Count Average (us)\n", callsite_rspace, ""); ++ ++ /* build a horizontal rule with dashes */ ++ n_dashes = 79 + callsite_rspace; ++ dashes = g_malloc(n_dashes + 1); ++ memset(dashes, '-', n_dashes); ++ dashes[n_dashes] = '\0'; ++ qemu_printf("%s\n", dashes); ++ ++ for (i = 0; i < rep->n_entries; i++) { ++ const QSPReportEntry *e = &rep->entries[i]; ++ GString *s = g_string_new(NULL); ++ ++ g_string_append_printf(s, "%-9s ", e->typename); ++ if (e->n_objs > 1) { ++ g_string_append_printf(s, "[%12u]", e->n_objs); ++ } else { ++ g_string_append_printf(s, "%14p", e->obj); ++ } ++ g_string_append_printf(s, " %s%*s %13.5f %12" PRIu64 " %12.2f\n", ++ e->callsite_at, ++ callsite_len - (int)strlen(e->callsite_at), "", ++ e->time_s, e->n_acqs, e->ns_avg * 1e-3); ++ qemu_printf("%s", s->str); ++ g_string_free(s, TRUE); ++ } ++ ++ qemu_printf("%s\n", dashes); ++ g_free(dashes); ++} ++ ++static void report_destroy(QSPReport *rep) ++{ ++ size_t i; ++ ++ for (i = 0; i < rep->n_entries; i++) { ++ QSPReportEntry *e = &rep->entries[i]; ++ ++ g_free(e->callsite_at); ++ } ++ g_free(rep->entries); ++} ++ ++void qsp_report(size_t max, enum QSPSortBy sort_by, ++ bool callsite_coalesce) ++{ ++ GTree *tree = g_tree_new_full(qsp_tree_cmp, &sort_by, g_free, NULL); ++ QSPReport rep; ++ ++ qsp_init(); ++ ++ rep.entries = g_new0(QSPReportEntry, max); ++ rep.n_entries = 0; ++ rep.max_n_entries = max; ++ ++ qsp_mktree(tree, callsite_coalesce); ++ g_tree_foreach(tree, qsp_tree_report, &rep); ++ g_tree_destroy(tree); ++ ++ pr_report(&rep); ++ report_destroy(&rep); ++} ++ ++static void qsp_snapshot_destroy(QSPSnapshot *snap) ++{ ++ qht_iter(&snap->ht, qsp_ht_delete, NULL); ++ qht_destroy(&snap->ht); ++ g_free(snap); ++} ++ ++void qsp_reset(void) ++{ ++ QSPSnapshot *new = g_new(QSPSnapshot, 1); ++ QSPSnapshot *old; ++ ++ qsp_init(); ++ ++ qht_init(&new->ht, qsp_entry_cmp, QSP_INITIAL_SIZE, ++ QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); ++ ++ /* take a snapshot of the current state */ ++ qht_iter(&qsp_ht, qsp_aggregate, &new->ht); ++ ++ /* replace the previous snapshot, if any */ ++ old = qatomic_xchg(&qsp_snapshot, new); ++ if (old) { ++ call_rcu(old, qsp_snapshot_destroy, rcu); ++ } ++} +diff --git a/qcow2/lib/util/range.c b/qcow2/lib/util/range.c +new file mode 100644 +index 00000000..f3f40098 +--- /dev/null ++++ b/qcow2/lib/util/range.c +@@ -0,0 +1,123 @@ ++/* ++ * QEMU 64-bit address ranges ++ * ++ * Copyright (c) 2015-2016 Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see . ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/range.h" ++ ++int range_compare(Range *a, Range *b) ++{ ++ assert(!range_is_empty(a) && !range_is_empty(b)); ++ ++ /* Careful, avoid wraparound */ ++ if (b->lob && b->lob - 1 > a->upb) { ++ return -1; ++ } ++ if (a->lob && a->lob - 1 > b->upb) { ++ return 1; ++ } ++ return 0; ++} ++ ++/* Insert @data into @list of ranges; caller no longer owns @data */ ++GList *range_list_insert(GList *list, Range *data) ++{ ++ GList *l; ++ ++ assert(!range_is_empty(data)); ++ ++ /* Skip all list elements strictly less than data */ ++ for (l = list; l && range_compare(l->data, data) < 0; l = l->next) { ++ } ++ ++ if (!l || range_compare(l->data, data) > 0) { ++ /* Rest of the list (if any) is strictly greater than @data */ ++ return g_list_insert_before(list, l, data); ++ } ++ ++ /* Current list element overlaps @data, merge the two */ ++ range_extend(l->data, data); ++ g_free(data); ++ ++ /* Merge any subsequent list elements that now also overlap */ ++ while (l->next && range_compare(l->data, l->next->data) == 0) { ++ GList *new_l; ++ ++ range_extend(l->data, l->next->data); ++ g_free(l->next->data); ++ new_l = g_list_delete_link(list, l->next); ++ assert(new_l == list); ++ } ++ ++ return list; ++} ++ ++static inline ++GList *append_new_range(GList *list, uint64_t lob, uint64_t upb) ++{ ++ Range *new = g_new0(Range, 1); ++ ++ range_set_bounds(new, lob, upb); ++ return g_list_append(list, new); ++} ++ ++ ++void range_inverse_array(GList *in, GList **rev, ++ uint64_t low, uint64_t high) ++{ ++ Range *r, *rn; ++ GList *l = in, *out = *rev; ++ ++ for (l = in; l && range_upb(l->data) < low; l = l->next) { ++ continue; ++ } ++ ++ if (!l) { ++ out = append_new_range(out, low, high); ++ goto exit; ++ } ++ r = (Range *)l->data; ++ ++ /* first range lob is greater than min, insert a first range */ ++ if (range_lob(r) > low) { ++ out = append_new_range(out, low, MIN(range_lob(r) - 1, high)); ++ } ++ ++ /* insert a range in between each original range until we reach high */ ++ for (; l->next; l = l->next) { ++ r = (Range *)l->data; ++ rn = (Range *)l->next->data; ++ if (range_lob(r) >= high) { ++ goto exit; ++ } ++ if (range_compare(r, rn)) { ++ out = append_new_range(out, range_upb(r) + 1, ++ MIN(range_lob(rn) - 1, high)); ++ } ++ } ++ ++ /* last range */ ++ r = (Range *)l->data; ++ ++ /* last range upb is less than max, insert a last range */ ++ if (range_upb(r) < high) { ++ out = append_new_range(out, range_upb(r) + 1, high); ++ } ++exit: ++ *rev = out; ++} +diff --git a/qcow2/lib/util/rcu.c b/qcow2/lib/util/rcu.c +new file mode 100644 +index 00000000..fa32c942 +--- /dev/null ++++ b/qcow2/lib/util/rcu.c +@@ -0,0 +1,472 @@ ++/* ++ * urcu-mb.c ++ * ++ * Userspace RCU library with explicit memory barriers ++ * ++ * Copyright (c) 2009 Mathieu Desnoyers ++ * Copyright (c) 2009 Paul E. McKenney, IBM Corporation. ++ * Copyright 2015 Red Hat, Inc. ++ * ++ * Ported to QEMU by Paolo Bonzini ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * IBM's contributions to this file may be relicensed under LGPLv2 or later. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/rcu.h" ++#include "qemu/atomic.h" ++#include "qemu/thread.h" ++#include "qemu/main-loop.h" ++#include "qemu/lockable.h" ++#if defined(CONFIG_MALLOC_TRIM) ++#include ++#endif ++ ++/* ++ * Global grace period counter. Bit 0 is always one in rcu_gp_ctr. ++ * Bits 1 and above are defined in synchronize_rcu. ++ */ ++#define RCU_GP_LOCKED (1UL << 0) ++#define RCU_GP_CTR (1UL << 1) ++ ++unsigned long rcu_gp_ctr = RCU_GP_LOCKED; ++ ++QemuEvent rcu_gp_event; ++static int in_drain_call_rcu; ++static QemuMutex rcu_registry_lock; ++static QemuMutex rcu_sync_lock; ++ ++/* ++ * Check whether a quiescent state was crossed between the beginning of ++ * update_counter_and_wait and now. ++ */ ++static inline int rcu_gp_ongoing(unsigned long *ctr) ++{ ++ unsigned long v; ++ ++ v = qatomic_read(ctr); ++ return v && (v != rcu_gp_ctr); ++} ++ ++/* Written to only by each individual reader. Read by both the reader and the ++ * writers. ++ */ ++QEMU_DEFINE_CO_TLS(struct rcu_reader_data, rcu_reader) ++ ++/* Protected by rcu_registry_lock. */ ++typedef QLIST_HEAD(, rcu_reader_data) ThreadList; ++static ThreadList registry = QLIST_HEAD_INITIALIZER(registry); ++ ++/* Wait for previous parity/grace period to be empty of readers. */ ++static void wait_for_readers(void) ++{ ++ ThreadList qsreaders = QLIST_HEAD_INITIALIZER(qsreaders); ++ struct rcu_reader_data *index, *tmp; ++ ++ for (;;) { ++ /* We want to be notified of changes made to rcu_gp_ongoing ++ * while we walk the list. ++ */ ++ qemu_event_reset(&rcu_gp_event); ++ ++ QLIST_FOREACH(index, ®istry, node) { ++ qatomic_set(&index->waiting, true); ++ } ++ ++ /* Here, order the stores to index->waiting before the loads of ++ * index->ctr. Pairs with smp_mb_placeholder() in rcu_read_unlock(), ++ * ensuring that the loads of index->ctr are sequentially consistent. ++ * ++ * If this is the last iteration, this barrier also prevents ++ * frees from seeping upwards, and orders the two wait phases ++ * on architectures with 32-bit longs; see synchronize_rcu(). ++ */ ++ smp_mb_global(); ++ ++ QLIST_FOREACH_SAFE(index, ®istry, node, tmp) { ++ if (!rcu_gp_ongoing(&index->ctr)) { ++ QLIST_REMOVE(index, node); ++ QLIST_INSERT_HEAD(&qsreaders, index, node); ++ ++ /* No need for memory barriers here, worst of all we ++ * get some extra futex wakeups. ++ */ ++ qatomic_set(&index->waiting, false); ++ } else if (qatomic_read(&in_drain_call_rcu)) { ++ notifier_list_notify(&index->force_rcu, NULL); ++ } ++ } ++ ++ if (QLIST_EMPTY(®istry)) { ++ break; ++ } ++ ++ /* Wait for one thread to report a quiescent state and try again. ++ * Release rcu_registry_lock, so rcu_(un)register_thread() doesn't ++ * wait too much time. ++ * ++ * rcu_register_thread() may add nodes to ®istry; it will not ++ * wake up synchronize_rcu, but that is okay because at least another ++ * thread must exit its RCU read-side critical section before ++ * synchronize_rcu is done. The next iteration of the loop will ++ * move the new thread's rcu_reader from ®istry to &qsreaders, ++ * because rcu_gp_ongoing() will return false. ++ * ++ * rcu_unregister_thread() may remove nodes from &qsreaders instead ++ * of ®istry if it runs during qemu_event_wait. That's okay; ++ * the node then will not be added back to ®istry by QLIST_SWAP ++ * below. The invariant is that the node is part of one list when ++ * rcu_registry_lock is released. ++ */ ++ qemu_mutex_unlock(&rcu_registry_lock); ++ qemu_event_wait(&rcu_gp_event); ++ qemu_mutex_lock(&rcu_registry_lock); ++ } ++ ++ /* put back the reader list in the registry */ ++ QLIST_SWAP(®istry, &qsreaders, node); ++} ++ ++void synchronize_rcu(void) ++{ ++ QEMU_LOCK_GUARD(&rcu_sync_lock); ++ ++ /* Write RCU-protected pointers before reading p_rcu_reader->ctr. ++ * Pairs with smp_mb_placeholder() in rcu_read_lock(). ++ * ++ * Also orders write to RCU-protected pointers before ++ * write to rcu_gp_ctr. ++ */ ++ smp_mb_global(); ++ ++ QEMU_LOCK_GUARD(&rcu_registry_lock); ++ if (!QLIST_EMPTY(®istry)) { ++ if (sizeof(rcu_gp_ctr) < 8) { ++ /* For architectures with 32-bit longs, a two-subphases algorithm ++ * ensures we do not encounter overflow bugs. ++ * ++ * Switch parity: 0 -> 1, 1 -> 0. ++ */ ++ qatomic_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR); ++ wait_for_readers(); ++ qatomic_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR); ++ } else { ++ /* Increment current grace period. */ ++ qatomic_set(&rcu_gp_ctr, rcu_gp_ctr + RCU_GP_CTR); ++ } ++ ++ wait_for_readers(); ++ } ++} ++ ++ ++#define RCU_CALL_MIN_SIZE 30 ++ ++/* Multi-producer, single-consumer queue based on urcu/static/wfqueue.h ++ * from liburcu. Note that head is only used by the consumer. ++ */ ++static struct rcu_head dummy; ++static struct rcu_head *head = &dummy, **tail = &dummy.next; ++static int rcu_call_count; ++static QemuEvent rcu_call_ready_event; ++ ++static void enqueue(struct rcu_head *node) ++{ ++ struct rcu_head **old_tail; ++ ++ node->next = NULL; ++ ++ /* ++ * Make this node the tail of the list. The node will be ++ * used by further enqueue operations, but it will not ++ * be dequeued yet... ++ */ ++ old_tail = qatomic_xchg(&tail, &node->next); ++ ++ /* ++ * ... until it is pointed to from another item in the list. ++ * In the meantime, try_dequeue() will find a NULL next pointer ++ * and loop. ++ * ++ * Synchronizes with qatomic_load_acquire() in try_dequeue(). ++ */ ++ qatomic_store_release(old_tail, node); ++} ++ ++static struct rcu_head *try_dequeue(void) ++{ ++ struct rcu_head *node, *next; ++ ++retry: ++ /* Head is only written by this thread, so no need for barriers. */ ++ node = head; ++ ++ /* ++ * If the head node has NULL in its next pointer, the value is ++ * wrong and we need to wait until its enqueuer finishes the update. ++ */ ++ next = qatomic_load_acquire(&node->next); ++ if (!next) { ++ return NULL; ++ } ++ ++ /* ++ * Test for an empty list, which we do not expect. Note that for ++ * the consumer head and tail are always consistent. The head ++ * is consistent because only the consumer reads/writes it. ++ * The tail, because it is the first step in the enqueuing. ++ * It is only the next pointers that might be inconsistent. ++ */ ++ if (head == &dummy && qatomic_read(&tail) == &dummy.next) { ++ abort(); ++ } ++ ++ /* ++ * Since we are the sole consumer, and we excluded the empty case ++ * above, the queue will always have at least two nodes: the ++ * dummy node, and the one being removed. So we do not need to update ++ * the tail pointer. ++ */ ++ head = next; ++ ++ /* If we dequeued the dummy node, add it back at the end and retry. */ ++ if (node == &dummy) { ++ enqueue(node); ++ goto retry; ++ } ++ ++ return node; ++} ++ ++static void *call_rcu_thread(void *opaque) ++{ ++ struct rcu_head *node; ++ ++ rcu_register_thread(); ++ ++ for (;;) { ++ int tries = 0; ++ int n = qatomic_read(&rcu_call_count); ++ ++ /* Heuristically wait for a decent number of callbacks to pile up. ++ * Fetch rcu_call_count now, we only must process elements that were ++ * added before synchronize_rcu() starts. ++ */ ++ while (n == 0 || (n < RCU_CALL_MIN_SIZE && ++tries <= 5)) { ++ g_usleep(10000); ++ if (n == 0) { ++ qemu_event_reset(&rcu_call_ready_event); ++ n = qatomic_read(&rcu_call_count); ++ if (n == 0) { ++#if defined(CONFIG_MALLOC_TRIM) ++ malloc_trim(4 * 1024 * 1024); ++#endif ++ qemu_event_wait(&rcu_call_ready_event); ++ } ++ } ++ n = qatomic_read(&rcu_call_count); ++ } ++ ++ qatomic_sub(&rcu_call_count, n); ++ synchronize_rcu(); ++ bql_lock(); ++ while (n > 0) { ++ node = try_dequeue(); ++ while (!node) { ++ bql_unlock(); ++ qemu_event_reset(&rcu_call_ready_event); ++ node = try_dequeue(); ++ if (!node) { ++ qemu_event_wait(&rcu_call_ready_event); ++ node = try_dequeue(); ++ } ++ bql_lock(); ++ } ++ ++ n--; ++ node->func(node); ++ } ++ bql_unlock(); ++ } ++ abort(); ++} ++ ++void call_rcu1(struct rcu_head *node, void (*func)(struct rcu_head *node)) ++{ ++ node->func = func; ++ enqueue(node); ++ qatomic_inc(&rcu_call_count); ++ qemu_event_set(&rcu_call_ready_event); ++} ++ ++ ++struct rcu_drain { ++ struct rcu_head rcu; ++ QemuEvent drain_complete_event; ++}; ++ ++static void drain_rcu_callback(struct rcu_head *node) ++{ ++ struct rcu_drain *event = (struct rcu_drain *)node; ++ qemu_event_set(&event->drain_complete_event); ++} ++ ++/* ++ * This function ensures that all pending RCU callbacks ++ * on the current thread are done executing ++ ++ * drops big qemu lock during the wait to allow RCU thread ++ * to process the callbacks ++ * ++ */ ++ ++void drain_call_rcu(void) ++{ ++ struct rcu_drain rcu_drain; ++ bool locked = bql_locked(); ++ ++ memset(&rcu_drain, 0, sizeof(struct rcu_drain)); ++ qemu_event_init(&rcu_drain.drain_complete_event, false); ++ ++ if (locked) { ++ bql_unlock(); ++ } ++ ++ ++ /* ++ * RCU callbacks are invoked in the same order as in which they ++ * are registered, thus we can be sure that when 'drain_rcu_callback' ++ * is called, all RCU callbacks that were registered on this thread ++ * prior to calling this function are completed. ++ * ++ * Note that since we have only one global queue of the RCU callbacks, ++ * we also end up waiting for most of RCU callbacks that were registered ++ * on the other threads, but this is a side effect that shouldn't be ++ * assumed. ++ */ ++ ++ qatomic_inc(&in_drain_call_rcu); ++ call_rcu1(&rcu_drain.rcu, drain_rcu_callback); ++ qemu_event_wait(&rcu_drain.drain_complete_event); ++ qatomic_dec(&in_drain_call_rcu); ++ ++ if (locked) { ++ bql_lock(); ++ } ++ ++} ++ ++void rcu_register_thread(void) ++{ ++ assert(get_ptr_rcu_reader()->ctr == 0); ++ qemu_mutex_lock(&rcu_registry_lock); ++ QLIST_INSERT_HEAD(®istry, get_ptr_rcu_reader(), node); ++ qemu_mutex_unlock(&rcu_registry_lock); ++} ++ ++void rcu_unregister_thread(void) ++{ ++ qemu_mutex_lock(&rcu_registry_lock); ++ QLIST_REMOVE(get_ptr_rcu_reader(), node); ++ qemu_mutex_unlock(&rcu_registry_lock); ++} ++ ++void rcu_add_force_rcu_notifier(Notifier *n) ++{ ++ qemu_mutex_lock(&rcu_registry_lock); ++ notifier_list_add(&get_ptr_rcu_reader()->force_rcu, n); ++ qemu_mutex_unlock(&rcu_registry_lock); ++} ++ ++void rcu_remove_force_rcu_notifier(Notifier *n) ++{ ++ qemu_mutex_lock(&rcu_registry_lock); ++ notifier_remove(n); ++ qemu_mutex_unlock(&rcu_registry_lock); ++} ++ ++static void rcu_init_complete(void) ++{ ++ QemuThread thread; ++ ++ qemu_mutex_init(&rcu_registry_lock); ++ qemu_mutex_init(&rcu_sync_lock); ++ qemu_event_init(&rcu_gp_event, true); ++ ++ qemu_event_init(&rcu_call_ready_event, false); ++ ++ /* The caller is assumed to have BQL, so the call_rcu thread ++ * must have been quiescent even after forking, just recreate it. ++ */ ++ qemu_thread_create(&thread, "call_rcu", call_rcu_thread, ++ NULL, QEMU_THREAD_DETACHED); ++ ++ rcu_register_thread(); ++} ++ ++static int atfork_depth = 1; ++ ++void rcu_enable_atfork(void) ++{ ++ atfork_depth++; ++} ++ ++void rcu_disable_atfork(void) ++{ ++ atfork_depth--; ++} ++ ++#ifdef CONFIG_POSIX ++static void rcu_init_lock(void) ++{ ++ if (atfork_depth < 1) { ++ return; ++ } ++ ++ qemu_mutex_lock(&rcu_sync_lock); ++ qemu_mutex_lock(&rcu_registry_lock); ++} ++ ++static void rcu_init_unlock(void) ++{ ++ if (atfork_depth < 1) { ++ return; ++ } ++ ++ qemu_mutex_unlock(&rcu_registry_lock); ++ qemu_mutex_unlock(&rcu_sync_lock); ++} ++ ++static void rcu_init_child(void) ++{ ++ if (atfork_depth < 1) { ++ return; ++ } ++ ++ memset(®istry, 0, sizeof(registry)); ++ rcu_init_complete(); ++} ++#endif ++ ++static void __attribute__((__constructor__)) rcu_init(void) ++{ ++ smp_mb_global_init(); ++#ifdef CONFIG_POSIX ++ pthread_atfork(rcu_init_lock, rcu_init_unlock, rcu_init_child); ++#endif ++ rcu_init_complete(); ++} +diff --git a/qcow2/lib/util/stats64.c b/qcow2/lib/util/stats64.c +new file mode 100644 +index 00000000..09736014 +--- /dev/null ++++ b/qcow2/lib/util/stats64.c +@@ -0,0 +1,148 @@ ++/* ++ * Atomic operations on 64-bit quantities. ++ * ++ * Copyright (C) 2017 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/atomic.h" ++#include "qemu/stats64.h" ++#include "qemu/processor.h" ++ ++#ifndef CONFIG_ATOMIC64 ++static inline void stat64_rdlock(Stat64 *s) ++{ ++ /* Keep out incoming writers to avoid them starving us. */ ++ qatomic_add(&s->lock, 2); ++ ++ /* If there is a concurrent writer, wait for it. */ ++ while (qatomic_read(&s->lock) & 1) { ++ cpu_relax(); ++ } ++} ++ ++static inline void stat64_rdunlock(Stat64 *s) ++{ ++ qatomic_sub(&s->lock, 2); ++} ++ ++static inline bool stat64_wrtrylock(Stat64 *s) ++{ ++ return qatomic_cmpxchg(&s->lock, 0, 1) == 0; ++} ++ ++static inline void stat64_wrunlock(Stat64 *s) ++{ ++ qatomic_dec(&s->lock); ++} ++ ++uint64_t stat64_get(const Stat64 *s) ++{ ++ uint32_t high, low; ++ ++ stat64_rdlock((Stat64 *)s); ++ ++ /* 64-bit writes always take the lock, so we can read in ++ * any order. ++ */ ++ high = qatomic_read(&s->high); ++ low = qatomic_read(&s->low); ++ stat64_rdunlock((Stat64 *)s); ++ ++ return ((uint64_t)high << 32) | low; ++} ++ ++void stat64_set(Stat64 *s, uint64_t val) ++{ ++ while (!stat64_wrtrylock(s)) { ++ cpu_relax(); ++ } ++ ++ qatomic_set(&s->high, val >> 32); ++ qatomic_set(&s->low, val); ++ stat64_wrunlock(s); ++} ++ ++bool stat64_add32_carry(Stat64 *s, uint32_t low, uint32_t high) ++{ ++ uint32_t old; ++ ++ if (!stat64_wrtrylock(s)) { ++ cpu_relax(); ++ return false; ++ } ++ ++ /* 64-bit reads always take the lock, so they don't care about the ++ * order of our update. By updating s->low first, we can check ++ * whether we have to carry into s->high. ++ */ ++ old = qatomic_fetch_add(&s->low, low); ++ high += (old + low) < old; ++ qatomic_add(&s->high, high); ++ stat64_wrunlock(s); ++ return true; ++} ++ ++bool stat64_min_slow(Stat64 *s, uint64_t value) ++{ ++ uint32_t high, low; ++ uint64_t orig; ++ ++ if (!stat64_wrtrylock(s)) { ++ cpu_relax(); ++ return false; ++ } ++ ++ high = qatomic_read(&s->high); ++ low = qatomic_read(&s->low); ++ ++ orig = ((uint64_t)high << 32) | low; ++ if (value < orig) { ++ /* We have to set low before high, just like stat64_min reads ++ * high before low. The value may become higher temporarily, but ++ * stat64_get does not notice (it takes the lock) and the only ill ++ * effect on stat64_min is that the slow path may be triggered ++ * unnecessarily. ++ */ ++ qatomic_set(&s->low, (uint32_t)value); ++ smp_wmb(); ++ qatomic_set(&s->high, value >> 32); ++ } ++ stat64_wrunlock(s); ++ return true; ++} ++ ++bool stat64_max_slow(Stat64 *s, uint64_t value) ++{ ++ uint32_t high, low; ++ uint64_t orig; ++ ++ if (!stat64_wrtrylock(s)) { ++ cpu_relax(); ++ return false; ++ } ++ ++ high = qatomic_read(&s->high); ++ low = qatomic_read(&s->low); ++ ++ orig = ((uint64_t)high << 32) | low; ++ if (value > orig) { ++ /* We have to set low before high, just like stat64_max reads ++ * high before low. The value may become lower temporarily, but ++ * stat64_get does not notice (it takes the lock) and the only ill ++ * effect on stat64_max is that the slow path may be triggered ++ * unnecessarily. ++ */ ++ qatomic_set(&s->low, (uint32_t)value); ++ smp_wmb(); ++ qatomic_set(&s->high, value >> 32); ++ } ++ stat64_wrunlock(s); ++ return true; ++} ++#endif +diff --git a/qcow2/lib/util/thread-pool.c b/qcow2/lib/util/thread-pool.c +new file mode 100644 +index 00000000..27eb777e +--- /dev/null ++++ b/qcow2/lib/util/thread-pool.c +@@ -0,0 +1,381 @@ ++/* ++ * QEMU block layer thread pool ++ * ++ * Copyright IBM, Corp. 2008 ++ * Copyright Red Hat, Inc. 2012 ++ * ++ * Authors: ++ * Anthony Liguori ++ * Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++#include "qemu/osdep.h" ++#include "qemu/defer-call.h" ++#include "qemu/queue.h" ++#include "qemu/thread.h" ++#include "qemu/coroutine.h" ++#include "trace.h" ++#include "block/thread-pool.h" ++#include "qemu/main-loop.h" ++ ++static void do_spawn_thread(ThreadPool *pool); ++ ++typedef struct ThreadPoolElement ThreadPoolElement; ++ ++enum ThreadState { ++ THREAD_QUEUED, ++ THREAD_ACTIVE, ++ THREAD_DONE, ++}; ++ ++struct ThreadPoolElement { ++ BlockAIOCB common; ++ ThreadPool *pool; ++ ThreadPoolFunc *func; ++ void *arg; ++ ++ /* Moving state out of THREAD_QUEUED is protected by lock. After ++ * that, only the worker thread can write to it. Reads and writes ++ * of state and ret are ordered with memory barriers. ++ */ ++ enum ThreadState state; ++ int ret; ++ ++ /* Access to this list is protected by lock. */ ++ QTAILQ_ENTRY(ThreadPoolElement) reqs; ++ ++ /* This list is only written by the thread pool's mother thread. */ ++ QLIST_ENTRY(ThreadPoolElement) all; ++}; ++ ++struct ThreadPool { ++ AioContext *ctx; ++ QEMUBH *completion_bh; ++ QemuMutex lock; ++ QemuCond worker_stopped; ++ QemuCond request_cond; ++ QEMUBH *new_thread_bh; ++ ++ /* The following variables are only accessed from one AioContext. */ ++ QLIST_HEAD(, ThreadPoolElement) head; ++ ++ /* The following variables are protected by lock. */ ++ QTAILQ_HEAD(, ThreadPoolElement) request_list; ++ int cur_threads; ++ int idle_threads; ++ int new_threads; /* backlog of threads we need to create */ ++ int pending_threads; /* threads created but not running yet */ ++ int min_threads; ++ int max_threads; ++}; ++ ++static void *worker_thread(void *opaque) ++{ ++ ThreadPool *pool = opaque; ++ ++ qemu_mutex_lock(&pool->lock); ++ pool->pending_threads--; ++ do_spawn_thread(pool); ++ ++ while (pool->cur_threads <= pool->max_threads) { ++ ThreadPoolElement *req; ++ int ret; ++ ++ if (QTAILQ_EMPTY(&pool->request_list)) { ++ pool->idle_threads++; ++ ret = qemu_cond_timedwait(&pool->request_cond, &pool->lock, 10000); ++ pool->idle_threads--; ++ if (ret == 0 && ++ QTAILQ_EMPTY(&pool->request_list) && ++ pool->cur_threads > pool->min_threads) { ++ /* Timed out + no work to do + no need for warm threads = exit. */ ++ break; ++ } ++ /* ++ * Even if there was some work to do, check if there aren't ++ * too many worker threads before picking it up. ++ */ ++ continue; ++ } ++ ++ req = QTAILQ_FIRST(&pool->request_list); ++ QTAILQ_REMOVE(&pool->request_list, req, reqs); ++ req->state = THREAD_ACTIVE; ++ qemu_mutex_unlock(&pool->lock); ++ ++ ret = req->func(req->arg); ++ ++ req->ret = ret; ++ /* Write ret before state. */ ++ smp_wmb(); ++ req->state = THREAD_DONE; ++ ++ qemu_bh_schedule(pool->completion_bh); ++ qemu_mutex_lock(&pool->lock); ++ } ++ ++ pool->cur_threads--; ++ qemu_cond_signal(&pool->worker_stopped); ++ ++ /* ++ * Wake up another thread, in case we got a wakeup but decided ++ * to exit due to pool->cur_threads > pool->max_threads. ++ */ ++ qemu_cond_signal(&pool->request_cond); ++ qemu_mutex_unlock(&pool->lock); ++ return NULL; ++} ++ ++static void do_spawn_thread(ThreadPool *pool) ++{ ++ QemuThread t; ++ ++ /* Runs with lock taken. */ ++ if (!pool->new_threads) { ++ return; ++ } ++ ++ pool->new_threads--; ++ pool->pending_threads++; ++ ++ qemu_thread_create(&t, "worker", worker_thread, pool, QEMU_THREAD_DETACHED); ++} ++ ++static void spawn_thread_bh_fn(void *opaque) ++{ ++ ThreadPool *pool = opaque; ++ ++ qemu_mutex_lock(&pool->lock); ++ do_spawn_thread(pool); ++ qemu_mutex_unlock(&pool->lock); ++} ++ ++static void spawn_thread(ThreadPool *pool) ++{ ++ pool->cur_threads++; ++ pool->new_threads++; ++ /* If there are threads being created, they will spawn new workers, so ++ * we don't spend time creating many threads in a loop holding a mutex or ++ * starving the current vcpu. ++ * ++ * If there are no idle threads, ask the main thread to create one, so we ++ * inherit the correct affinity instead of the vcpu affinity. ++ */ ++ if (!pool->pending_threads) { ++ qemu_bh_schedule(pool->new_thread_bh); ++ } ++} ++ ++static void thread_pool_completion_bh(void *opaque) ++{ ++ ThreadPool *pool = opaque; ++ ThreadPoolElement *elem, *next; ++ ++ defer_call_begin(); /* cb() may use defer_call() to coalesce work */ ++ ++restart: ++ QLIST_FOREACH_SAFE(elem, &pool->head, all, next) { ++ if (elem->state != THREAD_DONE) { ++ continue; ++ } ++ ++ trace_thread_pool_complete(pool, elem, elem->common.opaque, ++ elem->ret); ++ QLIST_REMOVE(elem, all); ++ ++ if (elem->common.cb) { ++ /* Read state before ret. */ ++ smp_rmb(); ++ ++ /* Schedule ourselves in case elem->common.cb() calls aio_poll() to ++ * wait for another request that completed at the same time. ++ */ ++ qemu_bh_schedule(pool->completion_bh); ++ ++ elem->common.cb(elem->common.opaque, elem->ret); ++ ++ /* We can safely cancel the completion_bh here regardless of someone ++ * else having scheduled it meanwhile because we reenter the ++ * completion function anyway (goto restart). ++ */ ++ qemu_bh_cancel(pool->completion_bh); ++ ++ qemu_aio_unref(elem); ++ goto restart; ++ } else { ++ qemu_aio_unref(elem); ++ } ++ } ++ ++ defer_call_end(); ++} ++ ++static void thread_pool_cancel(BlockAIOCB *acb) ++{ ++ ThreadPoolElement *elem = (ThreadPoolElement *)acb; ++ ThreadPool *pool = elem->pool; ++ ++ trace_thread_pool_cancel(elem, elem->common.opaque); ++ ++ QEMU_LOCK_GUARD(&pool->lock); ++ if (elem->state == THREAD_QUEUED) { ++ QTAILQ_REMOVE(&pool->request_list, elem, reqs); ++ qemu_bh_schedule(pool->completion_bh); ++ ++ elem->state = THREAD_DONE; ++ elem->ret = -ECANCELED; ++ } ++ ++} ++ ++static const AIOCBInfo thread_pool_aiocb_info = { ++ .aiocb_size = sizeof(ThreadPoolElement), ++ .cancel_async = thread_pool_cancel, ++}; ++ ++BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ ThreadPoolElement *req; ++ AioContext *ctx = qemu_get_current_aio_context(); ++ ThreadPool *pool = aio_get_thread_pool(ctx); ++ ++ /* Assert that the thread submitting work is the same running the pool */ ++ assert(pool->ctx == qemu_get_current_aio_context()); ++ ++ req = qemu_aio_get(&thread_pool_aiocb_info, NULL, cb, opaque); ++ req->func = func; ++ req->arg = arg; ++ req->state = THREAD_QUEUED; ++ req->pool = pool; ++ ++ QLIST_INSERT_HEAD(&pool->head, req, all); ++ ++ trace_thread_pool_submit(pool, req, arg); ++ ++ qemu_mutex_lock(&pool->lock); ++ if (pool->idle_threads == 0 && pool->cur_threads < pool->max_threads) { ++ spawn_thread(pool); ++ } ++ QTAILQ_INSERT_TAIL(&pool->request_list, req, reqs); ++ qemu_mutex_unlock(&pool->lock); ++ qemu_cond_signal(&pool->request_cond); ++ return &req->common; ++} ++ ++typedef struct ThreadPoolCo { ++ Coroutine *co; ++ int ret; ++} ThreadPoolCo; ++ ++static void thread_pool_co_cb(void *opaque, int ret) ++{ ++ ThreadPoolCo *co = opaque; ++ ++ co->ret = ret; ++ aio_co_wake(co->co); ++} ++ ++int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg) ++{ ++ ThreadPoolCo tpc = { .co = qemu_coroutine_self(), .ret = -EINPROGRESS }; ++ assert(qemu_in_coroutine()); ++ thread_pool_submit_aio(func, arg, thread_pool_co_cb, &tpc); ++ qemu_coroutine_yield(); ++ return tpc.ret; ++} ++ ++void thread_pool_submit(ThreadPoolFunc *func, void *arg) ++{ ++ thread_pool_submit_aio(func, arg, NULL, NULL); ++} ++ ++void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) ++{ ++ qemu_mutex_lock(&pool->lock); ++ ++ pool->min_threads = ctx->thread_pool_min; ++ pool->max_threads = ctx->thread_pool_max; ++ ++ /* ++ * We either have to: ++ * - Increase the number available of threads until over the min_threads ++ * threshold. ++ * - Bump the worker threads so that they exit, until under the max_threads ++ * threshold. ++ * - Do nothing. The current number of threads fall in between the min and ++ * max thresholds. We'll let the pool manage itself. ++ */ ++ for (int i = pool->cur_threads; i < pool->min_threads; i++) { ++ spawn_thread(pool); ++ } ++ ++ for (int i = pool->cur_threads; i > pool->max_threads; i--) { ++ qemu_cond_signal(&pool->request_cond); ++ } ++ ++ qemu_mutex_unlock(&pool->lock); ++} ++ ++static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx) ++{ ++ if (!ctx) { ++ ctx = qemu_get_aio_context(); ++ } ++ ++ memset(pool, 0, sizeof(*pool)); ++ pool->ctx = ctx; ++ pool->completion_bh = aio_bh_new(ctx, thread_pool_completion_bh, pool); ++ qemu_mutex_init(&pool->lock); ++ qemu_cond_init(&pool->worker_stopped); ++ qemu_cond_init(&pool->request_cond); ++ pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool); ++ ++ QLIST_INIT(&pool->head); ++ QTAILQ_INIT(&pool->request_list); ++ ++ thread_pool_update_params(pool, ctx); ++} ++ ++ThreadPool *thread_pool_new(AioContext *ctx) ++{ ++ ThreadPool *pool = g_new(ThreadPool, 1); ++ thread_pool_init_one(pool, ctx); ++ return pool; ++} ++ ++void thread_pool_free(ThreadPool *pool) ++{ ++ if (!pool) { ++ return; ++ } ++ ++ assert(QLIST_EMPTY(&pool->head)); ++ ++ qemu_mutex_lock(&pool->lock); ++ ++ /* Stop new threads from spawning */ ++ qemu_bh_delete(pool->new_thread_bh); ++ pool->cur_threads -= pool->new_threads; ++ pool->new_threads = 0; ++ ++ /* Wait for worker threads to terminate */ ++ pool->max_threads = 0; ++ qemu_cond_broadcast(&pool->request_cond); ++ while (pool->cur_threads > 0) { ++ qemu_cond_wait(&pool->worker_stopped, &pool->lock); ++ } ++ ++ qemu_mutex_unlock(&pool->lock); ++ ++ qemu_bh_delete(pool->completion_bh); ++ qemu_cond_destroy(&pool->request_cond); ++ qemu_cond_destroy(&pool->worker_stopped); ++ qemu_mutex_destroy(&pool->lock); ++ g_free(pool); ++} +diff --git a/qcow2/lib/util/timed-average.c b/qcow2/lib/util/timed-average.c +new file mode 100644 +index 00000000..2b49d532 +--- /dev/null ++++ b/qcow2/lib/util/timed-average.c +@@ -0,0 +1,231 @@ ++/* ++ * QEMU timed average computation ++ * ++ * Copyright (C) Nodalink, EURL. 2014 ++ * Copyright (C) Igalia, S.L. 2015 ++ * ++ * Authors: ++ * Benoît Canet ++ * Alberto Garcia ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 2 of the License, or ++ * (at your option) version 3 or any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "qemu/timed-average.h" ++ ++/* This module computes an average of a set of values within a time ++ * window. ++ * ++ * Algorithm: ++ * ++ * - Create two windows with a certain expiration period, and ++ * offsetted by period / 2. ++ * - Each time you want to account a new value, do it in both windows. ++ * - The minimum / maximum / average values are always returned from ++ * the oldest window. ++ * ++ * Example: ++ * ++ * t=0 |t=0.5 |t=1 |t=1.5 |t=2 ++ * wnd0: [0,0.5)|wnd0: [0.5,1.5) | |wnd0: [1.5,2.5) | ++ * wnd1: [0,1) | |wnd1: [1,2) | | ++ * ++ * Values are returned from: ++ * ++ * wnd0---------|wnd1------------|wnd0---------|wnd1-------------| ++ */ ++ ++/* Update the expiration of a time window ++ * ++ * @w: the window used ++ * @now: the current time in nanoseconds ++ * @period: the expiration period in nanoseconds ++ */ ++static void update_expiration(TimedAverageWindow *w, int64_t now, ++ int64_t period) ++{ ++ /* time elapsed since the last theoretical expiration */ ++ int64_t elapsed = (now - w->expiration) % period; ++ /* time remaininging until the next expiration */ ++ int64_t remaining = period - elapsed; ++ /* compute expiration */ ++ w->expiration = now + remaining; ++} ++ ++/* Reset a window ++ * ++ * @w: the window to reset ++ */ ++static void window_reset(TimedAverageWindow *w) ++{ ++ w->min = UINT64_MAX; ++ w->max = 0; ++ w->sum = 0; ++ w->count = 0; ++} ++ ++/* Get the current window (that is, the one with the earliest ++ * expiration time). ++ * ++ * @ta: the TimedAverage structure ++ * @ret: a pointer to the current window ++ */ ++static TimedAverageWindow *current_window(TimedAverage *ta) ++{ ++ return &ta->windows[ta->current]; ++} ++ ++/* Initialize a TimedAverage structure ++ * ++ * @ta: the TimedAverage structure ++ * @clock_type: the type of clock to use ++ * @period: the time window period in nanoseconds ++ */ ++void timed_average_init(TimedAverage *ta, QEMUClockType clock_type, ++ uint64_t period) ++{ ++ int64_t now = qemu_clock_get_ns(clock_type); ++ ++ /* Returned values are from the oldest window, so they belong to ++ * the interval [ta->period/2,ta->period). By adjusting the ++ * requested period by 4/3, we guarantee that they're in the ++ * interval [2/3 period,4/3 period), closer to the requested ++ * period on average */ ++ ta->period = (uint64_t) period * 4 / 3; ++ ta->clock_type = clock_type; ++ ta->current = 0; ++ ++ window_reset(&ta->windows[0]); ++ window_reset(&ta->windows[1]); ++ ++ /* Both windows are offsetted by half a period */ ++ ta->windows[0].expiration = now + ta->period / 2; ++ ta->windows[1].expiration = now + ta->period; ++} ++ ++/* Check if the time windows have expired, updating their counters and ++ * expiration time if that's the case. ++ * ++ * @ta: the TimedAverage structure ++ * @elapsed: if non-NULL, the elapsed time (in ns) within the current ++ * window will be stored here ++ */ ++static void check_expirations(TimedAverage *ta, uint64_t *elapsed) ++{ ++ int64_t now = qemu_clock_get_ns(ta->clock_type); ++ int i; ++ ++ assert(ta->period != 0); ++ ++ /* Check if the windows have expired */ ++ for (i = 0; i < 2; i++) { ++ TimedAverageWindow *w = &ta->windows[i]; ++ if (w->expiration <= now) { ++ window_reset(w); ++ update_expiration(w, now, ta->period); ++ } ++ } ++ ++ /* Make ta->current point to the oldest window */ ++ if (ta->windows[0].expiration < ta->windows[1].expiration) { ++ ta->current = 0; ++ } else { ++ ta->current = 1; ++ } ++ ++ /* Calculate the elapsed time within the current window */ ++ if (elapsed) { ++ int64_t remaining = ta->windows[ta->current].expiration - now; ++ *elapsed = ta->period - remaining; ++ } ++} ++ ++/* Account a value ++ * ++ * @ta: the TimedAverage structure ++ * @value: the value to account ++ */ ++void timed_average_account(TimedAverage *ta, uint64_t value) ++{ ++ int i; ++ check_expirations(ta, NULL); ++ ++ /* Do the accounting in both windows at the same time */ ++ for (i = 0; i < 2; i++) { ++ TimedAverageWindow *w = &ta->windows[i]; ++ ++ w->sum += value; ++ w->count++; ++ ++ if (value < w->min) { ++ w->min = value; ++ } ++ ++ if (value > w->max) { ++ w->max = value; ++ } ++ } ++} ++ ++/* Get the minimum value ++ * ++ * @ta: the TimedAverage structure ++ * @ret: the minimum value ++ */ ++uint64_t timed_average_min(TimedAverage *ta) ++{ ++ TimedAverageWindow *w; ++ check_expirations(ta, NULL); ++ w = current_window(ta); ++ return w->min < UINT64_MAX ? w->min : 0; ++} ++ ++/* Get the average value ++ * ++ * @ta: the TimedAverage structure ++ * @ret: the average value ++ */ ++uint64_t timed_average_avg(TimedAverage *ta) ++{ ++ TimedAverageWindow *w; ++ check_expirations(ta, NULL); ++ w = current_window(ta); ++ return w->count > 0 ? w->sum / w->count : 0; ++} ++ ++/* Get the maximum value ++ * ++ * @ta: the TimedAverage structure ++ * @ret: the maximum value ++ */ ++uint64_t timed_average_max(TimedAverage *ta) ++{ ++ check_expirations(ta, NULL); ++ return current_window(ta)->max; ++} ++ ++/* Get the sum of all accounted values ++ * @ta: the TimedAverage structure ++ * @elapsed: if non-NULL, the elapsed time (in ns) will be stored here ++ * @ret: the sum of all accounted values ++ */ ++uint64_t timed_average_sum(TimedAverage *ta, uint64_t *elapsed) ++{ ++ TimedAverageWindow *w; ++ check_expirations(ta, elapsed); ++ w = current_window(ta); ++ return w->sum; ++} +diff --git a/qcow2/lib/util/transactions.c b/qcow2/lib/util/transactions.c +new file mode 100644 +index 00000000..2dbdedce +--- /dev/null ++++ b/qcow2/lib/util/transactions.c +@@ -0,0 +1,100 @@ ++/* ++ * Simple transactions API ++ * ++ * Copyright (c) 2021 Virtuozzo International GmbH. ++ * ++ * Author: ++ * Sementsov-Ogievskiy Vladimir ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "qemu/transactions.h" ++#include "qemu/queue.h" ++ ++typedef struct TransactionAction { ++ TransactionActionDrv *drv; ++ void *opaque; ++ QSLIST_ENTRY(TransactionAction) entry; ++} TransactionAction; ++ ++struct Transaction { ++ QSLIST_HEAD(, TransactionAction) actions; ++}; ++ ++Transaction *tran_new(void) ++{ ++ Transaction *tran = g_new(Transaction, 1); ++ ++ QSLIST_INIT(&tran->actions); ++ ++ return tran; ++} ++ ++void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque) ++{ ++ TransactionAction *act; ++ ++ act = g_new(TransactionAction, 1); ++ *act = (TransactionAction) { ++ .drv = drv, ++ .opaque = opaque ++ }; ++ ++ QSLIST_INSERT_HEAD(&tran->actions, act, entry); ++} ++ ++void tran_abort(Transaction *tran) ++{ ++ TransactionAction *act, *next; ++ ++ QSLIST_FOREACH(act, &tran->actions, entry) { ++ if (act->drv->abort) { ++ act->drv->abort(act->opaque); ++ } ++ } ++ ++ QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) { ++ if (act->drv->clean) { ++ act->drv->clean(act->opaque); ++ } ++ ++ g_free(act); ++ } ++ ++ g_free(tran); ++} ++ ++void tran_commit(Transaction *tran) ++{ ++ TransactionAction *act, *next; ++ ++ QSLIST_FOREACH(act, &tran->actions, entry) { ++ if (act->drv->commit) { ++ act->drv->commit(act->opaque); ++ } ++ } ++ ++ QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) { ++ if (act->drv->clean) { ++ act->drv->clean(act->opaque); ++ } ++ ++ g_free(act); ++ } ++ ++ g_free(tran); ++} +diff --git a/qcow2/lib/util/unicode.c b/qcow2/lib/util/unicode.c +new file mode 100644 +index 00000000..8580bc59 +--- /dev/null ++++ b/qcow2/lib/util/unicode.c +@@ -0,0 +1,156 @@ ++/* ++ * Dealing with Unicode ++ * ++ * Copyright (C) 2013 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/unicode.h" ++ ++static bool is_valid_codepoint(int codepoint) ++{ ++ if (codepoint > 0x10FFFFu) { ++ return false; /* beyond Unicode range */ ++ } ++ if ((codepoint >= 0xFDD0 && codepoint <= 0xFDEF) ++ || (codepoint & 0xFFFE) == 0xFFFE) { ++ return false; /* noncharacter */ ++ } ++ if (codepoint >= 0xD800 && codepoint <= 0xDFFF) { ++ return false; /* surrogate code point */ ++ } ++ return true; ++} ++ ++/** ++ * mod_utf8_codepoint: ++ * @s: string encoded in modified UTF-8 ++ * @n: maximum number of bytes to read from @s, if less than 6 ++ * @end: set to end of sequence on return ++ * ++ * Convert the modified UTF-8 sequence at the start of @s. Modified ++ * UTF-8 is exactly like UTF-8, except U+0000 is encoded as ++ * "\xC0\x80". ++ * ++ * If @n is zero or @s points to a zero byte, the sequence is invalid, ++ * and @end is set to @s. ++ * ++ * If @s points to an impossible byte (0xFE or 0xFF) or a continuation ++ * byte, the sequence is invalid, and @end is set to @s + 1 ++ * ++ * Else, the first byte determines how many continuation bytes are ++ * expected. If there are fewer, the sequence is invalid, and @end is ++ * set to @s + 1 + actual number of continuation bytes. Else, the ++ * sequence is well-formed, and @end is set to @s + 1 + expected ++ * number of continuation bytes. ++ * ++ * A well-formed sequence is valid unless it encodes a codepoint ++ * outside the Unicode range U+0000..U+10FFFF, one of Unicode's 66 ++ * noncharacters, a surrogate codepoint, or is overlong. Except the ++ * overlong sequence "\xC0\x80" is valid. ++ * ++ * Conversion succeeds if and only if the sequence is valid. ++ * ++ * Returns: the Unicode codepoint on success, -1 on failure. ++ */ ++int mod_utf8_codepoint(const char *s, size_t n, char **end) ++{ ++ static int min_cp[5] = { 0x80, 0x800, 0x10000, 0x200000, 0x4000000 }; ++ const unsigned char *p; ++ unsigned byte, mask, len, i; ++ int cp; ++ ++ if (n == 0 || *s == 0) { ++ /* empty sequence */ ++ *end = (char *)s; ++ return -1; ++ } ++ ++ p = (const unsigned char *)s; ++ byte = *p++; ++ if (byte < 0x80) { ++ cp = byte; /* one byte sequence */ ++ } else if (byte >= 0xFE) { ++ cp = -1; /* impossible bytes 0xFE, 0xFF */ ++ } else if ((byte & 0x40) == 0) { ++ cp = -1; /* unexpected continuation byte */ ++ } else { ++ /* multi-byte sequence */ ++ len = 0; ++ for (mask = 0x80; byte & mask; mask >>= 1) { ++ len++; ++ } ++ assert(len > 1 && len < 7); ++ cp = byte & (mask - 1); ++ for (i = 1; i < len; i++) { ++ byte = i < n ? *p : 0; ++ if ((byte & 0xC0) != 0x80) { ++ cp = -1; /* continuation byte missing */ ++ goto out; ++ } ++ p++; ++ cp <<= 6; ++ cp |= byte & 0x3F; ++ } ++ if (!is_valid_codepoint(cp)) { ++ cp = -1; ++ } else if (cp < min_cp[len - 2] && !(cp == 0 && len == 2)) { ++ cp = -1; /* overlong, not \xC0\x80 */ ++ } ++ } ++ ++out: ++ *end = (char *)p; ++ return cp; ++} ++ ++/** ++ * mod_utf8_encode: ++ * @buf: Destination buffer ++ * @bufsz: size of @buf, at least 5. ++ * @codepoint: Unicode codepoint to encode ++ * ++ * Convert Unicode codepoint @codepoint to modified UTF-8. ++ * ++ * Returns: the length of the UTF-8 sequence on success, -1 when ++ * @codepoint is invalid. ++ */ ++ssize_t mod_utf8_encode(char buf[], size_t bufsz, int codepoint) ++{ ++ assert(bufsz >= 5); ++ ++ if (!is_valid_codepoint(codepoint)) { ++ return -1; ++ } ++ ++ if (codepoint > 0 && codepoint <= 0x7F) { ++ buf[0] = codepoint & 0x7F; ++ buf[1] = 0; ++ return 1; ++ } ++ if (codepoint <= 0x7FF) { ++ buf[0] = 0xC0 | ((codepoint >> 6) & 0x1F); ++ buf[1] = 0x80 | (codepoint & 0x3F); ++ buf[2] = 0; ++ return 2; ++ } ++ if (codepoint <= 0xFFFF) { ++ buf[0] = 0xE0 | ((codepoint >> 12) & 0x0F); ++ buf[1] = 0x80 | ((codepoint >> 6) & 0x3F); ++ buf[2] = 0x80 | (codepoint & 0x3F); ++ buf[3] = 0; ++ return 3; ++ } ++ buf[0] = 0xF0 | ((codepoint >> 18) & 0x07); ++ buf[1] = 0x80 | ((codepoint >> 12) & 0x3F); ++ buf[2] = 0x80 | ((codepoint >> 6) & 0x3F); ++ buf[3] = 0x80 | (codepoint & 0x3F); ++ buf[4] = 0; ++ return 4; ++} +diff --git a/qcow2/lib/util/uri.c b/qcow2/lib/util/uri.c +new file mode 100644 +index 00000000..573174bf +--- /dev/null ++++ b/qcow2/lib/util/uri.c +@@ -0,0 +1,1466 @@ ++/** ++ * uri.c: set of generic URI related routines ++ * ++ * Reference: RFCs 3986, 2732 and 2373 ++ * ++ * Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * DANIEL VEILLARD BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER ++ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Except as contained in this notice, the name of Daniel Veillard shall not ++ * be used in advertising or otherwise to promote the sale, use or other ++ * dealings in this Software without prior written authorization from him. ++ * ++ * daniel@veillard.com ++ * ++ ** ++ * ++ * Copyright (C) 2007, 2009-2010 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library. If not, see . ++ * ++ * Authors: ++ * Richard W.M. Jones ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++ ++#include "qemu/uri.h" ++ ++static void uri_clean(URI *uri); ++ ++/* ++ * Old rule from 2396 used in legacy handling code ++ * alpha = lowalpha | upalpha ++ */ ++#define IS_ALPHA(x) (IS_LOWALPHA(x) || IS_UPALPHA(x)) ++ ++/* ++ * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | ++ * "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | ++ * "u" | "v" | "w" | "x" | "y" | "z" ++ */ ++ ++#define IS_LOWALPHA(x) (((x) >= 'a') && ((x) <= 'z')) ++ ++/* ++ * upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | ++ * "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | ++ * "U" | "V" | "W" | "X" | "Y" | "Z" ++ */ ++#define IS_UPALPHA(x) (((x) >= 'A') && ((x) <= 'Z')) ++ ++#ifdef IS_DIGIT ++#undef IS_DIGIT ++#endif ++/* ++ * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ++ */ ++#define IS_DIGIT(x) (((x) >= '0') && ((x) <= '9')) ++ ++/* ++ * alphanum = alpha | digit ++ */ ++ ++#define IS_ALPHANUM(x) (IS_ALPHA(x) || IS_DIGIT(x)) ++ ++/* ++ * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" ++ */ ++ ++#define IS_MARK(x) (((x) == '-') || ((x) == '_') || ((x) == '.') || \ ++ ((x) == '!') || ((x) == '~') || ((x) == '*') || ((x) == '\'') || \ ++ ((x) == '(') || ((x) == ')')) ++ ++/* ++ * unwise = "{" | "}" | "|" | "\" | "^" | "`" ++ */ ++ ++#define IS_UNWISE(p) \ ++ (((*(p) == '{')) || ((*(p) == '}')) || ((*(p) == '|')) || \ ++ ((*(p) == '\\')) || ((*(p) == '^')) || ((*(p) == '[')) || \ ++ ((*(p) == ']')) || ((*(p) == '`'))) ++/* ++ * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," | ++ * "[" | "]" ++ */ ++ ++#define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') || \ ++ ((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') || \ ++ ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \ ++ ((x) == ']')) ++ ++/* ++ * unreserved = alphanum | mark ++ */ ++ ++#define IS_UNRESERVED(x) (IS_ALPHANUM(x) || IS_MARK(x)) ++ ++/* ++ * Skip to next pointer char, handle escaped sequences ++ */ ++ ++#define NEXT(p) ((*p == '%') ? p += 3 : p++) ++ ++/* ++ * Productions from the spec. ++ * ++ * authority = server | reg_name ++ * reg_name = 1*( unreserved | escaped | "$" | "," | ++ * ";" | ":" | "@" | "&" | "=" | "+" ) ++ * ++ * path = [ abs_path | opaque_part ] ++ */ ++ ++/************************************************************************ ++ * * ++ * RFC 3986 parser * ++ * * ++ ************************************************************************/ ++ ++#define ISA_DIGIT(p) ((*(p) >= '0') && (*(p) <= '9')) ++#define ISA_ALPHA(p) (((*(p) >= 'a') && (*(p) <= 'z')) || \ ++ ((*(p) >= 'A') && (*(p) <= 'Z'))) ++#define ISA_HEXDIG(p) \ ++ (ISA_DIGIT(p) || ((*(p) >= 'a') && (*(p) <= 'f')) || \ ++ ((*(p) >= 'A') && (*(p) <= 'F'))) ++ ++/* ++ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" ++ * / "*" / "+" / "," / ";" / "=" ++ */ ++#define ISA_SUB_DELIM(p) \ ++ (((*(p) == '!')) || ((*(p) == '$')) || ((*(p) == '&')) || \ ++ ((*(p) == '(')) || ((*(p) == ')')) || ((*(p) == '*')) || \ ++ ((*(p) == '+')) || ((*(p) == ',')) || ((*(p) == ';')) || \ ++ ((*(p) == '=')) || ((*(p) == '\''))) ++ ++/* ++ * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" ++ */ ++#define ISA_UNRESERVED(p) \ ++ ((ISA_ALPHA(p)) || (ISA_DIGIT(p)) || ((*(p) == '-')) || \ ++ ((*(p) == '.')) || ((*(p) == '_')) || ((*(p) == '~'))) ++ ++/* ++ * pct-encoded = "%" HEXDIG HEXDIG ++ */ ++#define ISA_PCT_ENCODED(p) \ ++ ((*(p) == '%') && (ISA_HEXDIG(p + 1)) && (ISA_HEXDIG(p + 2))) ++ ++/* ++ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" ++ */ ++#define ISA_PCHAR(p) \ ++ (ISA_UNRESERVED(p) || ISA_PCT_ENCODED(p) || ISA_SUB_DELIM(p) || \ ++ ((*(p) == ':')) || ((*(p) == '@'))) ++ ++/** ++ * rfc3986_parse_scheme: ++ * @uri: pointer to an URI structure ++ * @str: pointer to the string to analyze ++ * ++ * Parse an URI scheme ++ * ++ * ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_scheme(URI *uri, const char **str) ++{ ++ const char *cur; ++ ++ if (str == NULL) { ++ return -1; ++ } ++ ++ cur = *str; ++ if (!ISA_ALPHA(cur)) { ++ return 2; ++ } ++ cur++; ++ while (ISA_ALPHA(cur) || ISA_DIGIT(cur) || (*cur == '+') || (*cur == '-') || ++ (*cur == '.')) { ++ cur++; ++ } ++ if (uri != NULL) { ++ g_free(uri->scheme); ++ uri->scheme = g_strndup(*str, cur - *str); ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_fragment: ++ * @uri: pointer to an URI structure ++ * @str: pointer to the string to analyze ++ * ++ * Parse the query part of an URI ++ * ++ * fragment = *( pchar / "/" / "?" ) ++ * NOTE: the strict syntax as defined by 3986 does not allow '[' and ']' ++ * in the fragment identifier but this is used very broadly for ++ * xpointer scheme selection, so we are allowing it here to not break ++ * for example all the DocBook processing chains. ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_fragment(URI *uri, const char **str) ++{ ++ const char *cur; ++ ++ if (str == NULL) { ++ return -1; ++ } ++ ++ cur = *str; ++ ++ while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || ++ (*cur == '[') || (*cur == ']') || ++ ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) { ++ NEXT(cur); ++ } ++ if (uri != NULL) { ++ g_free(uri->fragment); ++ if (uri->cleanup & 2) { ++ uri->fragment = g_strndup(*str, cur - *str); ++ } else { ++ uri->fragment = g_uri_unescape_segment(*str, cur, NULL); ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_query: ++ * @uri: pointer to an URI structure ++ * @str: pointer to the string to analyze ++ * ++ * Parse the query part of an URI ++ * ++ * query = *uric ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_query(URI *uri, const char **str) ++{ ++ const char *cur; ++ ++ if (str == NULL) { ++ return -1; ++ } ++ ++ cur = *str; ++ ++ while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || ++ ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) { ++ NEXT(cur); ++ } ++ if (uri != NULL) { ++ g_free(uri->query); ++ uri->query = g_strndup(*str, cur - *str); ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_port: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse a port part and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * port = *DIGIT ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_port(URI *uri, const char **str) ++{ ++ const char *cur = *str; ++ int port = 0; ++ ++ if (ISA_DIGIT(cur)) { ++ while (ISA_DIGIT(cur)) { ++ port = port * 10 + (*cur - '0'); ++ if (port > 65535) { ++ return 1; ++ } ++ cur++; ++ } ++ if (uri) { ++ uri->port = port; ++ } ++ *str = cur; ++ return 0; ++ } ++ return 1; ++} ++ ++/** ++ * rfc3986_parse_user_info: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse a user information part and fill in the appropriate fields ++ * of the @uri structure ++ * ++ * userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_user_info(URI *uri, const char **str) ++{ ++ const char *cur; ++ ++ cur = *str; ++ while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur) || ++ (*cur == ':')) { ++ NEXT(cur); ++ } ++ if (*cur == '@') { ++ if (uri != NULL) { ++ g_free(uri->user); ++ if (uri->cleanup & 2) { ++ uri->user = g_strndup(*str, cur - *str); ++ } else { ++ uri->user = g_uri_unescape_segment(*str, cur, NULL); ++ } ++ } ++ *str = cur; ++ return 0; ++ } ++ return 1; ++} ++ ++/** ++ * rfc3986_parse_dec_octet: ++ * @str: the string to analyze ++ * ++ * dec-octet = DIGIT ; 0-9 ++ * / %x31-39 DIGIT ; 10-99 ++ * / "1" 2DIGIT ; 100-199 ++ * / "2" %x30-34 DIGIT ; 200-249 ++ * / "25" %x30-35 ; 250-255 ++ * ++ * Skip a dec-octet. ++ * ++ * Returns 0 if found and skipped, 1 otherwise ++ */ ++static int rfc3986_parse_dec_octet(const char **str) ++{ ++ const char *cur = *str; ++ ++ if (!(ISA_DIGIT(cur))) { ++ return 1; ++ } ++ if (!ISA_DIGIT(cur + 1)) { ++ cur++; ++ } else if ((*cur != '0') && (ISA_DIGIT(cur + 1)) && (!ISA_DIGIT(cur + 2))) { ++ cur += 2; ++ } else if ((*cur == '1') && (ISA_DIGIT(cur + 1)) && (ISA_DIGIT(cur + 2))) { ++ cur += 3; ++ } else if ((*cur == '2') && (*(cur + 1) >= '0') && (*(cur + 1) <= '4') && ++ (ISA_DIGIT(cur + 2))) { ++ cur += 3; ++ } else if ((*cur == '2') && (*(cur + 1) == '5') && (*(cur + 2) >= '0') && ++ (*(cur + 1) <= '5')) { ++ cur += 3; ++ } else { ++ return 1; ++ } ++ *str = cur; ++ return 0; ++} ++/** ++ * rfc3986_parse_host: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an host part and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * host = IP-literal / IPv4address / reg-name ++ * IP-literal = "[" ( IPv6address / IPvFuture ) "]" ++ * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet ++ * reg-name = *( unreserved / pct-encoded / sub-delims ) ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_host(URI *uri, const char **str) ++{ ++ const char *cur = *str; ++ const char *host; ++ ++ host = cur; ++ /* ++ * IPv6 and future addressing scheme are enclosed between brackets ++ */ ++ if (*cur == '[') { ++ cur++; ++ while ((*cur != ']') && (*cur != 0)) { ++ cur++; ++ } ++ if (*cur != ']') { ++ return 1; ++ } ++ cur++; ++ goto found; ++ } ++ /* ++ * try to parse an IPv4 ++ */ ++ if (ISA_DIGIT(cur)) { ++ if (rfc3986_parse_dec_octet(&cur) != 0) { ++ goto not_ipv4; ++ } ++ if (*cur != '.') { ++ goto not_ipv4; ++ } ++ cur++; ++ if (rfc3986_parse_dec_octet(&cur) != 0) { ++ goto not_ipv4; ++ } ++ if (*cur != '.') { ++ goto not_ipv4; ++ } ++ if (rfc3986_parse_dec_octet(&cur) != 0) { ++ goto not_ipv4; ++ } ++ if (*cur != '.') { ++ goto not_ipv4; ++ } ++ if (rfc3986_parse_dec_octet(&cur) != 0) { ++ goto not_ipv4; ++ } ++ goto found; ++ not_ipv4: ++ cur = *str; ++ } ++ /* ++ * then this should be a hostname which can be empty ++ */ ++ while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur)) { ++ NEXT(cur); ++ } ++found: ++ if (uri != NULL) { ++ g_free(uri->authority); ++ uri->authority = NULL; ++ g_free(uri->server); ++ if (cur != host) { ++ if (uri->cleanup & 2) { ++ uri->server = g_strndup(host, cur - host); ++ } else { ++ uri->server = g_uri_unescape_segment(host, cur, NULL); ++ } ++ } else { ++ uri->server = NULL; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_authority: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an authority part and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * authority = [ userinfo "@" ] host [ ":" port ] ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_authority(URI *uri, const char **str) ++{ ++ const char *cur; ++ int ret; ++ ++ cur = *str; ++ /* ++ * try to parse a userinfo and check for the trailing @ ++ */ ++ ret = rfc3986_parse_user_info(uri, &cur); ++ if ((ret != 0) || (*cur != '@')) { ++ cur = *str; ++ } else { ++ cur++; ++ } ++ ret = rfc3986_parse_host(uri, &cur); ++ if (ret != 0) { ++ return ret; ++ } ++ if (*cur == ':') { ++ cur++; ++ ret = rfc3986_parse_port(uri, &cur); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_segment: ++ * @str: the string to analyze ++ * @forbid: an optional forbidden character ++ * @empty: allow an empty segment ++ * ++ * Parse a segment and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * segment = *pchar ++ * segment-nz = 1*pchar ++ * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) ++ * ; non-zero-length segment without any colon ":" ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_segment(const char **str, char forbid, int empty) ++{ ++ const char *cur; ++ ++ cur = *str; ++ if (!ISA_PCHAR(cur)) { ++ if (empty) { ++ return 0; ++ } ++ return 1; ++ } ++ while (ISA_PCHAR(cur) && (*cur != forbid)) { ++ NEXT(cur); ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_path_ab_empty: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an path absolute or empty and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * path-abempty = *( "/" segment ) ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_path_ab_empty(URI *uri, const char **str) ++{ ++ const char *cur; ++ int ret; ++ ++ cur = *str; ++ ++ while (*cur == '/') { ++ cur++; ++ ret = rfc3986_parse_segment(&cur, 0, 1); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (uri != NULL) { ++ g_free(uri->path); ++ if (*str != cur) { ++ if (uri->cleanup & 2) { ++ uri->path = g_strndup(*str, cur - *str); ++ } else { ++ uri->path = g_uri_unescape_segment(*str, cur, NULL); ++ } ++ } else { ++ uri->path = NULL; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_path_absolute: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an path absolute and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * path-absolute = "/" [ segment-nz *( "/" segment ) ] ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_path_absolute(URI *uri, const char **str) ++{ ++ const char *cur; ++ int ret; ++ ++ cur = *str; ++ ++ if (*cur != '/') { ++ return 1; ++ } ++ cur++; ++ ret = rfc3986_parse_segment(&cur, 0, 0); ++ if (ret == 0) { ++ while (*cur == '/') { ++ cur++; ++ ret = rfc3986_parse_segment(&cur, 0, 1); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ } ++ if (uri != NULL) { ++ g_free(uri->path); ++ if (cur != *str) { ++ if (uri->cleanup & 2) { ++ uri->path = g_strndup(*str, cur - *str); ++ } else { ++ uri->path = g_uri_unescape_segment(*str, cur, NULL); ++ } ++ } else { ++ uri->path = NULL; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_path_rootless: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an path without root and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * path-rootless = segment-nz *( "/" segment ) ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_path_rootless(URI *uri, const char **str) ++{ ++ const char *cur; ++ int ret; ++ ++ cur = *str; ++ ++ ret = rfc3986_parse_segment(&cur, 0, 0); ++ if (ret != 0) { ++ return ret; ++ } ++ while (*cur == '/') { ++ cur++; ++ ret = rfc3986_parse_segment(&cur, 0, 1); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (uri != NULL) { ++ g_free(uri->path); ++ if (cur != *str) { ++ if (uri->cleanup & 2) { ++ uri->path = g_strndup(*str, cur - *str); ++ } else { ++ uri->path = g_uri_unescape_segment(*str, cur, NULL); ++ } ++ } else { ++ uri->path = NULL; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_path_no_scheme: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an path which is not a scheme and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * path-noscheme = segment-nz-nc *( "/" segment ) ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_path_no_scheme(URI *uri, const char **str) ++{ ++ const char *cur; ++ int ret; ++ ++ cur = *str; ++ ++ ret = rfc3986_parse_segment(&cur, ':', 0); ++ if (ret != 0) { ++ return ret; ++ } ++ while (*cur == '/') { ++ cur++; ++ ret = rfc3986_parse_segment(&cur, 0, 1); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (uri != NULL) { ++ g_free(uri->path); ++ if (cur != *str) { ++ if (uri->cleanup & 2) { ++ uri->path = g_strndup(*str, cur - *str); ++ } else { ++ uri->path = g_uri_unescape_segment(*str, cur, NULL); ++ } ++ } else { ++ uri->path = NULL; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_hier_part: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an hierarchical part and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * hier-part = "//" authority path-abempty ++ * / path-absolute ++ * / path-rootless ++ * / path-empty ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_hier_part(URI *uri, const char **str) ++{ ++ const char *cur; ++ int ret; ++ ++ cur = *str; ++ ++ if ((*cur == '/') && (*(cur + 1) == '/')) { ++ cur += 2; ++ ret = rfc3986_parse_authority(uri, &cur); ++ if (ret != 0) { ++ return ret; ++ } ++ ret = rfc3986_parse_path_ab_empty(uri, &cur); ++ if (ret != 0) { ++ return ret; ++ } ++ *str = cur; ++ return 0; ++ } else if (*cur == '/') { ++ ret = rfc3986_parse_path_absolute(uri, &cur); ++ if (ret != 0) { ++ return ret; ++ } ++ } else if (ISA_PCHAR(cur)) { ++ ret = rfc3986_parse_path_rootless(uri, &cur); ++ if (ret != 0) { ++ return ret; ++ } ++ } else { ++ /* path-empty is effectively empty */ ++ if (uri != NULL) { ++ g_free(uri->path); ++ uri->path = NULL; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_relative_ref: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an URI string and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * relative-ref = relative-part [ "?" query ] [ "#" fragment ] ++ * relative-part = "//" authority path-abempty ++ * / path-absolute ++ * / path-noscheme ++ * / path-empty ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_relative_ref(URI *uri, const char *str) ++{ ++ int ret; ++ ++ if ((*str == '/') && (*(str + 1) == '/')) { ++ str += 2; ++ ret = rfc3986_parse_authority(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ ret = rfc3986_parse_path_ab_empty(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } else if (*str == '/') { ++ ret = rfc3986_parse_path_absolute(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } else if (ISA_PCHAR(str)) { ++ ret = rfc3986_parse_path_no_scheme(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } else { ++ /* path-empty is effectively empty */ ++ if (uri != NULL) { ++ g_free(uri->path); ++ uri->path = NULL; ++ } ++ } ++ ++ if (*str == '?') { ++ str++; ++ ret = rfc3986_parse_query(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (*str == '#') { ++ str++; ++ ret = rfc3986_parse_fragment(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (*str != 0) { ++ uri_clean(uri); ++ return 1; ++ } ++ return 0; ++} ++ ++/** ++ * rfc3986_parse: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an URI string and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * scheme ":" hier-part [ "?" query ] [ "#" fragment ] ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse(URI *uri, const char *str) ++{ ++ int ret; ++ ++ ret = rfc3986_parse_scheme(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ if (*str != ':') { ++ return 1; ++ } ++ str++; ++ ret = rfc3986_parse_hier_part(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ if (*str == '?') { ++ str++; ++ ret = rfc3986_parse_query(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (*str == '#') { ++ str++; ++ ret = rfc3986_parse_fragment(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (*str != 0) { ++ uri_clean(uri); ++ return 1; ++ } ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_uri_reference: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an URI reference string and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * URI-reference = URI / relative-ref ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_uri_reference(URI *uri, const char *str) ++{ ++ int ret; ++ ++ if (str == NULL) { ++ return -1; ++ } ++ uri_clean(uri); ++ ++ /* ++ * Try first to parse absolute refs, then fallback to relative if ++ * it fails. ++ */ ++ ret = rfc3986_parse(uri, str); ++ if (ret != 0) { ++ uri_clean(uri); ++ ret = rfc3986_parse_relative_ref(uri, str); ++ if (ret != 0) { ++ uri_clean(uri); ++ return ret; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * uri_parse: ++ * @str: the URI string to analyze ++ * ++ * Parse an URI based on RFC 3986 ++ * ++ * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] ++ * ++ * Returns a newly built URI or NULL in case of error ++ */ ++URI *uri_parse(const char *str) ++{ ++ URI *uri; ++ int ret; ++ ++ if (str == NULL) { ++ return NULL; ++ } ++ uri = uri_new(); ++ ret = rfc3986_parse_uri_reference(uri, str); ++ if (ret) { ++ uri_free(uri); ++ return NULL; ++ } ++ return uri; ++} ++ ++/** ++ * uri_parse_into: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an URI reference string based on RFC 3986 and fills in the ++ * appropriate fields of the @uri structure ++ * ++ * URI-reference = URI / relative-ref ++ * ++ * Returns 0 or the error code ++ */ ++int uri_parse_into(URI *uri, const char *str) ++{ ++ return rfc3986_parse_uri_reference(uri, str); ++} ++ ++/** ++ * uri_parse_raw: ++ * @str: the URI string to analyze ++ * @raw: if 1 unescaping of URI pieces are disabled ++ * ++ * Parse an URI but allows to keep intact the original fragments. ++ * ++ * URI-reference = URI / relative-ref ++ * ++ * Returns a newly built URI or NULL in case of error ++ */ ++URI *uri_parse_raw(const char *str, int raw) ++{ ++ URI *uri; ++ int ret; ++ ++ if (str == NULL) { ++ return NULL; ++ } ++ uri = uri_new(); ++ if (raw) { ++ uri->cleanup |= 2; ++ } ++ ret = uri_parse_into(uri, str); ++ if (ret) { ++ uri_free(uri); ++ return NULL; ++ } ++ return uri; ++} ++ ++/************************************************************************ ++ * * ++ * Generic URI structure functions * ++ * * ++ ************************************************************************/ ++ ++/** ++ * uri_new: ++ * ++ * Simply creates an empty URI ++ * ++ * Returns the new structure or NULL in case of error ++ */ ++URI *uri_new(void) ++{ ++ return g_new0(URI, 1); ++} ++ ++/** ++ * realloc2n: ++ * ++ * Function to handle properly a reallocation when saving an URI ++ * Also imposes some limit on the length of an URI string output ++ */ ++static char *realloc2n(char *ret, int *max) ++{ ++ char *temp; ++ int tmp; ++ ++ tmp = *max * 2; ++ temp = g_realloc(ret, (tmp + 1)); ++ *max = tmp; ++ return temp; ++} ++ ++/** ++ * uri_to_string: ++ * @uri: pointer to an URI ++ * ++ * Save the URI as an escaped string ++ * ++ * Returns a new string (to be deallocated by caller) ++ */ ++char *uri_to_string(URI *uri) ++{ ++ char *ret = NULL; ++ char *temp; ++ const char *p; ++ int len; ++ int max; ++ ++ if (uri == NULL) { ++ return NULL; ++ } ++ ++ max = 80; ++ ret = g_malloc(max + 1); ++ len = 0; ++ ++ if (uri->scheme != NULL) { ++ p = uri->scheme; ++ while (*p != 0) { ++ if (len >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = *p++; ++ } ++ if (len >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = ':'; ++ } ++ if (uri->opaque != NULL) { ++ p = uri->opaque; ++ while (*p != 0) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ if (IS_RESERVED(*(p)) || IS_UNRESERVED(*(p))) { ++ ret[len++] = *p++; ++ } else { ++ int val = *(unsigned char *)p++; ++ int hi = val / 0x10, lo = val % 0x10; ++ ret[len++] = '%'; ++ ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); ++ ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); ++ } ++ } ++ } else { ++ if (uri->server != NULL) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = '/'; ++ ret[len++] = '/'; ++ if (uri->user != NULL) { ++ p = uri->user; ++ while (*p != 0) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ if ((IS_UNRESERVED(*(p))) || ((*(p) == ';')) || ++ ((*(p) == ':')) || ((*(p) == '&')) || ((*(p) == '=')) || ++ ((*(p) == '+')) || ((*(p) == '$')) || ((*(p) == ','))) { ++ ret[len++] = *p++; ++ } else { ++ int val = *(unsigned char *)p++; ++ int hi = val / 0x10, lo = val % 0x10; ++ ret[len++] = '%'; ++ ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); ++ ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); ++ } ++ } ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = '@'; ++ } ++ p = uri->server; ++ while (*p != 0) { ++ if (len >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = *p++; ++ } ++ if (uri->port > 0) { ++ if (len + 10 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ len += snprintf(&ret[len], max - len, ":%d", uri->port); ++ } ++ } else if (uri->authority != NULL) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = '/'; ++ ret[len++] = '/'; ++ p = uri->authority; ++ while (*p != 0) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ if ((IS_UNRESERVED(*(p))) || ((*(p) == '$')) || ++ ((*(p) == ',')) || ((*(p) == ';')) || ((*(p) == ':')) || ++ ((*(p) == '@')) || ((*(p) == '&')) || ((*(p) == '=')) || ++ ((*(p) == '+'))) { ++ ret[len++] = *p++; ++ } else { ++ int val = *(unsigned char *)p++; ++ int hi = val / 0x10, lo = val % 0x10; ++ ret[len++] = '%'; ++ ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); ++ ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); ++ } ++ } ++ } else if (uri->scheme != NULL) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = '/'; ++ ret[len++] = '/'; ++ } ++ if (uri->path != NULL) { ++ p = uri->path; ++ /* ++ * the colon in file:///d: should not be escaped or ++ * Windows accesses fail later. ++ */ ++ if ((uri->scheme != NULL) && (p[0] == '/') && ++ (((p[1] >= 'a') && (p[1] <= 'z')) || ++ ((p[1] >= 'A') && (p[1] <= 'Z'))) && ++ (p[2] == ':') && (!strcmp(uri->scheme, "file"))) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = *p++; ++ ret[len++] = *p++; ++ ret[len++] = *p++; ++ } ++ while (*p != 0) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ if ((IS_UNRESERVED(*(p))) || ((*(p) == '/')) || ++ ((*(p) == ';')) || ((*(p) == '@')) || ((*(p) == '&')) || ++ ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) || ++ ((*(p) == ','))) { ++ ret[len++] = *p++; ++ } else { ++ int val = *(unsigned char *)p++; ++ int hi = val / 0x10, lo = val % 0x10; ++ ret[len++] = '%'; ++ ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); ++ ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); ++ } ++ } ++ } ++ if (uri->query != NULL) { ++ if (len + 1 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = '?'; ++ p = uri->query; ++ while (*p != 0) { ++ if (len + 1 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = *p++; ++ } ++ } ++ } ++ if (uri->fragment != NULL) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = '#'; ++ p = uri->fragment; ++ while (*p != 0) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) { ++ ret[len++] = *p++; ++ } else { ++ int val = *(unsigned char *)p++; ++ int hi = val / 0x10, lo = val % 0x10; ++ ret[len++] = '%'; ++ ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); ++ ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); ++ } ++ } ++ } ++ if (len >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len] = 0; ++ return ret; ++} ++ ++/** ++ * uri_clean: ++ * @uri: pointer to an URI ++ * ++ * Make sure the URI struct is free of content ++ */ ++static void uri_clean(URI *uri) ++{ ++ if (uri == NULL) { ++ return; ++ } ++ ++ g_free(uri->scheme); ++ uri->scheme = NULL; ++ g_free(uri->server); ++ uri->server = NULL; ++ g_free(uri->user); ++ uri->user = NULL; ++ g_free(uri->path); ++ uri->path = NULL; ++ g_free(uri->fragment); ++ uri->fragment = NULL; ++ g_free(uri->opaque); ++ uri->opaque = NULL; ++ g_free(uri->authority); ++ uri->authority = NULL; ++ g_free(uri->query); ++ uri->query = NULL; ++} ++ ++/** ++ * uri_free: ++ * @uri: pointer to an URI, NULL is ignored ++ * ++ * Free up the URI struct ++ */ ++void uri_free(URI *uri) ++{ ++ uri_clean(uri); ++ g_free(uri); ++} ++ ++/************************************************************************ ++ * * ++ * Public functions * ++ * * ++ ************************************************************************/ ++ ++/* ++ * Utility functions to help parse and assemble query strings. ++ */ ++ ++struct QueryParams *query_params_new(int init_alloc) ++{ ++ struct QueryParams *ps; ++ ++ if (init_alloc <= 0) { ++ init_alloc = 1; ++ } ++ ++ ps = g_new(QueryParams, 1); ++ ps->n = 0; ++ ps->alloc = init_alloc; ++ ps->p = g_new(QueryParam, ps->alloc); ++ ++ return ps; ++} ++ ++/* Ensure there is space to store at least one more parameter ++ * at the end of the set. ++ */ ++static int query_params_append(struct QueryParams *ps, const char *name, ++ const char *value) ++{ ++ if (ps->n >= ps->alloc) { ++ ps->p = g_renew(QueryParam, ps->p, ps->alloc * 2); ++ ps->alloc *= 2; ++ } ++ ++ ps->p[ps->n].name = g_strdup(name); ++ ps->p[ps->n].value = g_strdup(value); ++ ps->p[ps->n].ignore = 0; ++ ps->n++; ++ ++ return 0; ++} ++ ++void query_params_free(struct QueryParams *ps) ++{ ++ int i; ++ ++ for (i = 0; i < ps->n; ++i) { ++ g_free(ps->p[i].name); ++ g_free(ps->p[i].value); ++ } ++ g_free(ps->p); ++ g_free(ps); ++} ++ ++struct QueryParams *query_params_parse(const char *query) ++{ ++ struct QueryParams *ps; ++ const char *end, *eq; ++ ++ ps = query_params_new(0); ++ if (!query || query[0] == '\0') { ++ return ps; ++ } ++ ++ while (*query) { ++ char *name = NULL, *value = NULL; ++ ++ /* Find the next separator, or end of the string. */ ++ end = strchr(query, '&'); ++ if (!end) { ++ end = qemu_strchrnul(query, ';'); ++ } ++ ++ /* Find the first '=' character between here and end. */ ++ eq = strchr(query, '='); ++ if (eq && eq >= end) { ++ eq = NULL; ++ } ++ ++ /* Empty section (eg. "&&"). */ ++ if (end == query) { ++ goto next; ++ } ++ ++ /* If there is no '=' character, then we have just "name" ++ * and consistent with CGI.pm we assume value is "". ++ */ ++ else if (!eq) { ++ name = g_uri_unescape_segment(query, end, NULL); ++ value = NULL; ++ } ++ /* Or if we have "name=" here (works around annoying ++ * problem when calling uri_string_unescape with len = 0). ++ */ ++ else if (eq + 1 == end) { ++ name = g_uri_unescape_segment(query, eq, NULL); ++ value = g_new0(char, 1); ++ } ++ /* If the '=' character is at the beginning then we have ++ * "=value" and consistent with CGI.pm we _ignore_ this. ++ */ ++ else if (query == eq) { ++ goto next; ++ } ++ ++ /* Otherwise it's "name=value". */ ++ else { ++ name = g_uri_unescape_segment(query, eq, NULL); ++ value = g_uri_unescape_segment(eq + 1, end, NULL); ++ } ++ ++ /* Append to the parameter set. */ ++ query_params_append(ps, name, value); ++ g_free(name); ++ g_free(value); ++ ++ next: ++ query = end; ++ if (*query) { ++ query++; /* skip '&' separator */ ++ } ++ } ++ ++ return ps; ++} +diff --git a/qcow2/lib/util/yank.c b/qcow2/lib/util/yank.c +new file mode 100644 +index 00000000..eaac5053 +--- /dev/null ++++ b/qcow2/lib/util/yank.c +@@ -0,0 +1,199 @@ ++/* ++ * QEMU yank feature ++ * ++ * Copyright (c) Lukas Straub ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/thread.h" ++#include "qemu/queue.h" ++#include "qemu/lockable.h" ++#include "qapi/qapi-commands-yank.h" ++#include "qapi/qapi-visit-yank.h" ++#include "qapi/clone-visitor.h" ++#include "qemu/yank.h" ++ ++struct YankFuncAndParam { ++ YankFn *func; ++ void *opaque; ++ QLIST_ENTRY(YankFuncAndParam) next; ++}; ++ ++struct YankInstanceEntry { ++ YankInstance *instance; ++ QLIST_HEAD(, YankFuncAndParam) yankfns; ++ QLIST_ENTRY(YankInstanceEntry) next; ++}; ++ ++typedef struct YankFuncAndParam YankFuncAndParam; ++typedef struct YankInstanceEntry YankInstanceEntry; ++ ++/* ++ * This lock protects the yank_instance_list below. Because it's taken by ++ * OOB-capable commands, it must be "fast", i.e. it may only be held for a ++ * bounded, short time. See docs/devel/qapi-code-gen.rst for additional ++ * information. ++ */ ++static QemuMutex yank_lock; ++ ++static QLIST_HEAD(, YankInstanceEntry) yank_instance_list ++ = QLIST_HEAD_INITIALIZER(yank_instance_list); ++ ++static bool yank_instance_equal(const YankInstance *a, const YankInstance *b) ++{ ++ if (a->type != b->type) { ++ return false; ++ } ++ ++ switch (a->type) { ++ case YANK_INSTANCE_TYPE_BLOCK_NODE: ++ return g_str_equal(a->u.block_node.node_name, ++ b->u.block_node.node_name); ++ ++ case YANK_INSTANCE_TYPE_CHARDEV: ++ return g_str_equal(a->u.chardev.id, b->u.chardev.id); ++ ++ case YANK_INSTANCE_TYPE_MIGRATION: ++ return true; ++ ++ default: ++ abort(); ++ } ++} ++ ++static YankInstanceEntry *yank_find_entry(const YankInstance *instance) ++{ ++ YankInstanceEntry *entry; ++ ++ QLIST_FOREACH(entry, &yank_instance_list, next) { ++ if (yank_instance_equal(entry->instance, instance)) { ++ return entry; ++ } ++ } ++ return NULL; ++} ++ ++bool yank_register_instance(const YankInstance *instance, Error **errp) ++{ ++ YankInstanceEntry *entry; ++ ++ QEMU_LOCK_GUARD(&yank_lock); ++ ++ if (yank_find_entry(instance)) { ++ error_setg(errp, "duplicate yank instance"); ++ return false; ++ } ++ ++ entry = g_new0(YankInstanceEntry, 1); ++ entry->instance = QAPI_CLONE(YankInstance, instance); ++ QLIST_INIT(&entry->yankfns); ++ QLIST_INSERT_HEAD(&yank_instance_list, entry, next); ++ ++ return true; ++} ++ ++void yank_unregister_instance(const YankInstance *instance) ++{ ++ YankInstanceEntry *entry; ++ ++ QEMU_LOCK_GUARD(&yank_lock); ++ entry = yank_find_entry(instance); ++ assert(entry); ++ ++ assert(QLIST_EMPTY(&entry->yankfns)); ++ QLIST_REMOVE(entry, next); ++ qapi_free_YankInstance(entry->instance); ++ g_free(entry); ++} ++ ++void yank_register_function(const YankInstance *instance, ++ YankFn *func, ++ void *opaque) ++{ ++ YankInstanceEntry *entry; ++ YankFuncAndParam *func_entry; ++ ++ QEMU_LOCK_GUARD(&yank_lock); ++ entry = yank_find_entry(instance); ++ assert(entry); ++ ++ func_entry = g_new0(YankFuncAndParam, 1); ++ func_entry->func = func; ++ func_entry->opaque = opaque; ++ ++ QLIST_INSERT_HEAD(&entry->yankfns, func_entry, next); ++} ++ ++void yank_unregister_function(const YankInstance *instance, ++ YankFn *func, ++ void *opaque) ++{ ++ YankInstanceEntry *entry; ++ YankFuncAndParam *func_entry; ++ ++ QEMU_LOCK_GUARD(&yank_lock); ++ entry = yank_find_entry(instance); ++ assert(entry); ++ ++ QLIST_FOREACH(func_entry, &entry->yankfns, next) { ++ if (func_entry->func == func && func_entry->opaque == opaque) { ++ QLIST_REMOVE(func_entry, next); ++ g_free(func_entry); ++ return; ++ } ++ } ++ ++ abort(); ++} ++ ++void qmp_yank(YankInstanceList *instances, ++ Error **errp) ++{ ++ YankInstanceList *tail; ++ YankInstanceEntry *entry; ++ YankFuncAndParam *func_entry; ++ ++ QEMU_LOCK_GUARD(&yank_lock); ++ for (tail = instances; tail; tail = tail->next) { ++ entry = yank_find_entry(tail->value); ++ if (!entry) { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Instance not found"); ++ return; ++ } ++ } ++ for (tail = instances; tail; tail = tail->next) { ++ entry = yank_find_entry(tail->value); ++ assert(entry); ++ QLIST_FOREACH(func_entry, &entry->yankfns, next) { ++ func_entry->func(func_entry->opaque); ++ } ++ } ++} ++ ++YankInstanceList *qmp_query_yank(Error **errp) ++{ ++ YankInstanceEntry *entry; ++ YankInstanceList *ret; ++ ++ ret = NULL; ++ ++ QEMU_LOCK_GUARD(&yank_lock); ++ QLIST_FOREACH(entry, &yank_instance_list, next) { ++ YankInstanceList *new_entry; ++ new_entry = g_new0(YankInstanceList, 1); ++ new_entry->value = QAPI_CLONE(YankInstance, entry->instance); ++ new_entry->next = ret; ++ ret = new_entry; ++ } ++ ++ return ret; ++} ++ ++static void __attribute__((__constructor__)) yank_init(void) ++{ ++ qemu_mutex_init(&yank_lock); ++} +diff --git a/qcow2/qemu-config.c b/qcow2/qemu-config.c +new file mode 100644 +index 00000000..a90c18da +--- /dev/null ++++ b/qcow2/qemu-config.c +@@ -0,0 +1,295 @@ ++#include "qemu/osdep.h" ++#include "block/qdict.h" /* for qdict_extract_subqdict() */ ++#include "qapi/error.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qlist.h" ++#include "qemu/error-report.h" ++#include "qemu/option.h" ++#include "qemu/config-file.h" ++ ++QemuOptsList *vm_config_groups[48]; ++QemuOptsList *drive_config_groups[5]; ++ ++static QemuOptsList *find_list(QemuOptsList **lists, const char *group, ++ Error **errp) ++{ ++ int i; ++ ++ qemu_load_module_for_opts(group); ++ for (i = 0; lists[i] != NULL; i++) { ++ if (strcmp(lists[i]->name, group) == 0) ++ break; ++ } ++ if (lists[i] == NULL) { ++ error_setg(errp, "There is no option group '%s'", group); ++ } ++ return lists[i]; ++} ++ ++QemuOptsList *qemu_find_opts(const char *group) ++{ ++ QemuOptsList *ret; ++ Error *local_err = NULL; ++ ++ ret = find_list(vm_config_groups, group, &local_err); ++ if (local_err) { ++ error_report_err(local_err); ++ } ++ ++ return ret; ++} ++ ++QemuOpts *qemu_find_opts_singleton(const char *group) ++{ ++ QemuOptsList *list; ++ QemuOpts *opts; ++ ++ list = qemu_find_opts(group); ++ assert(list); ++ opts = qemu_opts_find(list, NULL); ++ if (!opts) { ++ opts = qemu_opts_create(list, NULL, 0, &error_abort); ++ } ++ return opts; ++} ++ ++QemuOptsList *qemu_find_opts_err(const char *group, Error **errp) ++{ ++ return find_list(vm_config_groups, group, errp); ++} ++ ++void qemu_add_drive_opts(QemuOptsList *list) ++{ ++ int entries, i; ++ ++ entries = ARRAY_SIZE(drive_config_groups); ++ entries--; /* keep list NULL terminated */ ++ for (i = 0; i < entries; i++) { ++ if (drive_config_groups[i] == NULL) { ++ drive_config_groups[i] = list; ++ return; ++ } ++ } ++ fprintf(stderr, "ran out of space in drive_config_groups"); ++ abort(); ++} ++ ++void qemu_add_opts(QemuOptsList *list) ++{ ++ int entries, i; ++ ++ entries = ARRAY_SIZE(vm_config_groups); ++ entries--; /* keep list NULL terminated */ ++ for (i = 0; i < entries; i++) { ++ if (vm_config_groups[i] == NULL) { ++ vm_config_groups[i] = list; ++ return; ++ } ++ } ++ fprintf(stderr, "ran out of space in vm_config_groups"); ++ abort(); ++} ++ ++/* Returns number of config groups on success, -errno on error */ ++static int qemu_config_foreach(FILE *fp, QEMUConfigCB *cb, void *opaque, ++ const char *fname, Error **errp) ++{ ++ ERRP_GUARD(); ++ char line[1024], prev_group[64], group[64], arg[64], value[1024]; ++ Location loc; ++ QDict *qdict = NULL; ++ int res = -EINVAL, lno = 0; ++ int count = 0; ++ ++ loc_push_none(&loc); ++ while (fgets(line, sizeof(line), fp) != NULL) { ++ ++lno; ++ if (line[0] == '\n') { ++ /* skip empty lines */ ++ continue; ++ } ++ if (line[0] == '#') { ++ /* comment */ ++ continue; ++ } ++ if (line[0] == '[') { ++ QDict *prev = qdict; ++ if (sscanf(line, "[%63s \"%63[^\"]\"]", group, value) == 2) { ++ qdict = qdict_new(); ++ qdict_put_str(qdict, "id", value); ++ count++; ++ } else if (sscanf(line, "[%63[^]]]", group) == 1) { ++ qdict = qdict_new(); ++ count++; ++ } ++ if (qdict != prev) { ++ if (prev) { ++ cb(prev_group, prev, opaque, errp); ++ qobject_unref(prev); ++ if (*errp) { ++ goto out; ++ } ++ } ++ strcpy(prev_group, group); ++ continue; ++ } ++ } ++ loc_set_file(fname, lno); ++ value[0] = '\0'; ++ if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2 || ++ sscanf(line, " %63s = \"\"", arg) == 1) { ++ /* arg = value */ ++ if (qdict == NULL) { ++ error_setg(errp, "no group defined"); ++ goto out; ++ } ++ qdict_put_str(qdict, arg, value); ++ continue; ++ } ++ error_setg(errp, "parse error"); ++ goto out; ++ } ++ if (ferror(fp)) { ++ loc_pop(&loc); ++ error_setg_errno(errp, errno, "Cannot read config file"); ++ goto out_no_loc; ++ } ++ res = count; ++ if (qdict) { ++ cb(group, qdict, opaque, errp); ++ } ++out: ++ loc_pop(&loc); ++out_no_loc: ++ qobject_unref(qdict); ++ return res; ++} ++ ++void qemu_config_do_parse(const char *group, QDict *qdict, void *opaque, Error **errp) ++{ ++ QemuOptsList **lists = opaque; ++ QemuOptsList *list; ++ ++ list = find_list(lists, group, errp); ++ if (!list) { ++ return; ++ } ++ ++ qemu_opts_from_qdict(list, qdict, errp); ++} ++ ++int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp) ++{ ++ return qemu_config_foreach(fp, qemu_config_do_parse, lists, fname, errp); ++} ++ ++int qemu_read_config_file(const char *filename, QEMUConfigCB *cb, Error **errp) ++{ ++ FILE *f = fopen(filename, "r"); ++ int ret; ++ ++ if (f == NULL) { ++ error_setg_file_open(errp, errno, filename); ++ return -errno; ++ } ++ ++ ret = qemu_config_foreach(f, cb, vm_config_groups, filename, errp); ++ fclose(f); ++ return ret; ++} ++ ++static bool config_parse_qdict_section(QDict *options, QemuOptsList *opts, ++ Error **errp) ++{ ++ QemuOpts *subopts; ++ g_autoptr(QDict) subqdict = NULL; ++ g_autoptr(QList) list = NULL; ++ size_t orig_size, enum_size; ++ char *prefix; ++ ++ prefix = g_strdup_printf("%s.", opts->name); ++ qdict_extract_subqdict(options, &subqdict, prefix); ++ g_free(prefix); ++ orig_size = qdict_size(subqdict); ++ if (!orig_size) { ++ return true; ++ } ++ ++ subopts = qemu_opts_create(opts, NULL, 0, errp); ++ if (!subopts) { ++ return false; ++ } ++ ++ if (!qemu_opts_absorb_qdict(subopts, subqdict, errp)) { ++ return false; ++ } ++ ++ enum_size = qdict_size(subqdict); ++ if (enum_size < orig_size && enum_size) { ++ error_setg(errp, "Unknown option '%s' for [%s]", ++ qdict_first(subqdict)->key, opts->name); ++ return false; ++ } ++ ++ if (enum_size) { ++ /* Multiple, enumerated sections */ ++ QListEntry *list_entry; ++ unsigned i = 0; ++ ++ /* Not required anymore */ ++ qemu_opts_del(subopts); ++ ++ qdict_array_split(subqdict, &list); ++ if (qdict_size(subqdict)) { ++ error_setg(errp, "Unused option '%s' for [%s]", ++ qdict_first(subqdict)->key, opts->name); ++ return false; ++ } ++ ++ QLIST_FOREACH_ENTRY(list, list_entry) { ++ QDict *section = qobject_to(QDict, qlist_entry_obj(list_entry)); ++ char *opt_name; ++ ++ if (!section) { ++ error_setg(errp, "[%s] section (index %u) does not consist of " ++ "keys", opts->name, i); ++ return false; ++ } ++ ++ opt_name = g_strdup_printf("%s.%u", opts->name, i++); ++ subopts = qemu_opts_create(opts, opt_name, 1, errp); ++ g_free(opt_name); ++ if (!subopts) { ++ return false; ++ } ++ ++ if (!qemu_opts_absorb_qdict(subopts, section, errp)) { ++ qemu_opts_del(subopts); ++ return false; ++ } ++ ++ if (qdict_size(section)) { ++ error_setg(errp, "[%s] section doesn't support the option '%s'", ++ opts->name, qdict_first(section)->key); ++ qemu_opts_del(subopts); ++ return false; ++ } ++ } ++ } ++ ++ return true; ++} ++ ++bool qemu_config_parse_qdict(QDict *options, QemuOptsList **lists, ++ Error **errp) ++{ ++ int i; ++ ++ for (i = 0; lists[i]; i++) { ++ if (!config_parse_qdict_section(options, lists[i], errp)) { ++ return false; ++ } ++ } ++ ++ return true; ++} +diff --git a/qcow2/qemu-img-cmds.h b/qcow2/qemu-img-cmds.h +new file mode 100644 +index 00000000..a045b509 +--- /dev/null ++++ b/qcow2/qemu-img-cmds.h +@@ -0,0 +1,46 @@ ++ ++ ++DEF("amend", img_amend, ++"amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] [--force] -o options filename") ++ ++DEF("bench", img_bench, ++"bench [-c count] [-d depth] [-f fmt] [--flush-interval=flush_interval] [-i aio] [-n] [--no-drain] [-o offset] [--pattern=pattern] [-q] [-s buffer_size] [-S step_size] [-t cache] [-w] [-U] filename") ++ ++DEF("bitmap", img_bitmap, ++"bitmap (--merge SOURCE | --add | --remove | --clear | --enable | --disable)... [-b source_file [-F source_fmt]] [-g granularity] [--object objectdef] [--image-opts | -f fmt] filename bitmap") ++ ++DEF("check", img_check, ++"check [--object objectdef] [--image-opts] [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] [-U] filename") ++ ++DEF("commit", img_commit, ++"commit [--object objectdef] [--image-opts] [-q] [-f fmt] [-t cache] [-b base] [-r rate_limit] [-d] [-p] filename") ++ ++DEF("compare", img_compare, ++"compare [--object objectdef] [--image-opts] [-f fmt] [-F fmt] [-T src_cache] [-p] [-q] [-s] [-U] filename1 filename2") ++ ++DEF("convert", img_convert, ++"convert [--object objectdef] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file [-F backing_fmt]] [-o options] [-l snapshot_param] [-S sparse_size] [-r rate_limit] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename") ++ ++DEF("create", img_create, ++"create [--object objectdef] [-q] [-f fmt] [-b backing_file [-F backing_fmt]] [-u] [-o options] filename [size]") ++ ++DEF("dd", img_dd, ++"dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] if=input of=output") ++ ++DEF("info", img_info, ++"info [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [--backing-chain] [-U] filename") ++ ++DEF("map", img_map, ++"map [--object objectdef] [--image-opts] [-f fmt] [--start-offset=offset] [--max-length=len] [--output=ofmt] [-U] filename") ++ ++DEF("measure", img_measure, ++"measure [--output=ofmt] [-O output_fmt] [-o options] [--size N | [--object objectdef] [--image-opts] [-f fmt] [-l snapshot_param] filename]") ++ ++DEF("snapshot", img_snapshot, ++"snapshot [--object objectdef] [--image-opts] [-U] [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename") ++ ++DEF("rebase", img_rebase, ++"rebase [--object objectdef] [--image-opts] [-U] [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] [-c] -b backing_file [-F backing_fmt] filename") ++ ++DEF("resize", img_resize, ++"resize [--object objectdef] [--image-opts] [-f fmt] [--preallocation=prealloc] [-q] [--shrink] filename [+ | -]size") +diff --git a/qcow2/qemu-img.c b/qcow2/qemu-img.c +new file mode 100644 +index 00000000..7668f867 +--- /dev/null ++++ b/qcow2/qemu-img.c +@@ -0,0 +1,5621 @@ ++/* ++ * QEMU disk image utility ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include ++ ++#include "qemu/help-texts.h" ++#include "qemu/qemu-progress.h" ++#include "qemu-version.h" ++#include "qapi/error.h" ++#include "qapi/qapi-commands-block-core.h" ++#include "qapi/qapi-visit-block-core.h" ++#include "qapi/qobject-output-visitor.h" ++#include "qapi/qmp/qjson.h" ++#include "qapi/qmp/qdict.h" ++#include "qemu/cutils.h" ++#include "qemu/config-file.h" ++#include "qemu/option.h" ++#include "qemu/error-report.h" ++#include "qemu/log.h" ++#include "qemu/main-loop.h" ++#include "qemu/module.h" ++#include "qemu/sockets.h" ++#include "qemu/units.h" ++#include "qemu/memalign.h" ++#include "qom/object_interfaces.h" ++#include "sysemu/block-backend.h" ++#include "block/block_int.h" ++#include "block/blockjob.h" ++#include "block/dirty-bitmap.h" ++#include "block/qapi.h" ++#include "crypto/init.h" ++#include "trace/control.h" ++#include "qemu/throttle.h" ++#include "block/throttle-groups.h" ++ ++#define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \ ++ "\n" QEMU_COPYRIGHT "\n" ++ ++typedef struct img_cmd_t { ++ const char *name; ++ int (*handler)(int argc, char **argv); ++} img_cmd_t; ++ ++enum { ++ OPTION_OUTPUT = 256, ++ OPTION_BACKING_CHAIN = 257, ++ OPTION_OBJECT = 258, ++ OPTION_IMAGE_OPTS = 259, ++ OPTION_PATTERN = 260, ++ OPTION_FLUSH_INTERVAL = 261, ++ OPTION_NO_DRAIN = 262, ++ OPTION_TARGET_IMAGE_OPTS = 263, ++ OPTION_SIZE = 264, ++ OPTION_PREALLOCATION = 265, ++ OPTION_SHRINK = 266, ++ OPTION_SALVAGE = 267, ++ OPTION_TARGET_IS_ZERO = 268, ++ OPTION_ADD = 269, ++ OPTION_REMOVE = 270, ++ OPTION_CLEAR = 271, ++ OPTION_ENABLE = 272, ++ OPTION_DISABLE = 273, ++ OPTION_MERGE = 274, ++ OPTION_BITMAPS = 275, ++ OPTION_FORCE = 276, ++ OPTION_SKIP_BROKEN = 277, ++}; ++ ++typedef enum OutputFormat { ++ OFORMAT_JSON, ++ OFORMAT_HUMAN, ++} OutputFormat; ++ ++/* Default to cache=writeback as data integrity is not important for qemu-img */ ++#define BDRV_DEFAULT_CACHE "writeback" ++ ++static void format_print(void *opaque, const char *name) ++{ ++ printf(" %s", name); ++} ++ ++static G_NORETURN G_GNUC_PRINTF(1, 2) ++void error_exit(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ error_vreport(fmt, ap); ++ va_end(ap); ++ ++ error_printf("Try 'qemu-img --help' for more information\n"); ++ exit(EXIT_FAILURE); ++} ++ ++static G_NORETURN ++void missing_argument(const char *option) ++{ ++ error_exit("missing argument for option '%s'", option); ++} ++ ++static G_NORETURN ++void unrecognized_option(const char *option) ++{ ++ error_exit("unrecognized option '%s'", option); ++} ++ ++/* Please keep in synch with docs/tools/qemu-img.rst */ ++static G_NORETURN ++void help(void) ++{ ++ const char *help_msg = ++ QEMU_IMG_VERSION ++ "usage: qemu-img [standard options] command [command options]\n" ++ "QEMU disk image utility\n" ++ "\n" ++ " '-h', '--help' display this help and exit\n" ++ " '-V', '--version' output version information and exit\n" ++ " '-T', '--trace' [[enable=]][,events=][,file=]\n" ++ " specify tracing options\n" ++ "\n" ++ "Command syntax:\n" ++#define DEF(option, callback, arg_string) \ ++ " " arg_string "\n" ++#include "qemu-img-cmds.h" ++#undef DEF ++ "\n" ++ "Command parameters:\n" ++ " 'filename' is a disk image filename\n" ++ " 'objectdef' is a QEMU user creatable object definition. See the qemu(1)\n" ++ " manual page for a description of the object properties. The most common\n" ++ " object type is a 'secret', which is used to supply passwords and/or\n" ++ " encryption keys.\n" ++ " 'fmt' is the disk image format. It is guessed automatically in most cases\n" ++ " 'cache' is the cache mode used to write the output disk image, the valid\n" ++ " options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n" ++ " 'directsync' and 'unsafe' (default for convert)\n" ++ " 'src_cache' is the cache mode used to read input disk images, the valid\n" ++ " options are the same as for the 'cache' option\n" ++ " 'size' is the disk image size in bytes. Optional suffixes\n" ++ " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M),\n" ++ " 'T' (terabyte, 1024G), 'P' (petabyte, 1024T) and 'E' (exabyte, 1024P) are\n" ++ " supported. 'b' is ignored.\n" ++ " 'output_filename' is the destination disk image filename\n" ++ " 'output_fmt' is the destination format\n" ++ " 'options' is a comma separated list of format specific options in a\n" ++ " name=value format. Use -o help for an overview of the options supported by\n" ++ " the used format\n" ++ " 'snapshot_param' is param used for internal snapshot, format\n" ++ " is 'snapshot.id=[ID],snapshot.name=[NAME]', or\n" ++ " '[ID_OR_NAME]'\n" ++ " '-c' indicates that target image must be compressed (qcow format only)\n" ++ " '-u' allows unsafe backing chains. For rebasing, it is assumed that old and\n" ++ " new backing file match exactly. The image doesn't need a working\n" ++ " backing file before rebasing in this case (useful for renaming the\n" ++ " backing file). For image creation, allow creating without attempting\n" ++ " to open the backing file.\n" ++ " '-h' with or without a command shows this help and lists the supported formats\n" ++ " '-p' show progress of command (only certain commands)\n" ++ " '-q' use Quiet mode - do not print any output (except errors)\n" ++ " '-S' indicates the consecutive number of bytes (defaults to 4k) that must\n" ++ " contain only zeros for qemu-img to create a sparse image during\n" ++ " conversion. If the number of bytes is 0, the source will not be scanned for\n" ++ " unallocated or zero sectors, and the destination image will always be\n" ++ " fully allocated\n" ++ " '--output' takes the format in which the output must be done (human or json)\n" ++ " '-n' skips the target volume creation (useful if the volume is created\n" ++ " prior to running qemu-img)\n" ++ "\n" ++ "Parameters to bitmap subcommand:\n" ++ " 'bitmap' is the name of the bitmap to manipulate, through one or more\n" ++ " actions from '--add', '--remove', '--clear', '--enable', '--disable',\n" ++ " or '--merge source'\n" ++ " '-g granularity' sets the granularity for '--add' actions\n" ++ " '-b source' and '-F src_fmt' tell '--merge' actions to find the source\n" ++ " bitmaps from an alternative file\n" ++ "\n" ++ "Parameters to check subcommand:\n" ++ " '-r' tries to repair any inconsistencies that are found during the check.\n" ++ " '-r leaks' repairs only cluster leaks, whereas '-r all' fixes all\n" ++ " kinds of errors, with a higher risk of choosing the wrong fix or\n" ++ " hiding corruption that has already occurred.\n" ++ "\n" ++ "Parameters to convert subcommand:\n" ++ " '--bitmaps' copies all top-level persistent bitmaps to destination\n" ++ " '-m' specifies how many coroutines work in parallel during the convert\n" ++ " process (defaults to 8)\n" ++ " '-W' allow to write to the target out of order rather than sequential\n" ++ "\n" ++ "Parameters to snapshot subcommand:\n" ++ " 'snapshot' is the name of the snapshot to create, apply or delete\n" ++ " '-a' applies a snapshot (revert disk to saved state)\n" ++ " '-c' creates a snapshot\n" ++ " '-d' deletes a snapshot\n" ++ " '-l' lists all snapshots in the given image\n" ++ "\n" ++ "Parameters to compare subcommand:\n" ++ " '-f' first image format\n" ++ " '-F' second image format\n" ++ " '-s' run in Strict mode - fail on different image size or sector allocation\n" ++ "\n" ++ "Parameters to dd subcommand:\n" ++ " 'bs=BYTES' read and write up to BYTES bytes at a time " ++ "(default: 512)\n" ++ " 'count=N' copy only N input blocks\n" ++ " 'if=FILE' read from FILE\n" ++ " 'of=FILE' write to FILE\n" ++ " 'skip=N' skip N bs-sized blocks at the start of input\n"; ++ ++ printf("%s\nSupported formats:", help_msg); ++ bdrv_iterate_format(format_print, NULL, false); ++ printf("\n\n" QEMU_HELP_BOTTOM "\n"); ++ exit(EXIT_SUCCESS); ++} ++ ++/* ++ * Is @list safe for accumulate_options()? ++ * It is when multiple of them can be joined together separated by ','. ++ * To make that work, @list must not start with ',' (or else a ++ * separating ',' preceding it gets escaped), and it must not end with ++ * an odd number of ',' (or else a separating ',' following it gets ++ * escaped), or be empty (or else a separating ',' preceding it can ++ * escape a separating ',' following it). ++ * ++ */ ++static bool is_valid_option_list(const char *list) ++{ ++ size_t len = strlen(list); ++ size_t i; ++ ++ if (!list[0] || list[0] == ',') { ++ return false; ++ } ++ ++ for (i = len; i > 0 && list[i - 1] == ','; i--) { ++ } ++ if ((len - i) % 2) { ++ return false; ++ } ++ ++ return true; ++} ++ ++static int accumulate_options(char **options, char *list) ++{ ++ char *new_options; ++ ++ if (!is_valid_option_list(list)) { ++ error_report("Invalid option list: %s", list); ++ return -1; ++ } ++ ++ if (!*options) { ++ *options = g_strdup(list); ++ } else { ++ new_options = g_strdup_printf("%s,%s", *options, list); ++ g_free(*options); ++ *options = new_options; ++ } ++ return 0; ++} ++ ++static QemuOptsList qemu_source_opts = { ++ .name = "source", ++ .implied_opt_name = "file", ++ .head = QTAILQ_HEAD_INITIALIZER(qemu_source_opts.head), ++ .desc = { ++ { } ++ }, ++}; ++ ++static int G_GNUC_PRINTF(2, 3) qprintf(bool quiet, const char *fmt, ...) ++{ ++ int ret = 0; ++ if (!quiet) { ++ va_list args; ++ va_start(args, fmt); ++ ret = vprintf(fmt, args); ++ va_end(args); ++ } ++ return ret; ++} ++ ++ ++static int print_block_option_help(const char *filename, const char *fmt) ++{ ++ BlockDriver *drv, *proto_drv; ++ QemuOptsList *create_opts = NULL; ++ Error *local_err = NULL; ++ ++ /* Find driver and parse its options */ ++ drv = bdrv_find_format(fmt); ++ if (!drv) { ++ error_report("Unknown file format '%s'", fmt); ++ return 1; ++ } ++ ++ if (!drv->create_opts) { ++ error_report("Format driver '%s' does not support image creation", fmt); ++ return 1; ++ } ++ ++ create_opts = qemu_opts_append(create_opts, drv->create_opts); ++ if (filename) { ++ proto_drv = bdrv_find_protocol(filename, true, &local_err); ++ if (!proto_drv) { ++ error_report_err(local_err); ++ qemu_opts_free(create_opts); ++ return 1; ++ } ++ if (!proto_drv->create_opts) { ++ error_report("Protocol driver '%s' does not support image creation", ++ proto_drv->format_name); ++ qemu_opts_free(create_opts); ++ return 1; ++ } ++ create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); ++ } ++ ++ if (filename) { ++ printf("Supported options:\n"); ++ } else { ++ printf("Supported %s options:\n", fmt); ++ } ++ qemu_opts_print_help(create_opts, false); ++ qemu_opts_free(create_opts); ++ ++ if (!filename) { ++ printf("\n" ++ "The protocol level may support further options.\n" ++ "Specify the target filename to include those options.\n"); ++ } ++ ++ return 0; ++} ++ ++ ++static BlockBackend *img_open_opts(const char *optstr, ++ QemuOpts *opts, int flags, bool writethrough, ++ bool quiet, bool force_share) ++{ ++ QDict *options; ++ Error *local_err = NULL; ++ BlockBackend *blk; ++ options = qemu_opts_to_qdict(opts, NULL); ++ if (force_share) { ++ if (qdict_haskey(options, BDRV_OPT_FORCE_SHARE) ++ && strcmp(qdict_get_str(options, BDRV_OPT_FORCE_SHARE), "on")) { ++ error_report("--force-share/-U conflicts with image options"); ++ qobject_unref(options); ++ return NULL; ++ } ++ qdict_put_str(options, BDRV_OPT_FORCE_SHARE, "on"); ++ } ++ blk = blk_new_open(NULL, NULL, options, flags, &local_err); ++ if (!blk) { ++ error_reportf_err(local_err, "Could not open '%s': ", optstr); ++ return NULL; ++ } ++ blk_set_enable_write_cache(blk, !writethrough); ++ ++ return blk; ++} ++ ++static BlockBackend *img_open_file(const char *filename, ++ QDict *options, ++ const char *fmt, int flags, ++ bool writethrough, bool quiet, ++ bool force_share) ++{ ++ BlockBackend *blk; ++ Error *local_err = NULL; ++ ++ if (!options) { ++ options = qdict_new(); ++ } ++ if (fmt) { ++ qdict_put_str(options, "driver", fmt); ++ } ++ ++ if (force_share) { ++ qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); ++ } ++ blk = blk_new_open(filename, NULL, options, flags, &local_err); ++ if (!blk) { ++ error_reportf_err(local_err, "Could not open '%s': ", filename); ++ return NULL; ++ } ++ blk_set_enable_write_cache(blk, !writethrough); ++ ++ return blk; ++} ++ ++ ++static int img_add_key_secrets(void *opaque, ++ const char *name, const char *value, ++ Error **errp) ++{ ++ QDict *options = opaque; ++ ++ if (g_str_has_suffix(name, "key-secret")) { ++ qdict_put_str(options, name, value); ++ } ++ ++ return 0; ++} ++ ++ ++static BlockBackend *img_open(bool image_opts, ++ const char *filename, ++ const char *fmt, int flags, bool writethrough, ++ bool quiet, bool force_share) ++{ ++ BlockBackend *blk; ++ if (image_opts) { ++ QemuOpts *opts; ++ if (fmt) { ++ error_report("--image-opts and --format are mutually exclusive"); ++ return NULL; ++ } ++ opts = qemu_opts_parse_noisily(qemu_find_opts("source"), ++ filename, true); ++ if (!opts) { ++ return NULL; ++ } ++ blk = img_open_opts(filename, opts, flags, writethrough, quiet, ++ force_share); ++ } else { ++ blk = img_open_file(filename, NULL, fmt, flags, writethrough, quiet, ++ force_share); ++ } ++ ++ if (blk) { ++ blk_set_force_allow_inactivate(blk); ++ } ++ ++ return blk; ++} ++ ++ ++static int add_old_style_options(const char *fmt, QemuOpts *opts, ++ const char *base_filename, ++ const char *base_fmt) ++{ ++ if (base_filename) { ++ if (!qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename, ++ NULL)) { ++ error_report("Backing file not supported for file format '%s'", ++ fmt); ++ return -1; ++ } ++ } ++ if (base_fmt) { ++ if (!qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt, NULL)) { ++ error_report("Backing file format not supported for file " ++ "format '%s'", fmt); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++static int64_t cvtnum_full(const char *name, const char *value, int64_t min, ++ int64_t max) ++{ ++ int err; ++ uint64_t res; ++ ++ err = qemu_strtosz(value, NULL, &res); ++ if (err < 0 && err != -ERANGE) { ++ error_report("Invalid %s specified. You may use " ++ "k, M, G, T, P or E suffixes for", name); ++ error_report("kilobytes, megabytes, gigabytes, terabytes, " ++ "petabytes and exabytes."); ++ return err; ++ } ++ if (err == -ERANGE || res > max || res < min) { ++ error_report("Invalid %s specified. Must be between %" PRId64 ++ " and %" PRId64 ".", name, min, max); ++ return -ERANGE; ++ } ++ return res; ++} ++ ++static int64_t cvtnum(const char *name, const char *value) ++{ ++ return cvtnum_full(name, value, 0, INT64_MAX); ++} ++ ++static int img_create(int argc, char **argv) ++{ ++ int c; ++ uint64_t img_size = -1; ++ const char *fmt = "raw"; ++ const char *base_fmt = NULL; ++ const char *filename; ++ const char *base_filename = NULL; ++ char *options = NULL; ++ Error *local_err = NULL; ++ bool quiet = false; ++ int flags = 0; ++ ++ for(;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":F:b:f:ho:qu", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'F': ++ base_fmt = optarg; ++ break; ++ case 'b': ++ base_filename = optarg; ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'o': ++ if (accumulate_options(&options, optarg) < 0) { ++ goto fail; ++ } ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case 'u': ++ flags |= BDRV_O_NO_BACKING; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ } ++ } ++ ++ /* Get the filename */ ++ filename = (optind < argc) ? argv[optind] : NULL; ++ if (options && has_help_option(options)) { ++ g_free(options); ++ return print_block_option_help(filename, fmt); ++ } ++ ++ if (optind >= argc) { ++ error_exit("Expecting image file name"); ++ } ++ optind++; ++ ++ /* Get image size, if specified */ ++ if (optind < argc) { ++ int64_t sval; ++ ++ sval = cvtnum("image size", argv[optind++]); ++ if (sval < 0) { ++ goto fail; ++ } ++ img_size = (uint64_t)sval; ++ } ++ if (optind != argc) { ++ error_exit("Unexpected argument: %s", argv[optind]); ++ } ++ ++ bdrv_img_create(filename, fmt, base_filename, base_fmt, ++ options, img_size, flags, quiet, &local_err); ++ if (local_err) { ++ error_reportf_err(local_err, "%s: ", filename); ++ goto fail; ++ } ++ ++ g_free(options); ++ return 0; ++ ++fail: ++ g_free(options); ++ return 1; ++} ++ ++static void dump_json_image_check(ImageCheck *check, bool quiet) ++{ ++ GString *str; ++ QObject *obj; ++ Visitor *v = qobject_output_visitor_new(&obj); ++ ++ visit_type_ImageCheck(v, NULL, &check, &error_abort); ++ visit_complete(v, &obj); ++ str = qobject_to_json_pretty(obj, true); ++ assert(str != NULL); ++ qprintf(quiet, "%s\n", str->str); ++ qobject_unref(obj); ++ visit_free(v); ++ g_string_free(str, true); ++} ++ ++static void dump_human_image_check(ImageCheck *check, bool quiet) ++{ ++ if (!(check->corruptions || check->leaks || check->check_errors)) { ++ qprintf(quiet, "No errors were found on the image.\n"); ++ } else { ++ if (check->corruptions) { ++ qprintf(quiet, "\n%" PRId64 " errors were found on the image.\n" ++ "Data may be corrupted, or further writes to the image " ++ "may corrupt it.\n", ++ check->corruptions); ++ } ++ ++ if (check->leaks) { ++ qprintf(quiet, ++ "\n%" PRId64 " leaked clusters were found on the image.\n" ++ "This means waste of disk space, but no harm to data.\n", ++ check->leaks); ++ } ++ ++ if (check->check_errors) { ++ qprintf(quiet, ++ "\n%" PRId64 ++ " internal errors have occurred during the check.\n", ++ check->check_errors); ++ } ++ } ++ ++ if (check->total_clusters != 0 && check->allocated_clusters != 0) { ++ qprintf(quiet, "%" PRId64 "/%" PRId64 " = %0.2f%% allocated, " ++ "%0.2f%% fragmented, %0.2f%% compressed clusters\n", ++ check->allocated_clusters, check->total_clusters, ++ check->allocated_clusters * 100.0 / check->total_clusters, ++ check->fragmented_clusters * 100.0 / check->allocated_clusters, ++ check->compressed_clusters * 100.0 / ++ check->allocated_clusters); ++ } ++ ++ if (check->image_end_offset) { ++ qprintf(quiet, ++ "Image end offset: %" PRId64 "\n", check->image_end_offset); ++ } ++} ++ ++static int collect_image_check(BlockDriverState *bs, ++ ImageCheck *check, ++ const char *filename, ++ const char *fmt, ++ int fix) ++{ ++ int ret; ++ BdrvCheckResult result; ++ ++ ret = bdrv_check(bs, &result, fix); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ check->filename = g_strdup(filename); ++ check->format = g_strdup(bdrv_get_format_name(bs)); ++ check->check_errors = result.check_errors; ++ check->corruptions = result.corruptions; ++ check->has_corruptions = result.corruptions != 0; ++ check->leaks = result.leaks; ++ check->has_leaks = result.leaks != 0; ++ check->corruptions_fixed = result.corruptions_fixed; ++ check->has_corruptions_fixed = result.corruptions_fixed != 0; ++ check->leaks_fixed = result.leaks_fixed; ++ check->has_leaks_fixed = result.leaks_fixed != 0; ++ check->image_end_offset = result.image_end_offset; ++ check->has_image_end_offset = result.image_end_offset != 0; ++ check->total_clusters = result.bfi.total_clusters; ++ check->has_total_clusters = result.bfi.total_clusters != 0; ++ check->allocated_clusters = result.bfi.allocated_clusters; ++ check->has_allocated_clusters = result.bfi.allocated_clusters != 0; ++ check->fragmented_clusters = result.bfi.fragmented_clusters; ++ check->has_fragmented_clusters = result.bfi.fragmented_clusters != 0; ++ check->compressed_clusters = result.bfi.compressed_clusters; ++ check->has_compressed_clusters = result.bfi.compressed_clusters != 0; ++ ++ return 0; ++} ++ ++/* ++ * Checks an image for consistency. Exit codes: ++ * ++ * 0 - Check completed, image is good ++ * 1 - Check not completed because of internal errors ++ * 2 - Check completed, image is corrupted ++ * 3 - Check completed, image has leaked clusters, but is good otherwise ++ * 63 - Checks are not supported by the image format ++ */ ++static int img_check(int argc, char **argv) ++{ ++ int c, ret; ++ OutputFormat output_format = OFORMAT_HUMAN; ++ const char *filename, *fmt, *output, *cache; ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ int fix = 0; ++ int flags = BDRV_O_CHECK; ++ bool writethrough; ++ ImageCheck *check; ++ bool quiet = false; ++ bool image_opts = false; ++ bool force_share = false; ++ ++ fmt = NULL; ++ output = NULL; ++ cache = BDRV_DEFAULT_CACHE; ++ ++ for(;;) { ++ int option_index = 0; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"format", required_argument, 0, 'f'}, ++ {"repair", required_argument, 0, 'r'}, ++ {"output", required_argument, 0, OPTION_OUTPUT}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":hf:r:T:qU", ++ long_options, &option_index); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'r': ++ flags |= BDRV_O_RDWR; ++ ++ if (!strcmp(optarg, "leaks")) { ++ fix = BDRV_FIX_LEAKS; ++ } else if (!strcmp(optarg, "all")) { ++ fix = BDRV_FIX_LEAKS | BDRV_FIX_ERRORS; ++ } else { ++ error_exit("Unknown option value for -r " ++ "(expecting 'leaks' or 'all'): %s", optarg); ++ } ++ break; ++ case OPTION_OUTPUT: ++ output = optarg; ++ break; ++ case 'T': ++ cache = optarg; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ filename = argv[optind++]; ++ ++ if (output && !strcmp(output, "json")) { ++ output_format = OFORMAT_JSON; ++ } else if (output && !strcmp(output, "human")) { ++ output_format = OFORMAT_HUMAN; ++ } else if (output) { ++ error_report("--output must be used with human or json as argument."); ++ return 1; ++ } ++ ++ ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid source cache option: %s", cache); ++ return 1; ++ } ++ ++ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet, ++ force_share); ++ if (!blk) { ++ return 1; ++ } ++ bs = blk_bs(blk); ++ ++ check = g_new0(ImageCheck, 1); ++ ret = collect_image_check(bs, check, filename, fmt, fix); ++ ++ if (ret == -ENOTSUP) { ++ error_report("This image format does not support checks"); ++ ret = 63; ++ goto fail; ++ } ++ ++ if (check->corruptions_fixed || check->leaks_fixed) { ++ int corruptions_fixed, leaks_fixed; ++ bool has_leaks_fixed, has_corruptions_fixed; ++ ++ leaks_fixed = check->leaks_fixed; ++ has_leaks_fixed = check->has_leaks_fixed; ++ corruptions_fixed = check->corruptions_fixed; ++ has_corruptions_fixed = check->has_corruptions_fixed; ++ ++ if (output_format == OFORMAT_HUMAN) { ++ qprintf(quiet, ++ "The following inconsistencies were found and repaired:\n\n" ++ " %" PRId64 " leaked clusters\n" ++ " %" PRId64 " corruptions\n\n" ++ "Double checking the fixed image now...\n", ++ check->leaks_fixed, ++ check->corruptions_fixed); ++ } ++ ++ qapi_free_ImageCheck(check); ++ check = g_new0(ImageCheck, 1); ++ ret = collect_image_check(bs, check, filename, fmt, 0); ++ ++ check->leaks_fixed = leaks_fixed; ++ check->has_leaks_fixed = has_leaks_fixed; ++ check->corruptions_fixed = corruptions_fixed; ++ check->has_corruptions_fixed = has_corruptions_fixed; ++ } ++ ++ if (!ret) { ++ switch (output_format) { ++ case OFORMAT_HUMAN: ++ dump_human_image_check(check, quiet); ++ break; ++ case OFORMAT_JSON: ++ dump_json_image_check(check, quiet); ++ break; ++ } ++ } ++ ++ if (ret || check->check_errors) { ++ if (ret) { ++ error_report("Check failed: %s", strerror(-ret)); ++ } else { ++ error_report("Check failed"); ++ } ++ ret = 1; ++ goto fail; ++ } ++ ++ if (check->corruptions) { ++ ret = 2; ++ } else if (check->leaks) { ++ ret = 3; ++ } else { ++ ret = 0; ++ } ++ ++fail: ++ qapi_free_ImageCheck(check); ++ blk_unref(blk); ++ return ret; ++} ++ ++typedef struct CommonBlockJobCBInfo { ++ BlockDriverState *bs; ++ Error **errp; ++} CommonBlockJobCBInfo; ++ ++static void common_block_job_cb(void *opaque, int ret) ++{ ++ CommonBlockJobCBInfo *cbi = opaque; ++ ++ if (ret < 0) { ++ error_setg_errno(cbi->errp, -ret, "Block job failed"); ++ } ++} ++ ++static void run_block_job(BlockJob *job, Error **errp) ++{ ++ uint64_t progress_current, progress_total; ++ AioContext *aio_context = block_job_get_aio_context(job); ++ int ret = 0; ++ ++ job_lock(); ++ job_ref_locked(&job->job); ++ do { ++ float progress = 0.0f; ++ job_unlock(); ++ aio_poll(aio_context, true); ++ ++ progress_get_snapshot(&job->job.progress, &progress_current, ++ &progress_total); ++ if (progress_total) { ++ progress = (float)progress_current / progress_total * 100.f; ++ } ++ qemu_progress_print(progress, 0); ++ job_lock(); ++ } while (!job_is_ready_locked(&job->job) && ++ !job_is_completed_locked(&job->job)); ++ ++ if (!job_is_completed_locked(&job->job)) { ++ ret = job_complete_sync_locked(&job->job, errp); ++ } else { ++ ret = job->job.ret; ++ } ++ job_unref_locked(&job->job); ++ job_unlock(); ++ ++ /* publish completion progress only when success */ ++ if (!ret) { ++ qemu_progress_print(100.f, 0); ++ } ++} ++ ++static int img_commit(int argc, char **argv) ++{ ++ int c, ret, flags; ++ const char *filename, *fmt, *cache, *base; ++ BlockBackend *blk; ++ BlockDriverState *bs, *base_bs; ++ BlockJob *job; ++ bool progress = false, quiet = false, drop = false; ++ bool writethrough; ++ Error *local_err = NULL; ++ CommonBlockJobCBInfo cbi; ++ bool image_opts = false; ++ int64_t rate_limit = 0; ++ ++ fmt = NULL; ++ cache = BDRV_DEFAULT_CACHE; ++ base = NULL; ++ for(;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":f:ht:b:dpqr:", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 't': ++ cache = optarg; ++ break; ++ case 'b': ++ base = optarg; ++ /* -b implies -d */ ++ drop = true; ++ break; ++ case 'd': ++ drop = true; ++ break; ++ case 'p': ++ progress = true; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case 'r': ++ rate_limit = cvtnum("rate limit", optarg); ++ if (rate_limit < 0) { ++ return 1; ++ } ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ ++ /* Progress is not shown in Quiet mode */ ++ if (quiet) { ++ progress = false; ++ } ++ ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ filename = argv[optind++]; ++ ++ flags = BDRV_O_RDWR | BDRV_O_UNMAP; ++ ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid cache option: %s", cache); ++ return 1; ++ } ++ ++ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet, ++ false); ++ if (!blk) { ++ return 1; ++ } ++ bs = blk_bs(blk); ++ ++ qemu_progress_init(progress, 1.f); ++ qemu_progress_print(0.f, 100); ++ ++ bdrv_graph_rdlock_main_loop(); ++ if (base) { ++ base_bs = bdrv_find_backing_image(bs, base); ++ if (!base_bs) { ++ error_setg(&local_err, ++ "Did not find '%s' in the backing chain of '%s'", ++ base, filename); ++ bdrv_graph_rdunlock_main_loop(); ++ goto done; ++ } ++ } else { ++ /* This is different from QMP, which by default uses the deepest file in ++ * the backing chain (i.e., the very base); however, the traditional ++ * behavior of qemu-img commit is using the immediate backing file. */ ++ base_bs = bdrv_backing_chain_next(bs); ++ if (!base_bs) { ++ error_setg(&local_err, "Image does not have a backing file"); ++ bdrv_graph_rdunlock_main_loop(); ++ goto done; ++ } ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ cbi = (CommonBlockJobCBInfo){ ++ .errp = &local_err, ++ .bs = bs, ++ }; ++ ++ commit_active_start("commit", bs, base_bs, JOB_DEFAULT, rate_limit, ++ BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb, ++ &cbi, false, &local_err); ++ if (local_err) { ++ goto done; ++ } ++ ++ /* When the block job completes, the BlockBackend reference will point to ++ * the old backing file. In order to avoid that the top image is already ++ * deleted, so we can still empty it afterwards, increment the reference ++ * counter here preemptively. */ ++ if (!drop) { ++ bdrv_ref(bs); ++ } ++ ++ job = block_job_get("commit"); ++ assert(job); ++ run_block_job(job, &local_err); ++ if (local_err) { ++ goto unref_backing; ++ } ++ ++ if (!drop) { ++ BlockBackend *old_backing_blk; ++ ++ old_backing_blk = blk_new_with_bs(bs, BLK_PERM_WRITE, BLK_PERM_ALL, ++ &local_err); ++ if (!old_backing_blk) { ++ goto unref_backing; ++ } ++ ret = blk_make_empty(old_backing_blk, &local_err); ++ blk_unref(old_backing_blk); ++ if (ret == -ENOTSUP) { ++ error_free(local_err); ++ local_err = NULL; ++ } else if (ret < 0) { ++ goto unref_backing; ++ } ++ } ++ ++unref_backing: ++ if (!drop) { ++ bdrv_unref(bs); ++ } ++ ++done: ++ qemu_progress_end(); ++ ++ /* ++ * Manually inactivate the image first because this way we can know whether ++ * an error occurred. blk_unref() doesn't tell us about failures. ++ */ ++ ret = bdrv_inactivate_all(); ++ if (ret < 0 && !local_err) { ++ error_setg_errno(&local_err, -ret, "Error while closing the image"); ++ } ++ blk_unref(blk); ++ ++ if (local_err) { ++ error_report_err(local_err); ++ return 1; ++ } ++ ++ qprintf(quiet, "Image committed.\n"); ++ return 0; ++} ++ ++/* ++ * Returns -1 if 'buf' contains only zeroes, otherwise the byte index ++ * of the first sector boundary within buf where the sector contains a ++ * non-zero byte. This function is robust to a buffer that is not ++ * sector-aligned. ++ */ ++static int64_t find_nonzero(const uint8_t *buf, int64_t n) ++{ ++ int64_t i; ++ int64_t end = QEMU_ALIGN_DOWN(n, BDRV_SECTOR_SIZE); ++ ++ for (i = 0; i < end; i += BDRV_SECTOR_SIZE) { ++ if (!buffer_is_zero(buf + i, BDRV_SECTOR_SIZE)) { ++ return i; ++ } ++ } ++ if (i < n && !buffer_is_zero(buf + i, n - end)) { ++ return i; ++ } ++ return -1; ++} ++ ++/* ++ * Returns true iff the first sector pointed to by 'buf' contains at least ++ * a non-NUL byte. ++ * ++ * 'pnum' is set to the number of sectors (including and immediately following ++ * the first one) that are known to be in the same allocated/unallocated state. ++ * The function will try to align the end offset to alignment boundaries so ++ * that the request will at least end aligned and consecutive requests will ++ * also start at an aligned offset. ++ */ ++static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum, ++ int64_t sector_num, int alignment) ++{ ++ bool is_zero; ++ int i, tail; ++ ++ if (n <= 0) { ++ *pnum = 0; ++ return 0; ++ } ++ is_zero = buffer_is_zero(buf, BDRV_SECTOR_SIZE); ++ for(i = 1; i < n; i++) { ++ buf += BDRV_SECTOR_SIZE; ++ if (is_zero != buffer_is_zero(buf, BDRV_SECTOR_SIZE)) { ++ break; ++ } ++ } ++ ++ if (i == n) { ++ /* ++ * The whole buf is the same. ++ * No reason to split it into chunks, so return now. ++ */ ++ *pnum = i; ++ return !is_zero; ++ } ++ ++ tail = (sector_num + i) & (alignment - 1); ++ if (tail) { ++ if (is_zero && i <= tail) { ++ /* ++ * For sure next sector after i is data, and it will rewrite this ++ * tail anyway due to RMW. So, let's just write data now. ++ */ ++ is_zero = false; ++ } ++ if (!is_zero) { ++ /* If possible, align up end offset of allocated areas. */ ++ i += alignment - tail; ++ i = MIN(i, n); ++ } else { ++ /* ++ * For sure next sector after i is data, and it will rewrite this ++ * tail anyway due to RMW. Better is avoid RMW and write zeroes up ++ * to aligned bound. ++ */ ++ i -= tail; ++ } ++ } ++ *pnum = i; ++ return !is_zero; ++} ++ ++/* ++ * Like is_allocated_sectors, but if the buffer starts with a used sector, ++ * up to 'min' consecutive sectors containing zeros are ignored. This avoids ++ * breaking up write requests for only small sparse areas. ++ */ ++static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, ++ int min, int64_t sector_num, int alignment) ++{ ++ int ret; ++ int num_checked, num_used; ++ ++ if (n < min) { ++ min = n; ++ } ++ ++ ret = is_allocated_sectors(buf, n, pnum, sector_num, alignment); ++ if (!ret) { ++ return ret; ++ } ++ ++ num_used = *pnum; ++ buf += BDRV_SECTOR_SIZE * *pnum; ++ n -= *pnum; ++ sector_num += *pnum; ++ num_checked = num_used; ++ ++ while (n > 0) { ++ ret = is_allocated_sectors(buf, n, pnum, sector_num, alignment); ++ ++ buf += BDRV_SECTOR_SIZE * *pnum; ++ n -= *pnum; ++ sector_num += *pnum; ++ num_checked += *pnum; ++ if (ret) { ++ num_used = num_checked; ++ } else if (*pnum >= min) { ++ break; ++ } ++ } ++ ++ *pnum = num_used; ++ return 1; ++} ++ ++/* ++ * Compares two buffers chunk by chunk, where @chsize is the chunk size. ++ * If @chsize is 0, default chunk size of BDRV_SECTOR_SIZE is used. ++ * Returns 0 if the first chunk of each buffer matches, non-zero otherwise. ++ * ++ * @pnum is set to the size of the buffer prefix aligned to @chsize that ++ * has the same matching status as the first chunk. ++ */ ++static int compare_buffers(const uint8_t *buf1, const uint8_t *buf2, ++ int64_t bytes, uint64_t chsize, int64_t *pnum) ++{ ++ bool res; ++ int64_t i; ++ ++ assert(bytes > 0); ++ ++ if (!chsize) { ++ chsize = BDRV_SECTOR_SIZE; ++ } ++ i = MIN(bytes, chsize); ++ ++ res = !!memcmp(buf1, buf2, i); ++ while (i < bytes) { ++ int64_t len = MIN(bytes - i, chsize); ++ ++ if (!!memcmp(buf1 + i, buf2 + i, len) != res) { ++ break; ++ } ++ i += len; ++ } ++ ++ *pnum = i; ++ return res; ++} ++ ++#define IO_BUF_SIZE (2 * MiB) ++ ++/* ++ * Check if passed sectors are empty (not allocated or contain only 0 bytes) ++ * ++ * Intended for use by 'qemu-img compare': Returns 0 in case sectors are ++ * filled with 0, 1 if sectors contain non-zero data (this is a comparison ++ * failure), and 4 on error (the exit status for read errors), after emitting ++ * an error message. ++ * ++ * @param blk: BlockBackend for the image ++ * @param offset: Starting offset to check ++ * @param bytes: Number of bytes to check ++ * @param filename: Name of disk file we are checking (logging purpose) ++ * @param buffer: Allocated buffer for storing read data ++ * @param quiet: Flag for quiet mode ++ */ ++static int check_empty_sectors(BlockBackend *blk, int64_t offset, ++ int64_t bytes, const char *filename, ++ uint8_t *buffer, bool quiet) ++{ ++ int ret = 0; ++ int64_t idx; ++ ++ ret = blk_pread(blk, offset, bytes, buffer, 0); ++ if (ret < 0) { ++ error_report("Error while reading offset %" PRId64 " of %s: %s", ++ offset, filename, strerror(-ret)); ++ return 4; ++ } ++ idx = find_nonzero(buffer, bytes); ++ if (idx >= 0) { ++ qprintf(quiet, "Content mismatch at offset %" PRId64 "!\n", ++ offset + idx); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Compares two images. Exit codes: ++ * ++ * 0 - Images are identical or the requested help was printed ++ * 1 - Images differ ++ * >1 - Error occurred ++ */ ++static int img_compare(int argc, char **argv) ++{ ++ const char *fmt1 = NULL, *fmt2 = NULL, *cache, *filename1, *filename2; ++ BlockBackend *blk1, *blk2; ++ BlockDriverState *bs1, *bs2; ++ int64_t total_size1, total_size2; ++ uint8_t *buf1 = NULL, *buf2 = NULL; ++ int64_t pnum1, pnum2; ++ int allocated1, allocated2; ++ int ret = 0; /* return value - 0 Ident, 1 Different, >1 Error */ ++ bool progress = false, quiet = false, strict = false; ++ int flags; ++ bool writethrough; ++ int64_t total_size; ++ int64_t offset = 0; ++ int64_t chunk; ++ int c; ++ uint64_t progress_base; ++ bool image_opts = false; ++ bool force_share = false; ++ ++ cache = BDRV_DEFAULT_CACHE; ++ for (;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":hf:F:T:pqsU", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt1 = optarg; ++ break; ++ case 'F': ++ fmt2 = optarg; ++ break; ++ case 'T': ++ cache = optarg; ++ break; ++ case 'p': ++ progress = true; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case 's': ++ strict = true; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OBJECT: ++ { ++ Error *local_err = NULL; ++ ++ if (!user_creatable_add_from_str(optarg, &local_err)) { ++ if (local_err) { ++ error_report_err(local_err); ++ exit(2); ++ } else { ++ /* Help was printed */ ++ exit(EXIT_SUCCESS); ++ } ++ } ++ break; ++ } ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ ++ /* Progress is not shown in Quiet mode */ ++ if (quiet) { ++ progress = false; ++ } ++ ++ ++ if (optind != argc - 2) { ++ error_exit("Expecting two image file names"); ++ } ++ filename1 = argv[optind++]; ++ filename2 = argv[optind++]; ++ ++ /* Initialize before goto out */ ++ qemu_progress_init(progress, 2.0); ++ ++ flags = 0; ++ ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid source cache option: %s", cache); ++ ret = 2; ++ goto out3; ++ } ++ ++ blk1 = img_open(image_opts, filename1, fmt1, flags, writethrough, quiet, ++ force_share); ++ if (!blk1) { ++ ret = 2; ++ goto out3; ++ } ++ ++ blk2 = img_open(image_opts, filename2, fmt2, flags, writethrough, quiet, ++ force_share); ++ if (!blk2) { ++ ret = 2; ++ goto out2; ++ } ++ bs1 = blk_bs(blk1); ++ bs2 = blk_bs(blk2); ++ ++ buf1 = blk_blockalign(blk1, IO_BUF_SIZE); ++ buf2 = blk_blockalign(blk2, IO_BUF_SIZE); ++ total_size1 = blk_getlength(blk1); ++ if (total_size1 < 0) { ++ error_report("Can't get size of %s: %s", ++ filename1, strerror(-total_size1)); ++ ret = 4; ++ goto out; ++ } ++ total_size2 = blk_getlength(blk2); ++ if (total_size2 < 0) { ++ error_report("Can't get size of %s: %s", ++ filename2, strerror(-total_size2)); ++ ret = 4; ++ goto out; ++ } ++ total_size = MIN(total_size1, total_size2); ++ progress_base = MAX(total_size1, total_size2); ++ ++ qemu_progress_print(0, 100); ++ ++ if (strict && total_size1 != total_size2) { ++ ret = 1; ++ qprintf(quiet, "Strict mode: Image size mismatch!\n"); ++ goto out; ++ } ++ ++ while (offset < total_size) { ++ int status1, status2; ++ ++ status1 = bdrv_block_status_above(bs1, NULL, offset, ++ total_size1 - offset, &pnum1, NULL, ++ NULL); ++ if (status1 < 0) { ++ ret = 3; ++ error_report("Sector allocation test failed for %s", filename1); ++ goto out; ++ } ++ allocated1 = status1 & BDRV_BLOCK_ALLOCATED; ++ ++ status2 = bdrv_block_status_above(bs2, NULL, offset, ++ total_size2 - offset, &pnum2, NULL, ++ NULL); ++ if (status2 < 0) { ++ ret = 3; ++ error_report("Sector allocation test failed for %s", filename2); ++ goto out; ++ } ++ allocated2 = status2 & BDRV_BLOCK_ALLOCATED; ++ ++ assert(pnum1 && pnum2); ++ chunk = MIN(pnum1, pnum2); ++ ++ if (strict) { ++ if (status1 != status2) { ++ ret = 1; ++ qprintf(quiet, "Strict mode: Offset %" PRId64 ++ " block status mismatch!\n", offset); ++ goto out; ++ } ++ } ++ if ((status1 & BDRV_BLOCK_ZERO) && (status2 & BDRV_BLOCK_ZERO)) { ++ /* nothing to do */ ++ } else if (allocated1 == allocated2) { ++ if (allocated1) { ++ int64_t pnum; ++ ++ chunk = MIN(chunk, IO_BUF_SIZE); ++ ret = blk_pread(blk1, offset, chunk, buf1, 0); ++ if (ret < 0) { ++ error_report("Error while reading offset %" PRId64 ++ " of %s: %s", ++ offset, filename1, strerror(-ret)); ++ ret = 4; ++ goto out; ++ } ++ ret = blk_pread(blk2, offset, chunk, buf2, 0); ++ if (ret < 0) { ++ error_report("Error while reading offset %" PRId64 ++ " of %s: %s", ++ offset, filename2, strerror(-ret)); ++ ret = 4; ++ goto out; ++ } ++ ret = compare_buffers(buf1, buf2, chunk, 0, &pnum); ++ if (ret || pnum != chunk) { ++ qprintf(quiet, "Content mismatch at offset %" PRId64 "!\n", ++ offset + (ret ? 0 : pnum)); ++ ret = 1; ++ goto out; ++ } ++ } ++ } else { ++ chunk = MIN(chunk, IO_BUF_SIZE); ++ if (allocated1) { ++ ret = check_empty_sectors(blk1, offset, chunk, ++ filename1, buf1, quiet); ++ } else { ++ ret = check_empty_sectors(blk2, offset, chunk, ++ filename2, buf1, quiet); ++ } ++ if (ret) { ++ goto out; ++ } ++ } ++ offset += chunk; ++ qemu_progress_print(((float) chunk / progress_base) * 100, 100); ++ } ++ ++ if (total_size1 != total_size2) { ++ BlockBackend *blk_over; ++ const char *filename_over; ++ ++ qprintf(quiet, "Warning: Image size mismatch!\n"); ++ if (total_size1 > total_size2) { ++ blk_over = blk1; ++ filename_over = filename1; ++ } else { ++ blk_over = blk2; ++ filename_over = filename2; ++ } ++ ++ while (offset < progress_base) { ++ ret = bdrv_block_status_above(blk_bs(blk_over), NULL, offset, ++ progress_base - offset, &chunk, ++ NULL, NULL); ++ if (ret < 0) { ++ ret = 3; ++ error_report("Sector allocation test failed for %s", ++ filename_over); ++ goto out; ++ ++ } ++ if (ret & BDRV_BLOCK_ALLOCATED && !(ret & BDRV_BLOCK_ZERO)) { ++ chunk = MIN(chunk, IO_BUF_SIZE); ++ ret = check_empty_sectors(blk_over, offset, chunk, ++ filename_over, buf1, quiet); ++ if (ret) { ++ goto out; ++ } ++ } ++ offset += chunk; ++ qemu_progress_print(((float) chunk / progress_base) * 100, 100); ++ } ++ } ++ ++ qprintf(quiet, "Images are identical.\n"); ++ ret = 0; ++ ++out: ++ qemu_vfree(buf1); ++ qemu_vfree(buf2); ++ blk_unref(blk2); ++out2: ++ blk_unref(blk1); ++out3: ++ qemu_progress_end(); ++ return ret; ++} ++ ++/* Convenience wrapper around qmp_block_dirty_bitmap_merge */ ++static void do_dirty_bitmap_merge(const char *dst_node, const char *dst_name, ++ const char *src_node, const char *src_name, ++ Error **errp) ++{ ++ BlockDirtyBitmapOrStr *merge_src; ++ BlockDirtyBitmapOrStrList *list = NULL; ++ ++ merge_src = g_new0(BlockDirtyBitmapOrStr, 1); ++ merge_src->type = QTYPE_QDICT; ++ merge_src->u.external.node = g_strdup(src_node); ++ merge_src->u.external.name = g_strdup(src_name); ++ QAPI_LIST_PREPEND(list, merge_src); ++ qmp_block_dirty_bitmap_merge(dst_node, dst_name, list, errp); ++ qapi_free_BlockDirtyBitmapOrStrList(list); ++} ++ ++enum ImgConvertBlockStatus { ++ BLK_DATA, ++ BLK_ZERO, ++ BLK_BACKING_FILE, ++}; ++ ++#define MAX_COROUTINES 16 ++#define CONVERT_THROTTLE_GROUP "img_convert" ++ ++typedef struct ImgConvertState { ++ BlockBackend **src; ++ int64_t *src_sectors; ++ int *src_alignment; ++ int src_num; ++ int64_t total_sectors; ++ int64_t allocated_sectors; ++ int64_t allocated_done; ++ int64_t sector_num; ++ int64_t wr_offs; ++ enum ImgConvertBlockStatus status; ++ int64_t sector_next_status; ++ BlockBackend *target; ++ bool has_zero_init; ++ bool compressed; ++ bool target_is_new; ++ bool target_has_backing; ++ int64_t target_backing_sectors; /* negative if unknown */ ++ bool wr_in_order; ++ bool copy_range; ++ bool salvage; ++ bool quiet; ++ int min_sparse; ++ int alignment; ++ size_t cluster_sectors; ++ size_t buf_sectors; ++ long num_coroutines; ++ int running_coroutines; ++ Coroutine *co[MAX_COROUTINES]; ++ int64_t wait_sector_num[MAX_COROUTINES]; ++ CoMutex lock; ++ int ret; ++} ImgConvertState; ++ ++static void convert_select_part(ImgConvertState *s, int64_t sector_num, ++ int *src_cur, int64_t *src_cur_offset) ++{ ++ *src_cur = 0; ++ *src_cur_offset = 0; ++ while (sector_num - *src_cur_offset >= s->src_sectors[*src_cur]) { ++ *src_cur_offset += s->src_sectors[*src_cur]; ++ (*src_cur)++; ++ assert(*src_cur < s->src_num); ++ } ++} ++ ++static int coroutine_mixed_fn GRAPH_RDLOCK ++convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) ++{ ++ int64_t src_cur_offset; ++ int ret, n, src_cur; ++ bool post_backing_zero = false; ++ ++ convert_select_part(s, sector_num, &src_cur, &src_cur_offset); ++ ++ assert(s->total_sectors > sector_num); ++ n = MIN(s->total_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS); ++ ++ if (s->target_backing_sectors >= 0) { ++ if (sector_num >= s->target_backing_sectors) { ++ post_backing_zero = true; ++ } else if (sector_num + n > s->target_backing_sectors) { ++ /* Split requests around target_backing_sectors (because ++ * starting from there, zeros are handled differently) */ ++ n = s->target_backing_sectors - sector_num; ++ } ++ } ++ ++ if (s->sector_next_status <= sector_num) { ++ uint64_t offset = (sector_num - src_cur_offset) * BDRV_SECTOR_SIZE; ++ int64_t count; ++ int tail; ++ BlockDriverState *src_bs = blk_bs(s->src[src_cur]); ++ BlockDriverState *base; ++ ++ if (s->target_has_backing) { ++ base = bdrv_cow_bs(bdrv_skip_filters(src_bs)); ++ } else { ++ base = NULL; ++ } ++ ++ do { ++ count = n * BDRV_SECTOR_SIZE; ++ ++ ret = bdrv_block_status_above(src_bs, base, offset, count, &count, ++ NULL, NULL); ++ ++ if (ret < 0) { ++ if (s->salvage) { ++ if (n == 1) { ++ if (!s->quiet) { ++ warn_report("error while reading block status at " ++ "offset %" PRIu64 ": %s", offset, ++ strerror(-ret)); ++ } ++ /* Just try to read the data, then */ ++ ret = BDRV_BLOCK_DATA; ++ count = BDRV_SECTOR_SIZE; ++ } else { ++ /* Retry on a shorter range */ ++ n = DIV_ROUND_UP(n, 4); ++ } ++ } else { ++ error_report("error while reading block status at offset " ++ "%" PRIu64 ": %s", offset, strerror(-ret)); ++ return ret; ++ } ++ } ++ } while (ret < 0); ++ ++ n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE); ++ ++ /* ++ * Avoid that s->sector_next_status becomes unaligned to the source ++ * request alignment and/or cluster size to avoid unnecessary read ++ * cycles. ++ */ ++ tail = (sector_num - src_cur_offset + n) % s->src_alignment[src_cur]; ++ if (n > tail) { ++ n -= tail; ++ } ++ ++ if (ret & BDRV_BLOCK_ZERO) { ++ s->status = post_backing_zero ? BLK_BACKING_FILE : BLK_ZERO; ++ } else if (ret & BDRV_BLOCK_DATA) { ++ s->status = BLK_DATA; ++ } else { ++ s->status = s->target_has_backing ? BLK_BACKING_FILE : BLK_DATA; ++ } ++ ++ s->sector_next_status = sector_num + n; ++ } ++ ++ n = MIN(n, s->sector_next_status - sector_num); ++ if (s->status == BLK_DATA) { ++ n = MIN(n, s->buf_sectors); ++ } ++ ++ /* We need to write complete clusters for compressed images, so if an ++ * unallocated area is shorter than that, we must consider the whole ++ * cluster allocated. */ ++ if (s->compressed) { ++ if (n < s->cluster_sectors) { ++ n = MIN(s->cluster_sectors, s->total_sectors - sector_num); ++ s->status = BLK_DATA; ++ } else { ++ n = QEMU_ALIGN_DOWN(n, s->cluster_sectors); ++ } ++ } ++ ++ return n; ++} ++ ++static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num, ++ int nb_sectors, uint8_t *buf) ++{ ++ uint64_t single_read_until = 0; ++ int n, ret; ++ ++ assert(nb_sectors <= s->buf_sectors); ++ while (nb_sectors > 0) { ++ BlockBackend *blk; ++ int src_cur; ++ int64_t bs_sectors, src_cur_offset; ++ uint64_t offset; ++ ++ /* In the case of compression with multiple source files, we can get a ++ * nb_sectors that spreads into the next part. So we must be able to ++ * read across multiple BDSes for one convert_read() call. */ ++ convert_select_part(s, sector_num, &src_cur, &src_cur_offset); ++ blk = s->src[src_cur]; ++ bs_sectors = s->src_sectors[src_cur]; ++ ++ offset = (sector_num - src_cur_offset) << BDRV_SECTOR_BITS; ++ ++ n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset)); ++ if (single_read_until > offset) { ++ n = 1; ++ } ++ ++ ret = blk_co_pread(blk, offset, n << BDRV_SECTOR_BITS, buf, 0); ++ if (ret < 0) { ++ if (s->salvage) { ++ if (n > 1) { ++ single_read_until = offset + (n << BDRV_SECTOR_BITS); ++ continue; ++ } else { ++ if (!s->quiet) { ++ warn_report("error while reading offset %" PRIu64 ++ ": %s", offset, strerror(-ret)); ++ } ++ memset(buf, 0, BDRV_SECTOR_SIZE); ++ } ++ } else { ++ return ret; ++ } ++ } ++ ++ sector_num += n; ++ nb_sectors -= n; ++ buf += n * BDRV_SECTOR_SIZE; ++ } ++ ++ return 0; ++} ++ ++ ++static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, ++ int nb_sectors, uint8_t *buf, ++ enum ImgConvertBlockStatus status) ++{ ++ int ret; ++ ++ while (nb_sectors > 0) { ++ int n = nb_sectors; ++ BdrvRequestFlags flags = s->compressed ? BDRV_REQ_WRITE_COMPRESSED : 0; ++ ++ switch (status) { ++ case BLK_BACKING_FILE: ++ /* If we have a backing file, leave clusters unallocated that are ++ * unallocated in the source image, so that the backing file is ++ * visible at the respective offset. */ ++ assert(s->target_has_backing); ++ break; ++ ++ case BLK_DATA: ++ /* If we're told to keep the target fully allocated (-S 0) or there ++ * is real non-zero data, we must write it. Otherwise we can treat ++ * it as zero sectors. ++ * Compressed clusters need to be written as a whole, so in that ++ * case we can only save the write if the buffer is completely ++ * zeroed. */ ++ if (!s->min_sparse || ++ (!s->compressed && ++ is_allocated_sectors_min(buf, n, &n, s->min_sparse, ++ sector_num, s->alignment)) || ++ (s->compressed && ++ !buffer_is_zero(buf, n * BDRV_SECTOR_SIZE))) ++ { ++ ret = blk_co_pwrite(s->target, sector_num << BDRV_SECTOR_BITS, ++ n << BDRV_SECTOR_BITS, buf, flags); ++ if (ret < 0) { ++ return ret; ++ } ++ break; ++ } ++ /* fall-through */ ++ ++ case BLK_ZERO: ++ if (s->has_zero_init) { ++ assert(!s->target_has_backing); ++ break; ++ } ++ ret = blk_co_pwrite_zeroes(s->target, ++ sector_num << BDRV_SECTOR_BITS, ++ n << BDRV_SECTOR_BITS, ++ BDRV_REQ_MAY_UNMAP); ++ if (ret < 0) { ++ return ret; ++ } ++ break; ++ } ++ ++ sector_num += n; ++ nb_sectors -= n; ++ buf += n * BDRV_SECTOR_SIZE; ++ } ++ ++ return 0; ++} ++ ++static int coroutine_fn convert_co_copy_range(ImgConvertState *s, int64_t sector_num, ++ int nb_sectors) ++{ ++ int n, ret; ++ ++ while (nb_sectors > 0) { ++ BlockBackend *blk; ++ int src_cur; ++ int64_t bs_sectors, src_cur_offset; ++ int64_t offset; ++ ++ convert_select_part(s, sector_num, &src_cur, &src_cur_offset); ++ offset = (sector_num - src_cur_offset) << BDRV_SECTOR_BITS; ++ blk = s->src[src_cur]; ++ bs_sectors = s->src_sectors[src_cur]; ++ ++ n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset)); ++ ++ ret = blk_co_copy_range(blk, offset, s->target, ++ sector_num << BDRV_SECTOR_BITS, ++ n << BDRV_SECTOR_BITS, 0, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ sector_num += n; ++ nb_sectors -= n; ++ } ++ return 0; ++} ++ ++static void coroutine_fn convert_co_do_copy(void *opaque) ++{ ++ ImgConvertState *s = opaque; ++ uint8_t *buf = NULL; ++ int ret, i; ++ int index = -1; ++ ++ for (i = 0; i < s->num_coroutines; i++) { ++ if (s->co[i] == qemu_coroutine_self()) { ++ index = i; ++ break; ++ } ++ } ++ assert(index >= 0); ++ ++ s->running_coroutines++; ++ buf = blk_blockalign(s->target, s->buf_sectors * BDRV_SECTOR_SIZE); ++ ++ while (1) { ++ int n; ++ int64_t sector_num; ++ enum ImgConvertBlockStatus status; ++ bool copy_range; ++ ++ qemu_co_mutex_lock(&s->lock); ++ if (s->ret != -EINPROGRESS || s->sector_num >= s->total_sectors) { ++ qemu_co_mutex_unlock(&s->lock); ++ break; ++ } ++ WITH_GRAPH_RDLOCK_GUARD() { ++ n = convert_iteration_sectors(s, s->sector_num); ++ } ++ if (n < 0) { ++ qemu_co_mutex_unlock(&s->lock); ++ s->ret = n; ++ break; ++ } ++ /* save current sector and allocation status to local variables */ ++ sector_num = s->sector_num; ++ status = s->status; ++ if (!s->min_sparse && s->status == BLK_ZERO) { ++ n = MIN(n, s->buf_sectors); ++ } ++ /* increment global sector counter so that other coroutines can ++ * already continue reading beyond this request */ ++ s->sector_num += n; ++ qemu_co_mutex_unlock(&s->lock); ++ ++ if (status == BLK_DATA || (!s->min_sparse && status == BLK_ZERO)) { ++ s->allocated_done += n; ++ qemu_progress_print(100.0 * s->allocated_done / ++ s->allocated_sectors, 0); ++ } ++ ++retry: ++ copy_range = s->copy_range && s->status == BLK_DATA; ++ if (status == BLK_DATA && !copy_range) { ++ ret = convert_co_read(s, sector_num, n, buf); ++ if (ret < 0) { ++ error_report("error while reading at byte %lld: %s", ++ sector_num * BDRV_SECTOR_SIZE, strerror(-ret)); ++ s->ret = ret; ++ } ++ } else if (!s->min_sparse && status == BLK_ZERO) { ++ status = BLK_DATA; ++ memset(buf, 0x00, n * BDRV_SECTOR_SIZE); ++ } ++ ++ if (s->wr_in_order) { ++ /* keep writes in order */ ++ while (s->wr_offs != sector_num && s->ret == -EINPROGRESS) { ++ s->wait_sector_num[index] = sector_num; ++ qemu_coroutine_yield(); ++ } ++ s->wait_sector_num[index] = -1; ++ } ++ ++ if (s->ret == -EINPROGRESS) { ++ if (copy_range) { ++ WITH_GRAPH_RDLOCK_GUARD() { ++ ret = convert_co_copy_range(s, sector_num, n); ++ } ++ if (ret) { ++ s->copy_range = false; ++ goto retry; ++ } ++ } else { ++ ret = convert_co_write(s, sector_num, n, buf, status); ++ } ++ if (ret < 0) { ++ error_report("error while writing at byte %lld: %s", ++ sector_num * BDRV_SECTOR_SIZE, strerror(-ret)); ++ s->ret = ret; ++ } ++ } ++ ++ if (s->wr_in_order) { ++ /* reenter the coroutine that might have waited ++ * for this write to complete */ ++ s->wr_offs = sector_num + n; ++ for (i = 0; i < s->num_coroutines; i++) { ++ if (s->co[i] && s->wait_sector_num[i] == s->wr_offs) { ++ /* ++ * A -> B -> A cannot occur because A has ++ * s->wait_sector_num[i] == -1 during A -> B. Therefore ++ * B will never enter A during this time window. ++ */ ++ qemu_coroutine_enter(s->co[i]); ++ break; ++ } ++ } ++ } ++ } ++ ++ qemu_vfree(buf); ++ s->co[index] = NULL; ++ s->running_coroutines--; ++ if (!s->running_coroutines && s->ret == -EINPROGRESS) { ++ /* the convert job finished successfully */ ++ s->ret = 0; ++ } ++} ++ ++static int convert_do_copy(ImgConvertState *s) ++{ ++ int ret, i, n; ++ int64_t sector_num = 0; ++ ++ /* Check whether we have zero initialisation or can get it efficiently */ ++ if (!s->has_zero_init && s->target_is_new && s->min_sparse && ++ !s->target_has_backing) { ++ bdrv_graph_rdlock_main_loop(); ++ s->has_zero_init = bdrv_has_zero_init(blk_bs(s->target)); ++ bdrv_graph_rdunlock_main_loop(); ++ } ++ ++ /* Allocate buffer for copied data. For compressed images, only one cluster ++ * can be copied at a time. */ ++ if (s->compressed) { ++ if (s->cluster_sectors <= 0 || s->cluster_sectors > s->buf_sectors) { ++ error_report("invalid cluster size"); ++ return -EINVAL; ++ } ++ s->buf_sectors = s->cluster_sectors; ++ } ++ ++ while (sector_num < s->total_sectors) { ++ bdrv_graph_rdlock_main_loop(); ++ n = convert_iteration_sectors(s, sector_num); ++ bdrv_graph_rdunlock_main_loop(); ++ if (n < 0) { ++ return n; ++ } ++ if (s->status == BLK_DATA || (!s->min_sparse && s->status == BLK_ZERO)) ++ { ++ s->allocated_sectors += n; ++ } ++ sector_num += n; ++ } ++ ++ /* Do the copy */ ++ s->sector_next_status = 0; ++ s->ret = -EINPROGRESS; ++ ++ qemu_co_mutex_init(&s->lock); ++ for (i = 0; i < s->num_coroutines; i++) { ++ s->co[i] = qemu_coroutine_create(convert_co_do_copy, s); ++ s->wait_sector_num[i] = -1; ++ qemu_coroutine_enter(s->co[i]); ++ } ++ ++ while (s->running_coroutines) { ++ main_loop_wait(false); ++ } ++ ++ if (s->compressed && !s->ret) { ++ /* signal EOF to align */ ++ ret = blk_pwrite_compressed(s->target, 0, 0, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return s->ret; ++} ++ ++/* Check that bitmaps can be copied, or output an error */ ++static int convert_check_bitmaps(BlockDriverState *src, bool skip_broken) ++{ ++ BdrvDirtyBitmap *bm; ++ ++ if (!bdrv_supports_persistent_dirty_bitmap(src)) { ++ error_report("Source lacks bitmap support"); ++ return -1; ++ } ++ FOR_EACH_DIRTY_BITMAP(src, bm) { ++ if (!bdrv_dirty_bitmap_get_persistence(bm)) { ++ continue; ++ } ++ if (!skip_broken && bdrv_dirty_bitmap_inconsistent(bm)) { ++ error_report("Cannot copy inconsistent bitmap '%s'", ++ bdrv_dirty_bitmap_name(bm)); ++ error_printf("Try --skip-broken-bitmaps, or " ++ "use 'qemu-img bitmap --remove' to delete it\n"); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst, ++ bool skip_broken) ++{ ++ BdrvDirtyBitmap *bm; ++ Error *err = NULL; ++ ++ FOR_EACH_DIRTY_BITMAP(src, bm) { ++ const char *name; ++ ++ if (!bdrv_dirty_bitmap_get_persistence(bm)) { ++ continue; ++ } ++ name = bdrv_dirty_bitmap_name(bm); ++ if (skip_broken && bdrv_dirty_bitmap_inconsistent(bm)) { ++ warn_report("Skipping inconsistent bitmap '%s'", name); ++ continue; ++ } ++ qmp_block_dirty_bitmap_add(dst->node_name, name, ++ true, bdrv_dirty_bitmap_granularity(bm), ++ true, true, ++ true, !bdrv_dirty_bitmap_enabled(bm), ++ &err); ++ if (err) { ++ error_reportf_err(err, "Failed to create bitmap %s: ", name); ++ return -1; ++ } ++ ++ do_dirty_bitmap_merge(dst->node_name, name, src->node_name, name, ++ &err); ++ if (err) { ++ error_reportf_err(err, "Failed to populate bitmap %s: ", name); ++ qmp_block_dirty_bitmap_remove(dst->node_name, name, NULL); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++#define MAX_BUF_SECTORS 32768 ++ ++static void set_rate_limit(BlockBackend *blk, int64_t rate_limit) ++{ ++ ThrottleConfig cfg; ++ ++ throttle_config_init(&cfg); ++ cfg.buckets[THROTTLE_BPS_WRITE].avg = rate_limit; ++ ++ blk_io_limits_enable(blk, CONVERT_THROTTLE_GROUP); ++ blk_set_io_limits(blk, &cfg); ++} ++ ++static int img_convert(int argc, char **argv) ++{ ++ int c, bs_i, flags, src_flags = BDRV_O_NO_SHARE; ++ const char *fmt = NULL, *out_fmt = NULL, *cache = "unsafe", ++ *src_cache = BDRV_DEFAULT_CACHE, *out_baseimg = NULL, ++ *out_filename, *out_baseimg_param, *snapshot_name = NULL, ++ *backing_fmt = NULL; ++ BlockDriver *drv = NULL, *proto_drv = NULL; ++ BlockDriverInfo bdi; ++ BlockDriverState *out_bs; ++ QemuOpts *opts = NULL, *sn_opts = NULL; ++ QemuOptsList *create_opts = NULL; ++ QDict *open_opts = NULL; ++ char *options = NULL; ++ Error *local_err = NULL; ++ bool writethrough, src_writethrough, image_opts = false, ++ skip_create = false, progress = false, tgt_image_opts = false; ++ int64_t ret = -EINVAL; ++ bool force_share = false; ++ bool explict_min_sparse = false; ++ bool bitmaps = false; ++ bool skip_broken = false; ++ int64_t rate_limit = 0; ++ ++ ImgConvertState s = (ImgConvertState) { ++ /* Need at least 4k of zeros for sparse detection */ ++ .min_sparse = 8, ++ .copy_range = false, ++ .buf_sectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE, ++ .wr_in_order = true, ++ .num_coroutines = 8, ++ }; ++ ++ for(;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, ++ {"salvage", no_argument, 0, OPTION_SALVAGE}, ++ {"target-is-zero", no_argument, 0, OPTION_TARGET_IS_ZERO}, ++ {"bitmaps", no_argument, 0, OPTION_BITMAPS}, ++ {"skip-broken-bitmaps", no_argument, 0, OPTION_SKIP_BROKEN}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":hf:O:B:CcF:o:l:S:pt:T:qnm:WUr:", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'O': ++ out_fmt = optarg; ++ break; ++ case 'B': ++ out_baseimg = optarg; ++ break; ++ case 'C': ++ s.copy_range = true; ++ break; ++ case 'c': ++ s.compressed = true; ++ break; ++ case 'F': ++ backing_fmt = optarg; ++ break; ++ case 'o': ++ if (accumulate_options(&options, optarg) < 0) { ++ goto fail_getopt; ++ } ++ break; ++ case 'l': ++ if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { ++ sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts, ++ optarg, false); ++ if (!sn_opts) { ++ error_report("Failed in parsing snapshot param '%s'", ++ optarg); ++ goto fail_getopt; ++ } ++ } else { ++ snapshot_name = optarg; ++ } ++ break; ++ case 'S': ++ { ++ int64_t sval; ++ ++ sval = cvtnum("buffer size for sparse output", optarg); ++ if (sval < 0) { ++ goto fail_getopt; ++ } else if (!QEMU_IS_ALIGNED(sval, BDRV_SECTOR_SIZE) || ++ sval / BDRV_SECTOR_SIZE > MAX_BUF_SECTORS) { ++ error_report("Invalid buffer size for sparse output specified. " ++ "Valid sizes are multiples of %llu up to %llu. Select " ++ "0 to disable sparse detection (fully allocates output).", ++ BDRV_SECTOR_SIZE, MAX_BUF_SECTORS * BDRV_SECTOR_SIZE); ++ goto fail_getopt; ++ } ++ ++ s.min_sparse = sval / BDRV_SECTOR_SIZE; ++ explict_min_sparse = true; ++ break; ++ } ++ case 'p': ++ progress = true; ++ break; ++ case 't': ++ cache = optarg; ++ break; ++ case 'T': ++ src_cache = optarg; ++ break; ++ case 'q': ++ s.quiet = true; ++ break; ++ case 'n': ++ skip_create = true; ++ break; ++ case 'm': ++ if (qemu_strtol(optarg, NULL, 0, &s.num_coroutines) || ++ s.num_coroutines < 1 || s.num_coroutines > MAX_COROUTINES) { ++ error_report("Invalid number of coroutines. Allowed number of" ++ " coroutines is between 1 and %d", MAX_COROUTINES); ++ goto fail_getopt; ++ } ++ break; ++ case 'W': ++ s.wr_in_order = false; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case 'r': ++ rate_limit = cvtnum("rate limit", optarg); ++ if (rate_limit < 0) { ++ goto fail_getopt; ++ } ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ case OPTION_SALVAGE: ++ s.salvage = true; ++ break; ++ case OPTION_TARGET_IMAGE_OPTS: ++ tgt_image_opts = true; ++ break; ++ case OPTION_TARGET_IS_ZERO: ++ /* ++ * The user asserting that the target is blank has the ++ * same effect as the target driver supporting zero ++ * initialisation. ++ */ ++ s.has_zero_init = true; ++ break; ++ case OPTION_BITMAPS: ++ bitmaps = true; ++ break; ++ case OPTION_SKIP_BROKEN: ++ skip_broken = true; ++ break; ++ } ++ } ++ ++ if (!out_fmt && !tgt_image_opts) { ++ out_fmt = "raw"; ++ } ++ ++ if (skip_broken && !bitmaps) { ++ error_report("Use of --skip-broken-bitmaps requires --bitmaps"); ++ goto fail_getopt; ++ } ++ ++ if (s.compressed && s.copy_range) { ++ error_report("Cannot enable copy offloading when -c is used"); ++ goto fail_getopt; ++ } ++ ++ if (explict_min_sparse && s.copy_range) { ++ error_report("Cannot enable copy offloading when -S is used"); ++ goto fail_getopt; ++ } ++ ++ if (s.copy_range && s.salvage) { ++ error_report("Cannot use copy offloading in salvaging mode"); ++ goto fail_getopt; ++ } ++ ++ if (tgt_image_opts && !skip_create) { ++ error_report("--target-image-opts requires use of -n flag"); ++ goto fail_getopt; ++ } ++ ++ if (skip_create && options) { ++ error_report("-o has no effect when skipping image creation"); ++ goto fail_getopt; ++ } ++ ++ if (s.has_zero_init && !skip_create) { ++ error_report("--target-is-zero requires use of -n flag"); ++ goto fail_getopt; ++ } ++ ++ s.src_num = argc - optind - 1; ++ out_filename = s.src_num >= 1 ? argv[argc - 1] : NULL; ++ ++ if (options && has_help_option(options)) { ++ if (out_fmt) { ++ ret = print_block_option_help(out_filename, out_fmt); ++ goto fail_getopt; ++ } else { ++ error_report("Option help requires a format be specified"); ++ goto fail_getopt; ++ } ++ } ++ ++ if (s.src_num < 1) { ++ error_report("Must specify image file name"); ++ goto fail_getopt; ++ } ++ ++ /* ret is still -EINVAL until here */ ++ ret = bdrv_parse_cache_mode(src_cache, &src_flags, &src_writethrough); ++ if (ret < 0) { ++ error_report("Invalid source cache option: %s", src_cache); ++ goto fail_getopt; ++ } ++ ++ /* Initialize before goto out */ ++ if (s.quiet) { ++ progress = false; ++ } ++ qemu_progress_init(progress, 1.0); ++ qemu_progress_print(0, 100); ++ ++ s.src = g_new0(BlockBackend *, s.src_num); ++ s.src_sectors = g_new(int64_t, s.src_num); ++ s.src_alignment = g_new(int, s.src_num); ++ ++ for (bs_i = 0; bs_i < s.src_num; bs_i++) { ++ BlockDriverState *src_bs; ++ s.src[bs_i] = img_open(image_opts, argv[optind + bs_i], ++ fmt, src_flags, src_writethrough, s.quiet, ++ force_share); ++ if (!s.src[bs_i]) { ++ ret = -1; ++ goto out; ++ } ++ s.src_sectors[bs_i] = blk_nb_sectors(s.src[bs_i]); ++ if (s.src_sectors[bs_i] < 0) { ++ error_report("Could not get size of %s: %s", ++ argv[optind + bs_i], strerror(-s.src_sectors[bs_i])); ++ ret = -1; ++ goto out; ++ } ++ src_bs = blk_bs(s.src[bs_i]); ++ s.src_alignment[bs_i] = DIV_ROUND_UP(src_bs->bl.request_alignment, ++ BDRV_SECTOR_SIZE); ++ if (!bdrv_get_info(src_bs, &bdi)) { ++ s.src_alignment[bs_i] = MAX(s.src_alignment[bs_i], ++ bdi.cluster_size / BDRV_SECTOR_SIZE); ++ } ++ s.total_sectors += s.src_sectors[bs_i]; ++ } ++ ++ if (sn_opts) { ++ bdrv_snapshot_load_tmp(blk_bs(s.src[0]), ++ qemu_opt_get(sn_opts, SNAPSHOT_OPT_ID), ++ qemu_opt_get(sn_opts, SNAPSHOT_OPT_NAME), ++ &local_err); ++ } else if (snapshot_name != NULL) { ++ if (s.src_num > 1) { ++ error_report("No support for concatenating multiple snapshot"); ++ ret = -1; ++ goto out; ++ } ++ ++ bdrv_snapshot_load_tmp_by_id_or_name(blk_bs(s.src[0]), snapshot_name, ++ &local_err); ++ } ++ if (local_err) { ++ error_reportf_err(local_err, "Failed to load snapshot: "); ++ ret = -1; ++ goto out; ++ } ++ ++ if (!skip_create) { ++ /* Find driver and parse its options */ ++ drv = bdrv_find_format(out_fmt); ++ if (!drv) { ++ error_report("Unknown file format '%s'", out_fmt); ++ ret = -1; ++ goto out; ++ } ++ ++ proto_drv = bdrv_find_protocol(out_filename, true, &local_err); ++ if (!proto_drv) { ++ error_report_err(local_err); ++ ret = -1; ++ goto out; ++ } ++ ++ if (!drv->create_opts) { ++ error_report("Format driver '%s' does not support image creation", ++ drv->format_name); ++ ret = -1; ++ goto out; ++ } ++ ++ if (!proto_drv->create_opts) { ++ error_report("Protocol driver '%s' does not support image creation", ++ proto_drv->format_name); ++ ret = -1; ++ goto out; ++ } ++ ++ create_opts = qemu_opts_append(create_opts, drv->create_opts); ++ create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); ++ ++ opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); ++ if (options) { ++ if (!qemu_opts_do_parse(opts, options, NULL, &local_err)) { ++ error_report_err(local_err); ++ ret = -1; ++ goto out; ++ } ++ } ++ ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, ++ s.total_sectors * BDRV_SECTOR_SIZE, &error_abort); ++ ret = add_old_style_options(out_fmt, opts, out_baseimg, backing_fmt); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ ++ /* Get backing file name if -o backing_file was used */ ++ out_baseimg_param = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); ++ if (out_baseimg_param) { ++ out_baseimg = out_baseimg_param; ++ } ++ s.target_has_backing = (bool) out_baseimg; ++ ++ if (s.has_zero_init && s.target_has_backing) { ++ error_report("Cannot use --target-is-zero when the destination " ++ "image has a backing file"); ++ goto out; ++ } ++ ++ if (s.src_num > 1 && out_baseimg) { ++ error_report("Having a backing file for the target makes no sense when " ++ "concatenating multiple input images"); ++ ret = -1; ++ goto out; ++ } ++ ++ if (out_baseimg_param) { ++ if (!qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT)) { ++ error_report("Use of backing file requires explicit " ++ "backing format"); ++ ret = -1; ++ goto out; ++ } ++ } ++ ++ /* Check if compression is supported */ ++ if (s.compressed) { ++ bool encryption = ++ qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT, false); ++ const char *encryptfmt = ++ qemu_opt_get(opts, BLOCK_OPT_ENCRYPT_FORMAT); ++ const char *preallocation = ++ qemu_opt_get(opts, BLOCK_OPT_PREALLOC); ++ ++ if (drv && !block_driver_can_compress(drv)) { ++ error_report("Compression not supported for this file format"); ++ ret = -1; ++ goto out; ++ } ++ ++ if (encryption || encryptfmt) { ++ error_report("Compression and encryption not supported at " ++ "the same time"); ++ ret = -1; ++ goto out; ++ } ++ ++ if (preallocation ++ && strcmp(preallocation, "off")) ++ { ++ error_report("Compression and preallocation not supported at " ++ "the same time"); ++ ret = -1; ++ goto out; ++ } ++ } ++ ++ /* Determine if bitmaps need copying */ ++ if (bitmaps) { ++ if (s.src_num > 1) { ++ error_report("Copying bitmaps only possible with single source"); ++ ret = -1; ++ goto out; ++ } ++ ret = convert_check_bitmaps(blk_bs(s.src[0]), skip_broken); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ ++ /* ++ * The later open call will need any decryption secrets, and ++ * bdrv_create() will purge "opts", so extract them now before ++ * they are lost. ++ */ ++ if (!skip_create) { ++ open_opts = qdict_new(); ++ qemu_opt_foreach(opts, img_add_key_secrets, open_opts, &error_abort); ++ ++ /* Create the new image */ ++ ret = bdrv_create(drv, out_filename, opts, &local_err); ++ if (ret < 0) { ++ error_reportf_err(local_err, "%s: error while converting %s: ", ++ out_filename, out_fmt); ++ goto out; ++ } ++ } ++ ++ s.target_is_new = !skip_create; ++ ++ flags = s.min_sparse ? (BDRV_O_RDWR | BDRV_O_UNMAP) : BDRV_O_RDWR; ++ ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid cache option: %s", cache); ++ goto out; ++ } ++ ++ if (flags & BDRV_O_NOCACHE) { ++ /* ++ * If we open the target with O_DIRECT, it may be necessary to ++ * extend its size to align to the physical sector size. ++ */ ++ flags |= BDRV_O_RESIZE; ++ } ++ ++ if (skip_create) { ++ s.target = img_open(tgt_image_opts, out_filename, out_fmt, ++ flags, writethrough, s.quiet, false); ++ } else { ++ /* TODO ultimately we should allow --target-image-opts ++ * to be used even when -n is not given. ++ * That has to wait for bdrv_create to be improved ++ * to allow filenames in option syntax ++ */ ++ s.target = img_open_file(out_filename, open_opts, out_fmt, ++ flags, writethrough, s.quiet, false); ++ open_opts = NULL; /* blk_new_open will have freed it */ ++ } ++ if (!s.target) { ++ ret = -1; ++ goto out; ++ } ++ out_bs = blk_bs(s.target); ++ ++ if (bitmaps && !bdrv_supports_persistent_dirty_bitmap(out_bs)) { ++ error_report("Format driver '%s' does not support bitmaps", ++ out_bs->drv->format_name); ++ ret = -1; ++ goto out; ++ } ++ ++ if (s.compressed && !block_driver_can_compress(out_bs->drv)) { ++ error_report("Compression not supported for this file format"); ++ ret = -1; ++ goto out; ++ } ++ ++ /* increase bufsectors from the default 4096 (2M) if opt_transfer ++ * or discard_alignment of the out_bs is greater. Limit to ++ * MAX_BUF_SECTORS as maximum which is currently 32768 (16MB). */ ++ s.buf_sectors = MIN(MAX_BUF_SECTORS, ++ MAX(s.buf_sectors, ++ MAX(out_bs->bl.opt_transfer >> BDRV_SECTOR_BITS, ++ out_bs->bl.pdiscard_alignment >> ++ BDRV_SECTOR_BITS))); ++ ++ /* try to align the write requests to the destination to avoid unnecessary ++ * RMW cycles. */ ++ s.alignment = MAX(pow2floor(s.min_sparse), ++ DIV_ROUND_UP(out_bs->bl.request_alignment, ++ BDRV_SECTOR_SIZE)); ++ assert(is_power_of_2(s.alignment)); ++ ++ if (skip_create) { ++ int64_t output_sectors = blk_nb_sectors(s.target); ++ if (output_sectors < 0) { ++ error_report("unable to get output image length: %s", ++ strerror(-output_sectors)); ++ ret = -1; ++ goto out; ++ } else if (output_sectors < s.total_sectors) { ++ error_report("output file is smaller than input file"); ++ ret = -1; ++ goto out; ++ } ++ } ++ ++ if (s.target_has_backing && s.target_is_new) { ++ /* Errors are treated as "backing length unknown" (which means ++ * s.target_backing_sectors has to be negative, which it will ++ * be automatically). The backing file length is used only ++ * for optimizations, so such a case is not fatal. */ ++ bdrv_graph_rdlock_main_loop(); ++ s.target_backing_sectors = ++ bdrv_nb_sectors(bdrv_backing_chain_next(out_bs)); ++ bdrv_graph_rdunlock_main_loop(); ++ } else { ++ s.target_backing_sectors = -1; ++ } ++ ++ ret = bdrv_get_info(out_bs, &bdi); ++ if (ret < 0) { ++ if (s.compressed) { ++ error_report("could not get block driver info"); ++ goto out; ++ } ++ } else { ++ s.compressed = s.compressed || bdi.needs_compressed_writes; ++ s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE; ++ } ++ ++ if (rate_limit) { ++ set_rate_limit(s.target, rate_limit); ++ } ++ ++ ret = convert_do_copy(&s); ++ ++ /* Now copy the bitmaps */ ++ if (bitmaps && ret == 0) { ++ ret = convert_copy_bitmaps(blk_bs(s.src[0]), out_bs, skip_broken); ++ } ++ ++out: ++ if (!ret) { ++ qemu_progress_print(100, 0); ++ } ++ qemu_progress_end(); ++ qemu_opts_del(opts); ++ qemu_opts_free(create_opts); ++ qobject_unref(open_opts); ++ blk_unref(s.target); ++ if (s.src) { ++ for (bs_i = 0; bs_i < s.src_num; bs_i++) { ++ blk_unref(s.src[bs_i]); ++ } ++ g_free(s.src); ++ } ++ g_free(s.src_sectors); ++ g_free(s.src_alignment); ++fail_getopt: ++ qemu_opts_del(sn_opts); ++ g_free(options); ++ ++ return !!ret; ++} ++ ++ ++static void dump_snapshots(BlockDriverState *bs) ++{ ++ QEMUSnapshotInfo *sn_tab, *sn; ++ int nb_sns, i; ++ ++ nb_sns = bdrv_snapshot_list(bs, &sn_tab); ++ if (nb_sns <= 0) ++ return; ++ printf("Snapshot list:\n"); ++ bdrv_snapshot_dump(NULL); ++ printf("\n"); ++ for(i = 0; i < nb_sns; i++) { ++ sn = &sn_tab[i]; ++ bdrv_snapshot_dump(sn); ++ printf("\n"); ++ } ++ g_free(sn_tab); ++} ++ ++static void dump_json_block_graph_info_list(BlockGraphInfoList *list) ++{ ++ GString *str; ++ QObject *obj; ++ Visitor *v = qobject_output_visitor_new(&obj); ++ ++ visit_type_BlockGraphInfoList(v, NULL, &list, &error_abort); ++ visit_complete(v, &obj); ++ str = qobject_to_json_pretty(obj, true); ++ assert(str != NULL); ++ printf("%s\n", str->str); ++ qobject_unref(obj); ++ visit_free(v); ++ g_string_free(str, true); ++} ++ ++static void dump_json_block_graph_info(BlockGraphInfo *info) ++{ ++ GString *str; ++ QObject *obj; ++ Visitor *v = qobject_output_visitor_new(&obj); ++ ++ visit_type_BlockGraphInfo(v, NULL, &info, &error_abort); ++ visit_complete(v, &obj); ++ str = qobject_to_json_pretty(obj, true); ++ assert(str != NULL); ++ printf("%s\n", str->str); ++ qobject_unref(obj); ++ visit_free(v); ++ g_string_free(str, true); ++} ++ ++static void dump_human_image_info(BlockGraphInfo *info, int indentation, ++ const char *path) ++{ ++ BlockChildInfoList *children_list; ++ ++ bdrv_node_info_dump(qapi_BlockGraphInfo_base(info), indentation, ++ info->children == NULL); ++ ++ for (children_list = info->children; children_list; ++ children_list = children_list->next) ++ { ++ BlockChildInfo *child = children_list->value; ++ g_autofree char *child_path = NULL; ++ ++ printf("%*sChild node '%s%s':\n", ++ indentation * 4, "", path, child->name); ++ child_path = g_strdup_printf("%s%s/", path, child->name); ++ dump_human_image_info(child->info, indentation + 1, child_path); ++ } ++} ++ ++static void dump_human_image_info_list(BlockGraphInfoList *list) ++{ ++ BlockGraphInfoList *elem; ++ bool delim = false; ++ ++ for (elem = list; elem; elem = elem->next) { ++ if (delim) { ++ printf("\n"); ++ } ++ delim = true; ++ ++ dump_human_image_info(elem->value, 0, "/"); ++ } ++} ++ ++static gboolean str_equal_func(gconstpointer a, gconstpointer b) ++{ ++ return strcmp(a, b) == 0; ++} ++ ++/** ++ * Open an image file chain and return an BlockGraphInfoList ++ * ++ * @filename: topmost image filename ++ * @fmt: topmost image format (may be NULL to autodetect) ++ * @chain: true - enumerate entire backing file chain ++ * false - only topmost image file ++ * ++ * Returns a list of BlockNodeInfo objects or NULL if there was an error ++ * opening an image file. If there was an error a message will have been ++ * printed to stderr. ++ */ ++static BlockGraphInfoList *collect_image_info_list(bool image_opts, ++ const char *filename, ++ const char *fmt, ++ bool chain, bool force_share) ++{ ++ BlockGraphInfoList *head = NULL; ++ BlockGraphInfoList **tail = &head; ++ GHashTable *filenames; ++ Error *err = NULL; ++ ++ filenames = g_hash_table_new_full(g_str_hash, str_equal_func, NULL, NULL); ++ ++ while (filename) { ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ BlockGraphInfo *info; ++ ++ if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) { ++ error_report("Backing file '%s' creates an infinite loop.", ++ filename); ++ goto err; ++ } ++ g_hash_table_insert(filenames, (gpointer)filename, NULL); ++ ++ blk = img_open(image_opts, filename, fmt, ++ BDRV_O_NO_BACKING | BDRV_O_NO_IO, false, false, ++ force_share); ++ if (!blk) { ++ goto err; ++ } ++ bs = blk_bs(blk); ++ ++ /* ++ * Note that the returned BlockGraphInfo object will not have ++ * information about this image's backing node, because we have opened ++ * it with BDRV_O_NO_BACKING. Printing this object will therefore not ++ * duplicate the backing chain information that we obtain by walking ++ * the chain manually here. ++ */ ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_query_block_graph_info(bs, &info, &err); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (err) { ++ error_report_err(err); ++ blk_unref(blk); ++ goto err; ++ } ++ ++ QAPI_LIST_APPEND(tail, info); ++ ++ blk_unref(blk); ++ ++ /* Clear parameters that only apply to the topmost image */ ++ filename = fmt = NULL; ++ image_opts = false; ++ ++ if (chain) { ++ if (info->full_backing_filename) { ++ filename = info->full_backing_filename; ++ } else if (info->backing_filename) { ++ error_report("Could not determine absolute backing filename," ++ " but backing filename '%s' present", ++ info->backing_filename); ++ goto err; ++ } ++ if (info->backing_filename_format) { ++ fmt = info->backing_filename_format; ++ } ++ } ++ } ++ g_hash_table_destroy(filenames); ++ return head; ++ ++err: ++ qapi_free_BlockGraphInfoList(head); ++ g_hash_table_destroy(filenames); ++ return NULL; ++} ++ ++static int img_info(int argc, char **argv) ++{ ++ int c; ++ OutputFormat output_format = OFORMAT_HUMAN; ++ bool chain = false; ++ const char *filename, *fmt, *output; ++ BlockGraphInfoList *list; ++ bool image_opts = false; ++ bool force_share = false; ++ ++ fmt = NULL; ++ output = NULL; ++ for(;;) { ++ int option_index = 0; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"format", required_argument, 0, 'f'}, ++ {"output", required_argument, 0, OPTION_OUTPUT}, ++ {"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":f:hU", ++ long_options, &option_index); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OUTPUT: ++ output = optarg; ++ break; ++ case OPTION_BACKING_CHAIN: ++ chain = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ filename = argv[optind++]; ++ ++ if (output && !strcmp(output, "json")) { ++ output_format = OFORMAT_JSON; ++ } else if (output && !strcmp(output, "human")) { ++ output_format = OFORMAT_HUMAN; ++ } else if (output) { ++ error_report("--output must be used with human or json as argument."); ++ return 1; ++ } ++ ++ list = collect_image_info_list(image_opts, filename, fmt, chain, ++ force_share); ++ if (!list) { ++ return 1; ++ } ++ ++ switch (output_format) { ++ case OFORMAT_HUMAN: ++ dump_human_image_info_list(list); ++ break; ++ case OFORMAT_JSON: ++ if (chain) { ++ dump_json_block_graph_info_list(list); ++ } else { ++ dump_json_block_graph_info(list->value); ++ } ++ break; ++ } ++ ++ qapi_free_BlockGraphInfoList(list); ++ return 0; ++} ++ ++static int dump_map_entry(OutputFormat output_format, MapEntry *e, ++ MapEntry *next) ++{ ++ switch (output_format) { ++ case OFORMAT_HUMAN: ++ if (e->data && !e->has_offset) { ++ error_report("File contains external, encrypted or compressed clusters."); ++ return -1; ++ } ++ if (e->data && !e->zero) { ++ printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n", ++ e->start, e->length, ++ e->has_offset ? e->offset : 0, ++ e->filename ?: ""); ++ } ++ /* This format ignores the distinction between 0, ZERO and ZERO|DATA. ++ * Modify the flags here to allow more coalescing. ++ */ ++ if (next && (!next->data || next->zero)) { ++ next->data = false; ++ next->zero = true; ++ } ++ break; ++ case OFORMAT_JSON: ++ printf("{ \"start\": %"PRId64", \"length\": %"PRId64"," ++ " \"depth\": %"PRId64", \"present\": %s, \"zero\": %s," ++ " \"data\": %s, \"compressed\": %s", ++ e->start, e->length, e->depth, ++ e->present ? "true" : "false", ++ e->zero ? "true" : "false", ++ e->data ? "true" : "false", ++ e->compressed ? "true" : "false"); ++ if (e->has_offset) { ++ printf(", \"offset\": %"PRId64"", e->offset); ++ } ++ putchar('}'); ++ ++ if (next) { ++ puts(","); ++ } ++ break; ++ } ++ return 0; ++} ++ ++static int get_block_status(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, MapEntry *e) ++{ ++ int ret; ++ int depth; ++ BlockDriverState *file; ++ bool has_offset; ++ int64_t map; ++ char *filename = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ /* As an optimization, we could cache the current range of unallocated ++ * clusters in each file of the chain, and avoid querying the same ++ * range repeatedly. ++ */ ++ ++ depth = 0; ++ for (;;) { ++ bs = bdrv_skip_filters(bs); ++ ret = bdrv_block_status(bs, offset, bytes, &bytes, &map, &file); ++ if (ret < 0) { ++ return ret; ++ } ++ assert(bytes); ++ if (ret & (BDRV_BLOCK_ZERO|BDRV_BLOCK_DATA)) { ++ break; ++ } ++ bs = bdrv_cow_bs(bs); ++ if (bs == NULL) { ++ ret = 0; ++ break; ++ } ++ ++ depth++; ++ } ++ ++ has_offset = !!(ret & BDRV_BLOCK_OFFSET_VALID); ++ ++ if (file && has_offset) { ++ bdrv_refresh_filename(file); ++ filename = file->filename; ++ } ++ ++ *e = (MapEntry) { ++ .start = offset, ++ .length = bytes, ++ .data = !!(ret & BDRV_BLOCK_DATA), ++ .zero = !!(ret & BDRV_BLOCK_ZERO), ++ .compressed = !!(ret & BDRV_BLOCK_COMPRESSED), ++ .offset = map, ++ .has_offset = has_offset, ++ .depth = depth, ++ .present = !!(ret & BDRV_BLOCK_ALLOCATED), ++ .filename = filename, ++ }; ++ ++ return 0; ++} ++ ++static inline bool entry_mergeable(const MapEntry *curr, const MapEntry *next) ++{ ++ if (curr->length == 0) { ++ return false; ++ } ++ if (curr->zero != next->zero || ++ curr->data != next->data || ++ curr->compressed != next->compressed || ++ curr->depth != next->depth || ++ curr->present != next->present || ++ !curr->filename != !next->filename || ++ curr->has_offset != next->has_offset) { ++ return false; ++ } ++ if (curr->filename && strcmp(curr->filename, next->filename)) { ++ return false; ++ } ++ if (curr->has_offset && curr->offset + curr->length != next->offset) { ++ return false; ++ } ++ return true; ++} ++ ++static int img_map(int argc, char **argv) ++{ ++ int c; ++ OutputFormat output_format = OFORMAT_HUMAN; ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ const char *filename, *fmt, *output; ++ int64_t length; ++ MapEntry curr = { .length = 0 }, next; ++ int ret = 0; ++ bool image_opts = false; ++ bool force_share = false; ++ int64_t start_offset = 0; ++ int64_t max_length = -1; ++ ++ fmt = NULL; ++ output = NULL; ++ for (;;) { ++ int option_index = 0; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"format", required_argument, 0, 'f'}, ++ {"output", required_argument, 0, OPTION_OUTPUT}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {"start-offset", required_argument, 0, 's'}, ++ {"max-length", required_argument, 0, 'l'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":f:s:l:hU", ++ long_options, &option_index); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OUTPUT: ++ output = optarg; ++ break; ++ case 's': ++ start_offset = cvtnum("start offset", optarg); ++ if (start_offset < 0) { ++ return 1; ++ } ++ break; ++ case 'l': ++ max_length = cvtnum("max length", optarg); ++ if (max_length < 0) { ++ return 1; ++ } ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ filename = argv[optind]; ++ ++ if (output && !strcmp(output, "json")) { ++ output_format = OFORMAT_JSON; ++ } else if (output && !strcmp(output, "human")) { ++ output_format = OFORMAT_HUMAN; ++ } else if (output) { ++ error_report("--output must be used with human or json as argument."); ++ return 1; ++ } ++ ++ blk = img_open(image_opts, filename, fmt, 0, false, false, force_share); ++ if (!blk) { ++ return 1; ++ } ++ bs = blk_bs(blk); ++ ++ if (output_format == OFORMAT_HUMAN) { ++ printf("%-16s%-16s%-16s%s\n", "Offset", "Length", "Mapped to", "File"); ++ } else if (output_format == OFORMAT_JSON) { ++ putchar('['); ++ } ++ ++ length = blk_getlength(blk); ++ if (length < 0) { ++ error_report("Failed to get size for '%s'", filename); ++ return 1; ++ } ++ if (max_length != -1) { ++ length = MIN(start_offset + max_length, length); ++ } ++ ++ curr.start = start_offset; ++ while (curr.start + curr.length < length) { ++ int64_t offset = curr.start + curr.length; ++ int64_t n = length - offset; ++ ++ ret = get_block_status(bs, offset, n, &next); ++ if (ret < 0) { ++ error_report("Could not read file metadata: %s", strerror(-ret)); ++ goto out; ++ } ++ ++ if (entry_mergeable(&curr, &next)) { ++ curr.length += next.length; ++ continue; ++ } ++ ++ if (curr.length > 0) { ++ ret = dump_map_entry(output_format, &curr, &next); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ curr = next; ++ } ++ ++ ret = dump_map_entry(output_format, &curr, NULL); ++ if (output_format == OFORMAT_JSON) { ++ puts("]"); ++ } ++ ++out: ++ blk_unref(blk); ++ return ret < 0; ++} ++ ++#define SNAPSHOT_LIST 1 ++#define SNAPSHOT_CREATE 2 ++#define SNAPSHOT_APPLY 3 ++#define SNAPSHOT_DELETE 4 ++ ++static int img_snapshot(int argc, char **argv) ++{ ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ QEMUSnapshotInfo sn; ++ char *filename, *snapshot_name = NULL; ++ int c, ret = 0, bdrv_oflags; ++ int action = 0; ++ bool quiet = false; ++ Error *err = NULL; ++ bool image_opts = false; ++ bool force_share = false; ++ int64_t rt; ++ ++ bdrv_oflags = BDRV_O_RDWR; ++ /* Parse commandline parameters */ ++ for(;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":la:c:d:hqU", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ return 0; ++ case 'l': ++ if (action) { ++ error_exit("Cannot mix '-l', '-a', '-c', '-d'"); ++ return 0; ++ } ++ action = SNAPSHOT_LIST; ++ bdrv_oflags &= ~BDRV_O_RDWR; /* no need for RW */ ++ break; ++ case 'a': ++ if (action) { ++ error_exit("Cannot mix '-l', '-a', '-c', '-d'"); ++ return 0; ++ } ++ action = SNAPSHOT_APPLY; ++ snapshot_name = optarg; ++ break; ++ case 'c': ++ if (action) { ++ error_exit("Cannot mix '-l', '-a', '-c', '-d'"); ++ return 0; ++ } ++ action = SNAPSHOT_CREATE; ++ snapshot_name = optarg; ++ break; ++ case 'd': ++ if (action) { ++ error_exit("Cannot mix '-l', '-a', '-c', '-d'"); ++ return 0; ++ } ++ action = SNAPSHOT_DELETE; ++ snapshot_name = optarg; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ filename = argv[optind++]; ++ ++ /* Open the image */ ++ blk = img_open(image_opts, filename, NULL, bdrv_oflags, false, quiet, ++ force_share); ++ if (!blk) { ++ return 1; ++ } ++ bs = blk_bs(blk); ++ ++ /* Perform the requested action */ ++ switch(action) { ++ case SNAPSHOT_LIST: ++ dump_snapshots(bs); ++ break; ++ ++ case SNAPSHOT_CREATE: ++ memset(&sn, 0, sizeof(sn)); ++ pstrcpy(sn.name, sizeof(sn.name), snapshot_name); ++ ++ rt = g_get_real_time(); ++ sn.date_sec = rt / G_USEC_PER_SEC; ++ sn.date_nsec = (rt % G_USEC_PER_SEC) * 1000; ++ ++ bdrv_graph_rdlock_main_loop(); ++ ret = bdrv_snapshot_create(bs, &sn); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (ret) { ++ error_report("Could not create snapshot '%s': %s", ++ snapshot_name, strerror(-ret)); ++ } ++ break; ++ ++ case SNAPSHOT_APPLY: ++ ret = bdrv_snapshot_goto(bs, snapshot_name, &err); ++ if (ret) { ++ error_reportf_err(err, "Could not apply snapshot '%s': ", ++ snapshot_name); ++ } ++ break; ++ ++ case SNAPSHOT_DELETE: ++ bdrv_graph_rdlock_main_loop(); ++ ret = bdrv_snapshot_find(bs, &sn, snapshot_name); ++ if (ret < 0) { ++ error_report("Could not delete snapshot '%s': snapshot not " ++ "found", snapshot_name); ++ ret = 1; ++ } else { ++ ret = bdrv_snapshot_delete(bs, sn.id_str, sn.name, &err); ++ if (ret < 0) { ++ error_reportf_err(err, "Could not delete snapshot '%s': ", ++ snapshot_name); ++ ret = 1; ++ } ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ break; ++ } ++ ++ /* Cleanup */ ++ blk_unref(blk); ++ if (ret) { ++ return 1; ++ } ++ return 0; ++} ++ ++static int img_rebase(int argc, char **argv) ++{ ++ BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL; ++ uint8_t *buf_old = NULL; ++ uint8_t *buf_new = NULL; ++ BlockDriverState *bs = NULL, *prefix_chain_bs = NULL; ++ BlockDriverState *unfiltered_bs, *unfiltered_bs_cow; ++ BlockDriverInfo bdi = {0}; ++ char *filename; ++ const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; ++ int c, flags, src_flags, ret; ++ BdrvRequestFlags write_flags = 0; ++ bool writethrough, src_writethrough; ++ int unsafe = 0; ++ bool force_share = false; ++ int progress = 0; ++ bool quiet = false; ++ bool compress = false; ++ Error *local_err = NULL; ++ bool image_opts = false; ++ int64_t write_align; ++ ++ /* Parse commandline parameters */ ++ fmt = NULL; ++ cache = BDRV_DEFAULT_CACHE; ++ src_cache = BDRV_DEFAULT_CACHE; ++ out_baseimg = NULL; ++ out_basefmt = NULL; ++ for(;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {"compress", no_argument, 0, 'c'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":hf:F:b:upt:T:qUc", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ return 0; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'F': ++ out_basefmt = optarg; ++ break; ++ case 'b': ++ out_baseimg = optarg; ++ break; ++ case 'u': ++ unsafe = 1; ++ break; ++ case 'p': ++ progress = 1; ++ break; ++ case 't': ++ cache = optarg; ++ break; ++ case 'T': ++ src_cache = optarg; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case 'c': ++ compress = true; ++ break; ++ } ++ } ++ ++ if (quiet) { ++ progress = 0; ++ } ++ ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ if (!unsafe && !out_baseimg) { ++ error_exit("Must specify backing file (-b) or use unsafe mode (-u)"); ++ } ++ filename = argv[optind++]; ++ ++ qemu_progress_init(progress, 2.0); ++ qemu_progress_print(0, 100); ++ ++ flags = BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0); ++ ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid cache option: %s", cache); ++ goto out; ++ } ++ ++ src_flags = 0; ++ ret = bdrv_parse_cache_mode(src_cache, &src_flags, &src_writethrough); ++ if (ret < 0) { ++ error_report("Invalid source cache option: %s", src_cache); ++ goto out; ++ } ++ ++ /* The source files are opened read-only, don't care about WCE */ ++ assert((src_flags & BDRV_O_RDWR) == 0); ++ (void) src_writethrough; ++ ++ /* ++ * Open the images. ++ * ++ * Ignore the old backing file for unsafe rebase in case we want to correct ++ * the reference to a renamed or moved backing file. ++ */ ++ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet, ++ false); ++ if (!blk) { ++ ret = -1; ++ goto out; ++ } ++ bs = blk_bs(blk); ++ ++ bdrv_graph_rdlock_main_loop(); ++ unfiltered_bs = bdrv_skip_filters(bs); ++ unfiltered_bs_cow = bdrv_cow_bs(unfiltered_bs); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (compress && !block_driver_can_compress(unfiltered_bs->drv)) { ++ error_report("Compression not supported for this file format"); ++ ret = -1; ++ goto out; ++ } else if (compress) { ++ write_flags |= BDRV_REQ_WRITE_COMPRESSED; ++ } ++ ++ if (out_basefmt != NULL) { ++ if (bdrv_find_format(out_basefmt) == NULL) { ++ error_report("Invalid format name: '%s'", out_basefmt); ++ ret = -1; ++ goto out; ++ } ++ } ++ ++ /* ++ * We need overlay subcluster size (or cluster size in case writes are ++ * compressed) to make sure write requests are aligned. ++ */ ++ ret = bdrv_get_info(unfiltered_bs, &bdi); ++ if (ret < 0) { ++ error_report("could not get block driver info"); ++ goto out; ++ } else if (bdi.subcluster_size == 0) { ++ bdi.cluster_size = bdi.subcluster_size = 1; ++ } ++ ++ write_align = compress ? bdi.cluster_size : bdi.subcluster_size; ++ ++ /* For safe rebasing we need to compare old and new backing file */ ++ if (!unsafe) { ++ QDict *options = NULL; ++ BlockDriverState *base_bs; ++ ++ bdrv_graph_rdlock_main_loop(); ++ base_bs = bdrv_cow_bs(unfiltered_bs); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (base_bs) { ++ blk_old_backing = blk_new(qemu_get_aio_context(), ++ BLK_PERM_CONSISTENT_READ, ++ BLK_PERM_ALL); ++ ret = blk_insert_bs(blk_old_backing, base_bs, ++ &local_err); ++ if (ret < 0) { ++ error_reportf_err(local_err, ++ "Could not reuse old backing file '%s': ", ++ base_bs->filename); ++ goto out; ++ } ++ } else { ++ blk_old_backing = NULL; ++ } ++ ++ if (out_baseimg[0]) { ++ const char *overlay_filename; ++ char *out_real_path; ++ ++ options = qdict_new(); ++ if (out_basefmt) { ++ qdict_put_str(options, "driver", out_basefmt); ++ } ++ if (force_share) { ++ qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_refresh_filename(bs); ++ bdrv_graph_rdunlock_main_loop(); ++ overlay_filename = bs->exact_filename[0] ? bs->exact_filename ++ : bs->filename; ++ out_real_path = ++ bdrv_get_full_backing_filename_from_filename(overlay_filename, ++ out_baseimg, ++ &local_err); ++ if (local_err) { ++ qobject_unref(options); ++ error_reportf_err(local_err, ++ "Could not resolve backing filename: "); ++ ret = -1; ++ goto out; ++ } ++ ++ /* ++ * Find out whether we rebase an image on top of a previous image ++ * in its chain. ++ */ ++ prefix_chain_bs = bdrv_find_backing_image(bs, out_real_path); ++ if (prefix_chain_bs) { ++ qobject_unref(options); ++ g_free(out_real_path); ++ ++ blk_new_backing = blk_new(qemu_get_aio_context(), ++ BLK_PERM_CONSISTENT_READ, ++ BLK_PERM_ALL); ++ ret = blk_insert_bs(blk_new_backing, prefix_chain_bs, ++ &local_err); ++ if (ret < 0) { ++ error_reportf_err(local_err, ++ "Could not reuse backing file '%s': ", ++ out_baseimg); ++ goto out; ++ } ++ } else { ++ blk_new_backing = blk_new_open(out_real_path, NULL, ++ options, src_flags, &local_err); ++ g_free(out_real_path); ++ if (!blk_new_backing) { ++ error_reportf_err(local_err, ++ "Could not open new backing file '%s': ", ++ out_baseimg); ++ ret = -1; ++ goto out; ++ } ++ } ++ } ++ } ++ ++ /* ++ * Check each unallocated cluster in the COW file. If it is unallocated, ++ * accesses go to the backing file. We must therefore compare this cluster ++ * in the old and new backing file, and if they differ we need to copy it ++ * from the old backing file into the COW file. ++ * ++ * If qemu-img crashes during this step, no harm is done. The content of ++ * the image is the same as the original one at any time. ++ */ ++ if (!unsafe) { ++ int64_t size; ++ int64_t old_backing_size = 0; ++ int64_t new_backing_size = 0; ++ uint64_t offset; ++ int64_t n, n_old = 0, n_new = 0; ++ float local_progress = 0; ++ ++ if (blk_old_backing && bdrv_opt_mem_align(blk_bs(blk_old_backing)) > ++ bdrv_opt_mem_align(blk_bs(blk))) { ++ buf_old = blk_blockalign(blk_old_backing, IO_BUF_SIZE); ++ } else { ++ buf_old = blk_blockalign(blk, IO_BUF_SIZE); ++ } ++ buf_new = blk_blockalign(blk_new_backing, IO_BUF_SIZE); ++ ++ size = blk_getlength(blk); ++ if (size < 0) { ++ error_report("Could not get size of '%s': %s", ++ filename, strerror(-size)); ++ ret = -1; ++ goto out; ++ } ++ if (blk_old_backing) { ++ old_backing_size = blk_getlength(blk_old_backing); ++ if (old_backing_size < 0) { ++ char backing_name[PATH_MAX]; ++ ++ bdrv_get_backing_filename(bs, backing_name, ++ sizeof(backing_name)); ++ error_report("Could not get size of '%s': %s", ++ backing_name, strerror(-old_backing_size)); ++ ret = -1; ++ goto out; ++ } ++ } ++ if (blk_new_backing) { ++ new_backing_size = blk_getlength(blk_new_backing); ++ if (new_backing_size < 0) { ++ error_report("Could not get size of '%s': %s", ++ out_baseimg, strerror(-new_backing_size)); ++ ret = -1; ++ goto out; ++ } ++ } ++ ++ if (size != 0) { ++ local_progress = (float)100 / (size / MIN(size, IO_BUF_SIZE)); ++ } ++ ++ for (offset = 0; offset < size; offset += n) { ++ bool old_backing_eof = false; ++ int64_t n_alloc; ++ ++ /* How many bytes can we handle with the next read? */ ++ n = MIN(IO_BUF_SIZE, size - offset); ++ ++ /* If the cluster is allocated, we don't need to take action */ ++ ret = bdrv_is_allocated(unfiltered_bs, offset, n, &n); ++ if (ret < 0) { ++ error_report("error while reading image metadata: %s", ++ strerror(-ret)); ++ goto out; ++ } ++ if (ret) { ++ continue; ++ } ++ ++ if (prefix_chain_bs) { ++ uint64_t bytes = n; ++ ++ /* ++ * If cluster wasn't changed since prefix_chain, we don't need ++ * to take action ++ */ ++ ret = bdrv_is_allocated_above(unfiltered_bs_cow, ++ prefix_chain_bs, false, ++ offset, n, &n); ++ if (ret < 0) { ++ error_report("error while reading image metadata: %s", ++ strerror(-ret)); ++ goto out; ++ } ++ if (!ret && n) { ++ continue; ++ } ++ if (!n) { ++ /* ++ * If we've reached EOF of the old backing, it means that ++ * offsets beyond the old backing size were read as zeroes. ++ * Now we will need to explicitly zero the cluster in ++ * order to preserve that state after the rebase. ++ */ ++ n = bytes; ++ } ++ } ++ ++ /* ++ * At this point we know that the region [offset; offset + n) ++ * is unallocated within the target image. This region might be ++ * unaligned to the target image's (sub)cluster boundaries, as ++ * old backing may have smaller clusters (or have subclusters). ++ * We extend it to the aligned boundaries to avoid CoW on ++ * partial writes in blk_pwrite(), ++ */ ++ n += offset - QEMU_ALIGN_DOWN(offset, write_align); ++ offset = QEMU_ALIGN_DOWN(offset, write_align); ++ n += QEMU_ALIGN_UP(offset + n, write_align) - (offset + n); ++ n = MIN(n, size - offset); ++ assert(!bdrv_is_allocated(unfiltered_bs, offset, n, &n_alloc) && ++ n_alloc == n); ++ ++ /* ++ * Much like with the target image, we'll try to read as much ++ * of the old and new backings as we can. ++ */ ++ n_old = MIN(n, MAX(0, old_backing_size - (int64_t) offset)); ++ n_new = MIN(n, MAX(0, new_backing_size - (int64_t) offset)); ++ ++ /* ++ * Read old and new backing file and take into consideration that ++ * backing files may be smaller than the COW image. ++ */ ++ memset(buf_old + n_old, 0, n - n_old); ++ if (!n_old) { ++ old_backing_eof = true; ++ } else { ++ ret = blk_pread(blk_old_backing, offset, n_old, buf_old, 0); ++ if (ret < 0) { ++ error_report("error while reading from old backing file"); ++ goto out; ++ } ++ } ++ ++ memset(buf_new + n_new, 0, n - n_new); ++ if (n_new) { ++ ret = blk_pread(blk_new_backing, offset, n_new, buf_new, 0); ++ if (ret < 0) { ++ error_report("error while reading from new backing file"); ++ goto out; ++ } ++ } ++ ++ /* If they differ, we need to write to the COW file */ ++ uint64_t written = 0; ++ ++ while (written < n) { ++ int64_t pnum; ++ ++ if (compare_buffers(buf_old + written, buf_new + written, ++ n - written, write_align, &pnum)) ++ { ++ if (old_backing_eof) { ++ ret = blk_pwrite_zeroes(blk, offset + written, pnum, 0); ++ } else { ++ assert(written + pnum <= IO_BUF_SIZE); ++ ret = blk_pwrite(blk, offset + written, pnum, ++ buf_old + written, write_flags); ++ } ++ if (ret < 0) { ++ error_report("Error while writing to COW image: %s", ++ strerror(-ret)); ++ goto out; ++ } ++ } ++ ++ written += pnum; ++ if (offset + written >= old_backing_size) { ++ old_backing_eof = true; ++ } ++ } ++ qemu_progress_print(local_progress, 100); ++ } ++ } ++ ++ /* ++ * Change the backing file. All clusters that are different from the old ++ * backing file are overwritten in the COW file now, so the visible content ++ * doesn't change when we switch the backing file. ++ */ ++ if (out_baseimg && *out_baseimg) { ++ ret = bdrv_change_backing_file(unfiltered_bs, out_baseimg, out_basefmt, ++ true); ++ } else { ++ ret = bdrv_change_backing_file(unfiltered_bs, NULL, NULL, false); ++ } ++ ++ if (ret == -ENOSPC) { ++ error_report("Could not change the backing file to '%s': No " ++ "space left in the file header", out_baseimg); ++ } else if (ret == -EINVAL && out_baseimg && !out_basefmt) { ++ error_report("Could not change the backing file to '%s': backing " ++ "format must be specified", out_baseimg); ++ } else if (ret < 0) { ++ error_report("Could not change the backing file to '%s': %s", ++ out_baseimg, strerror(-ret)); ++ } ++ ++ qemu_progress_print(100, 0); ++ /* ++ * TODO At this point it is possible to check if any clusters that are ++ * allocated in the COW file are the same in the backing file. If so, they ++ * could be dropped from the COW file. Don't do this before switching the ++ * backing file, in case of a crash this would lead to corruption. ++ */ ++out: ++ qemu_progress_end(); ++ /* Cleanup */ ++ if (!unsafe) { ++ blk_unref(blk_old_backing); ++ blk_unref(blk_new_backing); ++ } ++ qemu_vfree(buf_old); ++ qemu_vfree(buf_new); ++ ++ blk_unref(blk); ++ if (ret) { ++ return 1; ++ } ++ return 0; ++} ++ ++static int img_resize(int argc, char **argv) ++{ ++ Error *err = NULL; ++ int c, ret, relative; ++ const char *filename, *fmt, *size; ++ int64_t n, total_size, current_size; ++ bool quiet = false; ++ BlockBackend *blk = NULL; ++ PreallocMode prealloc = PREALLOC_MODE_OFF; ++ QemuOpts *param; ++ ++ static QemuOptsList resize_options = { ++ .name = "resize_options", ++ .head = QTAILQ_HEAD_INITIALIZER(resize_options.head), ++ .desc = { ++ { ++ .name = BLOCK_OPT_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Virtual disk size" ++ }, { ++ /* end of list */ ++ } ++ }, ++ }; ++ bool image_opts = false; ++ bool shrink = false; ++ ++ /* Remove size from argv manually so that negative numbers are not treated ++ * as options by getopt. */ ++ if (argc < 3) { ++ error_exit("Not enough arguments"); ++ return 1; ++ } ++ ++ size = argv[--argc]; ++ ++ /* Parse getopt arguments */ ++ fmt = NULL; ++ for(;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"preallocation", required_argument, 0, OPTION_PREALLOCATION}, ++ {"shrink", no_argument, 0, OPTION_SHRINK}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":f:hq", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ case OPTION_PREALLOCATION: ++ prealloc = qapi_enum_parse(&PreallocMode_lookup, optarg, ++ PREALLOC_MODE__MAX, NULL); ++ if (prealloc == PREALLOC_MODE__MAX) { ++ error_report("Invalid preallocation mode '%s'", optarg); ++ return 1; ++ } ++ break; ++ case OPTION_SHRINK: ++ shrink = true; ++ break; ++ } ++ } ++ if (optind != argc - 1) { ++ error_exit("Expecting image file name and size"); ++ } ++ filename = argv[optind++]; ++ ++ /* Choose grow, shrink, or absolute resize mode */ ++ switch (size[0]) { ++ case '+': ++ relative = 1; ++ size++; ++ break; ++ case '-': ++ relative = -1; ++ size++; ++ break; ++ default: ++ relative = 0; ++ break; ++ } ++ ++ /* Parse size */ ++ param = qemu_opts_create(&resize_options, NULL, 0, &error_abort); ++ if (!qemu_opt_set(param, BLOCK_OPT_SIZE, size, &err)) { ++ error_report_err(err); ++ ret = -1; ++ qemu_opts_del(param); ++ goto out; ++ } ++ n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0); ++ qemu_opts_del(param); ++ ++ blk = img_open(image_opts, filename, fmt, ++ BDRV_O_RDWR | BDRV_O_RESIZE, false, quiet, ++ false); ++ if (!blk) { ++ ret = -1; ++ goto out; ++ } ++ ++ current_size = blk_getlength(blk); ++ if (current_size < 0) { ++ error_report("Failed to inquire current image length: %s", ++ strerror(-current_size)); ++ ret = -1; ++ goto out; ++ } ++ ++ if (relative) { ++ total_size = current_size + n * relative; ++ } else { ++ total_size = n; ++ } ++ if (total_size <= 0) { ++ error_report("New image size must be positive"); ++ ret = -1; ++ goto out; ++ } ++ ++ if (total_size <= current_size && prealloc != PREALLOC_MODE_OFF) { ++ error_report("Preallocation can only be used for growing images"); ++ ret = -1; ++ goto out; ++ } ++ ++ if (total_size < current_size && !shrink) { ++ error_report("Use the --shrink option to perform a shrink operation."); ++ warn_report("Shrinking an image will delete all data beyond the " ++ "shrunken image's end. Before performing such an " ++ "operation, make sure there is no important data there."); ++ ret = -1; ++ goto out; ++ } ++ ++ /* ++ * The user expects the image to have the desired size after ++ * resizing, so pass @exact=true. It is of no use to report ++ * success when the image has not actually been resized. ++ */ ++ ret = blk_truncate(blk, total_size, true, prealloc, 0, &err); ++ if (!ret) { ++ qprintf(quiet, "Image resized.\n"); ++ } else { ++ error_report_err(err); ++ } ++out: ++ blk_unref(blk); ++ if (ret) { ++ return 1; ++ } ++ return 0; ++} ++ ++static void amend_status_cb(BlockDriverState *bs, ++ int64_t offset, int64_t total_work_size, ++ void *opaque) ++{ ++ qemu_progress_print(100.f * offset / total_work_size, 0); ++} ++ ++static int print_amend_option_help(const char *format) ++{ ++ BlockDriver *drv; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ /* Find driver and parse its options */ ++ drv = bdrv_find_format(format); ++ if (!drv) { ++ error_report("Unknown file format '%s'", format); ++ return 1; ++ } ++ ++ if (!drv->bdrv_amend_options) { ++ error_report("Format driver '%s' does not support option amendment", ++ format); ++ return 1; ++ } ++ ++ /* Every driver supporting amendment must have amend_opts */ ++ assert(drv->amend_opts); ++ ++ printf("Amend options for '%s':\n", format); ++ qemu_opts_print_help(drv->amend_opts, false); ++ return 0; ++} ++ ++static int img_amend(int argc, char **argv) ++{ ++ Error *err = NULL; ++ int c, ret = 0; ++ char *options = NULL; ++ QemuOptsList *amend_opts = NULL; ++ QemuOpts *opts = NULL; ++ const char *fmt = NULL, *filename, *cache; ++ int flags; ++ bool writethrough; ++ bool quiet = false, progress = false; ++ BlockBackend *blk = NULL; ++ BlockDriverState *bs = NULL; ++ bool image_opts = false; ++ bool force = false; ++ ++ cache = BDRV_DEFAULT_CACHE; ++ for (;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force", no_argument, 0, OPTION_FORCE}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":ho:f:t:pq", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ ++ switch (c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'o': ++ if (accumulate_options(&options, optarg) < 0) { ++ ret = -1; ++ goto out_no_progress; ++ } ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 't': ++ cache = optarg; ++ break; ++ case 'p': ++ progress = true; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ case OPTION_FORCE: ++ force = true; ++ break; ++ } ++ } ++ ++ if (!options) { ++ error_exit("Must specify options (-o)"); ++ } ++ ++ if (quiet) { ++ progress = false; ++ } ++ qemu_progress_init(progress, 1.0); ++ ++ filename = (optind == argc - 1) ? argv[argc - 1] : NULL; ++ if (fmt && has_help_option(options)) { ++ /* If a format is explicitly specified (and possibly no filename is ++ * given), print option help here */ ++ ret = print_amend_option_help(fmt); ++ goto out; ++ } ++ ++ if (optind != argc - 1) { ++ error_report("Expecting one image file name"); ++ ret = -1; ++ goto out; ++ } ++ ++ flags = BDRV_O_RDWR; ++ ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid cache option: %s", cache); ++ goto out; ++ } ++ ++ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet, ++ false); ++ if (!blk) { ++ ret = -1; ++ goto out; ++ } ++ bs = blk_bs(blk); ++ ++ fmt = bs->drv->format_name; ++ ++ if (has_help_option(options)) { ++ /* If the format was auto-detected, print option help here */ ++ ret = print_amend_option_help(fmt); ++ goto out; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ if (!bs->drv->bdrv_amend_options) { ++ error_report("Format driver '%s' does not support option amendment", ++ fmt); ++ bdrv_graph_rdunlock_main_loop(); ++ ret = -1; ++ goto out; ++ } ++ ++ /* Every driver supporting amendment must have amend_opts */ ++ assert(bs->drv->amend_opts); ++ ++ amend_opts = qemu_opts_append(amend_opts, bs->drv->amend_opts); ++ opts = qemu_opts_create(amend_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_do_parse(opts, options, NULL, &err)) { ++ /* Try to parse options using the create options */ ++ amend_opts = qemu_opts_append(amend_opts, bs->drv->create_opts); ++ qemu_opts_del(opts); ++ opts = qemu_opts_create(amend_opts, NULL, 0, &error_abort); ++ if (qemu_opts_do_parse(opts, options, NULL, NULL)) { ++ error_append_hint(&err, ++ "This option is only supported for image creation\n"); ++ } ++ ++ bdrv_graph_rdunlock_main_loop(); ++ error_report_err(err); ++ ret = -1; ++ goto out; ++ } ++ ++ /* In case the driver does not call amend_status_cb() */ ++ qemu_progress_print(0.f, 0); ++ ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, force, &err); ++ qemu_progress_print(100.f, 0); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (ret < 0) { ++ error_report_err(err); ++ goto out; ++ } ++ ++out: ++ qemu_progress_end(); ++ ++out_no_progress: ++ blk_unref(blk); ++ qemu_opts_del(opts); ++ qemu_opts_free(amend_opts); ++ g_free(options); ++ ++ if (ret) { ++ return 1; ++ } ++ return 0; ++} ++ ++typedef struct BenchData { ++ BlockBackend *blk; ++ uint64_t image_size; ++ bool write; ++ int bufsize; ++ int step; ++ int nrreq; ++ int n; ++ int flush_interval; ++ bool drain_on_flush; ++ uint8_t *buf; ++ QEMUIOVector *qiov; ++ ++ int in_flight; ++ bool in_flush; ++ uint64_t offset; ++} BenchData; ++ ++static void bench_undrained_flush_cb(void *opaque, int ret) ++{ ++ if (ret < 0) { ++ error_report("Failed flush request: %s", strerror(-ret)); ++ exit(EXIT_FAILURE); ++ } ++} ++ ++static void bench_cb(void *opaque, int ret) ++{ ++ BenchData *b = opaque; ++ BlockAIOCB *acb; ++ ++ if (ret < 0) { ++ error_report("Failed request: %s", strerror(-ret)); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (b->in_flush) { ++ /* Just finished a flush with drained queue: Start next requests */ ++ assert(b->in_flight == 0); ++ b->in_flush = false; ++ } else if (b->in_flight > 0) { ++ int remaining = b->n - b->in_flight; ++ ++ b->n--; ++ b->in_flight--; ++ ++ /* Time for flush? Drain queue if requested, then flush */ ++ if (b->flush_interval && remaining % b->flush_interval == 0) { ++ if (!b->in_flight || !b->drain_on_flush) { ++ BlockCompletionFunc *cb; ++ ++ if (b->drain_on_flush) { ++ b->in_flush = true; ++ cb = bench_cb; ++ } else { ++ cb = bench_undrained_flush_cb; ++ } ++ ++ acb = blk_aio_flush(b->blk, cb, b); ++ if (!acb) { ++ error_report("Failed to issue flush request"); ++ exit(EXIT_FAILURE); ++ } ++ } ++ if (b->drain_on_flush) { ++ return; ++ } ++ } ++ } ++ ++ while (b->n > b->in_flight && b->in_flight < b->nrreq) { ++ int64_t offset = b->offset; ++ /* blk_aio_* might look for completed I/Os and kick bench_cb ++ * again, so make sure this operation is counted by in_flight ++ * and b->offset is ready for the next submission. ++ */ ++ b->in_flight++; ++ b->offset += b->step; ++ b->offset %= b->image_size; ++ if (b->write) { ++ acb = blk_aio_pwritev(b->blk, offset, b->qiov, 0, bench_cb, b); ++ } else { ++ acb = blk_aio_preadv(b->blk, offset, b->qiov, 0, bench_cb, b); ++ } ++ if (!acb) { ++ error_report("Failed to issue request"); ++ exit(EXIT_FAILURE); ++ } ++ } ++} ++ ++static int img_bench(int argc, char **argv) ++{ ++ int c, ret = 0; ++ const char *fmt = NULL, *filename; ++ bool quiet = false; ++ bool image_opts = false; ++ bool is_write = false; ++ int count = 75000; ++ int depth = 64; ++ int64_t offset = 0; ++ size_t bufsize = 4096; ++ int pattern = 0; ++ size_t step = 0; ++ int flush_interval = 0; ++ bool drain_on_flush = true; ++ int64_t image_size; ++ BlockBackend *blk = NULL; ++ BenchData data = {}; ++ int flags = 0; ++ bool writethrough = false; ++ struct timeval t1, t2; ++ int i; ++ bool force_share = false; ++ size_t buf_size = 0; ++ ++ for (;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"flush-interval", required_argument, 0, OPTION_FLUSH_INTERVAL}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"pattern", required_argument, 0, OPTION_PATTERN}, ++ {"no-drain", no_argument, 0, OPTION_NO_DRAIN}, ++ {"force-share", no_argument, 0, 'U'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":hc:d:f:ni:o:qs:S:t:wU", long_options, ++ NULL); ++ if (c == -1) { ++ break; ++ } ++ ++ switch (c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'c': ++ { ++ unsigned long res; ++ ++ if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > INT_MAX) { ++ error_report("Invalid request count specified"); ++ return 1; ++ } ++ count = res; ++ break; ++ } ++ case 'd': ++ { ++ unsigned long res; ++ ++ if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > INT_MAX) { ++ error_report("Invalid queue depth specified"); ++ return 1; ++ } ++ depth = res; ++ break; ++ } ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'n': ++ flags |= BDRV_O_NATIVE_AIO; ++ break; ++ case 'i': ++ ret = bdrv_parse_aio(optarg, &flags); ++ if (ret < 0) { ++ error_report("Invalid aio option: %s", optarg); ++ ret = -1; ++ goto out; ++ } ++ break; ++ case 'o': ++ { ++ offset = cvtnum("offset", optarg); ++ if (offset < 0) { ++ return 1; ++ } ++ break; ++ } ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case 's': ++ { ++ int64_t sval; ++ ++ sval = cvtnum_full("buffer size", optarg, 0, INT_MAX); ++ if (sval < 0) { ++ return 1; ++ } ++ ++ bufsize = sval; ++ break; ++ } ++ case 'S': ++ { ++ int64_t sval; ++ ++ sval = cvtnum_full("step_size", optarg, 0, INT_MAX); ++ if (sval < 0) { ++ return 1; ++ } ++ ++ step = sval; ++ break; ++ } ++ case 't': ++ ret = bdrv_parse_cache_mode(optarg, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid cache mode"); ++ ret = -1; ++ goto out; ++ } ++ break; ++ case 'w': ++ flags |= BDRV_O_RDWR; ++ is_write = true; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_PATTERN: ++ { ++ unsigned long res; ++ ++ if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > 0xff) { ++ error_report("Invalid pattern byte specified"); ++ return 1; ++ } ++ pattern = res; ++ break; ++ } ++ case OPTION_FLUSH_INTERVAL: ++ { ++ unsigned long res; ++ ++ if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > INT_MAX) { ++ error_report("Invalid flush interval specified"); ++ return 1; ++ } ++ flush_interval = res; ++ break; ++ } ++ case OPTION_NO_DRAIN: ++ drain_on_flush = false; ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ filename = argv[argc - 1]; ++ ++ if (!is_write && flush_interval) { ++ error_report("--flush-interval is only available in write tests"); ++ ret = -1; ++ goto out; ++ } ++ if (flush_interval && flush_interval < depth) { ++ error_report("Flush interval can't be smaller than depth"); ++ ret = -1; ++ goto out; ++ } ++ ++ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet, ++ force_share); ++ if (!blk) { ++ ret = -1; ++ goto out; ++ } ++ ++ image_size = blk_getlength(blk); ++ if (image_size < 0) { ++ ret = image_size; ++ goto out; ++ } ++ ++ data = (BenchData) { ++ .blk = blk, ++ .image_size = image_size, ++ .bufsize = bufsize, ++ .step = step ?: bufsize, ++ .nrreq = depth, ++ .n = count, ++ .offset = offset, ++ .write = is_write, ++ .flush_interval = flush_interval, ++ .drain_on_flush = drain_on_flush, ++ }; ++ printf("Sending %d %s requests, %d bytes each, %d in parallel " ++ "(starting at offset %" PRId64 ", step size %d)\n", ++ data.n, data.write ? "write" : "read", data.bufsize, data.nrreq, ++ data.offset, data.step); ++ if (flush_interval) { ++ printf("Sending flush every %d requests\n", flush_interval); ++ } ++ ++ buf_size = data.nrreq * data.bufsize; ++ data.buf = blk_blockalign(blk, buf_size); ++ memset(data.buf, pattern, data.nrreq * data.bufsize); ++ ++ blk_register_buf(blk, data.buf, buf_size, &error_fatal); ++ ++ data.qiov = g_new(QEMUIOVector, data.nrreq); ++ for (i = 0; i < data.nrreq; i++) { ++ qemu_iovec_init(&data.qiov[i], 1); ++ qemu_iovec_add(&data.qiov[i], ++ data.buf + i * data.bufsize, data.bufsize); ++ } ++ ++ gettimeofday(&t1, NULL); ++ bench_cb(&data, 0); ++ ++ while (data.n > 0) { ++ main_loop_wait(false); ++ } ++ gettimeofday(&t2, NULL); ++ ++ printf("Run completed in %3.3f seconds.\n", ++ (t2.tv_sec - t1.tv_sec) ++ + ((double)(t2.tv_usec - t1.tv_usec) / 1000000)); ++ ++out: ++ if (data.buf) { ++ blk_unregister_buf(blk, data.buf, buf_size); ++ } ++ qemu_vfree(data.buf); ++ blk_unref(blk); ++ ++ if (ret) { ++ return 1; ++ } ++ return 0; ++} ++ ++enum ImgBitmapAct { ++ BITMAP_ADD, ++ BITMAP_REMOVE, ++ BITMAP_CLEAR, ++ BITMAP_ENABLE, ++ BITMAP_DISABLE, ++ BITMAP_MERGE, ++}; ++typedef struct ImgBitmapAction { ++ enum ImgBitmapAct act; ++ const char *src; /* only used for merge */ ++ QSIMPLEQ_ENTRY(ImgBitmapAction) next; ++} ImgBitmapAction; ++ ++static int img_bitmap(int argc, char **argv) ++{ ++ Error *err = NULL; ++ int c, ret = 1; ++ QemuOpts *opts = NULL; ++ const char *fmt = NULL, *src_fmt = NULL, *src_filename = NULL; ++ const char *filename, *bitmap; ++ BlockBackend *blk = NULL, *src = NULL; ++ BlockDriverState *bs = NULL, *src_bs = NULL; ++ bool image_opts = false; ++ int64_t granularity = 0; ++ bool add = false, merge = false; ++ QSIMPLEQ_HEAD(, ImgBitmapAction) actions; ++ ImgBitmapAction *act, *act_next; ++ const char *op; ++ int inactivate_ret; ++ ++ QSIMPLEQ_INIT(&actions); ++ ++ for (;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"add", no_argument, 0, OPTION_ADD}, ++ {"remove", no_argument, 0, OPTION_REMOVE}, ++ {"clear", no_argument, 0, OPTION_CLEAR}, ++ {"enable", no_argument, 0, OPTION_ENABLE}, ++ {"disable", no_argument, 0, OPTION_DISABLE}, ++ {"merge", required_argument, 0, OPTION_MERGE}, ++ {"granularity", required_argument, 0, 'g'}, ++ {"source-file", required_argument, 0, 'b'}, ++ {"source-format", required_argument, 0, 'F'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":b:f:F:g:h", long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ ++ switch (c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'b': ++ src_filename = optarg; ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'F': ++ src_fmt = optarg; ++ break; ++ case 'g': ++ granularity = cvtnum("granularity", optarg); ++ if (granularity < 0) { ++ return 1; ++ } ++ break; ++ case OPTION_ADD: ++ act = g_new0(ImgBitmapAction, 1); ++ act->act = BITMAP_ADD; ++ QSIMPLEQ_INSERT_TAIL(&actions, act, next); ++ add = true; ++ break; ++ case OPTION_REMOVE: ++ act = g_new0(ImgBitmapAction, 1); ++ act->act = BITMAP_REMOVE; ++ QSIMPLEQ_INSERT_TAIL(&actions, act, next); ++ break; ++ case OPTION_CLEAR: ++ act = g_new0(ImgBitmapAction, 1); ++ act->act = BITMAP_CLEAR; ++ QSIMPLEQ_INSERT_TAIL(&actions, act, next); ++ break; ++ case OPTION_ENABLE: ++ act = g_new0(ImgBitmapAction, 1); ++ act->act = BITMAP_ENABLE; ++ QSIMPLEQ_INSERT_TAIL(&actions, act, next); ++ break; ++ case OPTION_DISABLE: ++ act = g_new0(ImgBitmapAction, 1); ++ act->act = BITMAP_DISABLE; ++ QSIMPLEQ_INSERT_TAIL(&actions, act, next); ++ break; ++ case OPTION_MERGE: ++ act = g_new0(ImgBitmapAction, 1); ++ act->act = BITMAP_MERGE; ++ act->src = optarg; ++ QSIMPLEQ_INSERT_TAIL(&actions, act, next); ++ merge = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ ++ if (QSIMPLEQ_EMPTY(&actions)) { ++ error_report("Need at least one of --add, --remove, --clear, " ++ "--enable, --disable, or --merge"); ++ goto out; ++ } ++ ++ if (granularity && !add) { ++ error_report("granularity only supported with --add"); ++ goto out; ++ } ++ if (src_fmt && !src_filename) { ++ error_report("-F only supported with -b"); ++ goto out; ++ } ++ if (src_filename && !merge) { ++ error_report("Merge bitmap source file only supported with " ++ "--merge"); ++ goto out; ++ } ++ ++ if (optind != argc - 2) { ++ error_report("Expecting filename and bitmap name"); ++ goto out; ++ } ++ ++ filename = argv[optind]; ++ bitmap = argv[optind + 1]; ++ ++ /* ++ * No need to open backing chains; we will be manipulating bitmaps ++ * directly in this image without reference to image contents. ++ */ ++ blk = img_open(image_opts, filename, fmt, BDRV_O_RDWR | BDRV_O_NO_BACKING, ++ false, false, false); ++ if (!blk) { ++ goto out; ++ } ++ bs = blk_bs(blk); ++ if (src_filename) { ++ src = img_open(false, src_filename, src_fmt, BDRV_O_NO_BACKING, ++ false, false, false); ++ if (!src) { ++ goto out; ++ } ++ src_bs = blk_bs(src); ++ } else { ++ src_bs = bs; ++ } ++ ++ QSIMPLEQ_FOREACH_SAFE(act, &actions, next, act_next) { ++ switch (act->act) { ++ case BITMAP_ADD: ++ qmp_block_dirty_bitmap_add(bs->node_name, bitmap, ++ !!granularity, granularity, true, true, ++ false, false, &err); ++ op = "add"; ++ break; ++ case BITMAP_REMOVE: ++ qmp_block_dirty_bitmap_remove(bs->node_name, bitmap, &err); ++ op = "remove"; ++ break; ++ case BITMAP_CLEAR: ++ qmp_block_dirty_bitmap_clear(bs->node_name, bitmap, &err); ++ op = "clear"; ++ break; ++ case BITMAP_ENABLE: ++ qmp_block_dirty_bitmap_enable(bs->node_name, bitmap, &err); ++ op = "enable"; ++ break; ++ case BITMAP_DISABLE: ++ qmp_block_dirty_bitmap_disable(bs->node_name, bitmap, &err); ++ op = "disable"; ++ break; ++ case BITMAP_MERGE: ++ do_dirty_bitmap_merge(bs->node_name, bitmap, src_bs->node_name, ++ act->src, &err); ++ op = "merge"; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ ++ if (err) { ++ error_reportf_err(err, "Operation %s on bitmap %s failed: ", ++ op, bitmap); ++ goto out; ++ } ++ g_free(act); ++ } ++ ++ ret = 0; ++ ++ out: ++ /* ++ * Manually inactivate the images first because this way we can know whether ++ * an error occurred. blk_unref() doesn't tell us about failures. ++ */ ++ inactivate_ret = bdrv_inactivate_all(); ++ if (inactivate_ret < 0) { ++ error_report("Error while closing the image: %s", strerror(-inactivate_ret)); ++ ret = 1; ++ } ++ ++ blk_unref(src); ++ blk_unref(blk); ++ qemu_opts_del(opts); ++ return ret; ++} ++ ++#define C_BS 01 ++#define C_COUNT 02 ++#define C_IF 04 ++#define C_OF 010 ++#define C_SKIP 020 ++ ++struct DdInfo { ++ unsigned int flags; ++ int64_t count; ++}; ++ ++struct DdIo { ++ int bsz; /* Block size */ ++ char *filename; ++ uint8_t *buf; ++ int64_t offset; ++}; ++ ++struct DdOpts { ++ const char *name; ++ int (*f)(const char *, struct DdIo *, struct DdIo *, struct DdInfo *); ++ unsigned int flag; ++}; ++ ++static int img_dd_bs(const char *arg, ++ struct DdIo *in, struct DdIo *out, ++ struct DdInfo *dd) ++{ ++ int64_t res; ++ ++ res = cvtnum_full("bs", arg, 1, INT_MAX); ++ ++ if (res < 0) { ++ return 1; ++ } ++ in->bsz = out->bsz = res; ++ ++ return 0; ++} ++ ++static int img_dd_count(const char *arg, ++ struct DdIo *in, struct DdIo *out, ++ struct DdInfo *dd) ++{ ++ dd->count = cvtnum("count", arg); ++ ++ if (dd->count < 0) { ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int img_dd_if(const char *arg, ++ struct DdIo *in, struct DdIo *out, ++ struct DdInfo *dd) ++{ ++ in->filename = g_strdup(arg); ++ ++ return 0; ++} ++ ++static int img_dd_of(const char *arg, ++ struct DdIo *in, struct DdIo *out, ++ struct DdInfo *dd) ++{ ++ out->filename = g_strdup(arg); ++ ++ return 0; ++} ++ ++static int img_dd_skip(const char *arg, ++ struct DdIo *in, struct DdIo *out, ++ struct DdInfo *dd) ++{ ++ in->offset = cvtnum("skip", arg); ++ ++ if (in->offset < 0) { ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int img_dd(int argc, char **argv) ++{ ++ int ret = 0; ++ char *arg = NULL; ++ char *tmp; ++ BlockDriver *drv = NULL, *proto_drv = NULL; ++ BlockBackend *blk1 = NULL, *blk2 = NULL; ++ QemuOpts *opts = NULL; ++ QemuOptsList *create_opts = NULL; ++ Error *local_err = NULL; ++ bool image_opts = false; ++ int c, i; ++ const char *out_fmt = "raw"; ++ const char *fmt = NULL; ++ int64_t size = 0; ++ int64_t out_pos, in_pos; ++ bool force_share = false; ++ struct DdInfo dd = { ++ .flags = 0, ++ .count = 0, ++ }; ++ struct DdIo in = { ++ .bsz = 512, /* Block size is by default 512 bytes */ ++ .filename = NULL, ++ .buf = NULL, ++ .offset = 0 ++ }; ++ struct DdIo out = { ++ .bsz = 512, ++ .filename = NULL, ++ .buf = NULL, ++ .offset = 0 ++ }; ++ ++ const struct DdOpts options[] = { ++ { "bs", img_dd_bs, C_BS }, ++ { "count", img_dd_count, C_COUNT }, ++ { "if", img_dd_if, C_IF }, ++ { "of", img_dd_of, C_OF }, ++ { "skip", img_dd_skip, C_SKIP }, ++ { NULL, NULL, 0 } ++ }; ++ const struct option long_options[] = { ++ { "help", no_argument, 0, 'h'}, ++ { "object", required_argument, 0, OPTION_OBJECT}, ++ { "image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ { "force-share", no_argument, 0, 'U'}, ++ { 0, 0, 0, 0 } ++ }; ++ ++ while ((c = getopt_long(argc, argv, ":hf:O:U", long_options, NULL))) { ++ if (c == EOF) { ++ break; ++ } ++ switch (c) { ++ case 'O': ++ out_fmt = optarg; ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ ++ for (i = optind; i < argc; i++) { ++ int j; ++ arg = g_strdup(argv[i]); ++ ++ tmp = strchr(arg, '='); ++ if (tmp == NULL) { ++ error_report("unrecognized operand %s", arg); ++ ret = -1; ++ goto out; ++ } ++ ++ *tmp++ = '\0'; ++ ++ for (j = 0; options[j].name != NULL; j++) { ++ if (!strcmp(arg, options[j].name)) { ++ break; ++ } ++ } ++ if (options[j].name == NULL) { ++ error_report("unrecognized operand %s", arg); ++ ret = -1; ++ goto out; ++ } ++ ++ if (options[j].f(tmp, &in, &out, &dd) != 0) { ++ ret = -1; ++ goto out; ++ } ++ dd.flags |= options[j].flag; ++ g_free(arg); ++ arg = NULL; ++ } ++ ++ if (!(dd.flags & C_IF && dd.flags & C_OF)) { ++ error_report("Must specify both input and output files"); ++ ret = -1; ++ goto out; ++ } ++ ++ blk1 = img_open(image_opts, in.filename, fmt, 0, false, false, ++ force_share); ++ ++ if (!blk1) { ++ ret = -1; ++ goto out; ++ } ++ ++ drv = bdrv_find_format(out_fmt); ++ if (!drv) { ++ error_report("Unknown file format"); ++ ret = -1; ++ goto out; ++ } ++ proto_drv = bdrv_find_protocol(out.filename, true, &local_err); ++ ++ if (!proto_drv) { ++ error_report_err(local_err); ++ ret = -1; ++ goto out; ++ } ++ if (!drv->create_opts) { ++ error_report("Format driver '%s' does not support image creation", ++ drv->format_name); ++ ret = -1; ++ goto out; ++ } ++ if (!proto_drv->create_opts) { ++ error_report("Protocol driver '%s' does not support image creation", ++ proto_drv->format_name); ++ ret = -1; ++ goto out; ++ } ++ create_opts = qemu_opts_append(create_opts, drv->create_opts); ++ create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); ++ ++ opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); ++ ++ size = blk_getlength(blk1); ++ if (size < 0) { ++ error_report("Failed to get size for '%s'", in.filename); ++ ret = -1; ++ goto out; ++ } ++ ++ if (dd.flags & C_COUNT && dd.count <= INT64_MAX / in.bsz && ++ dd.count * in.bsz < size) { ++ size = dd.count * in.bsz; ++ } ++ ++ /* Overflow means the specified offset is beyond input image's size */ ++ if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz || ++ size < in.bsz * in.offset)) { ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, 0, &error_abort); ++ } else { ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, ++ size - in.bsz * in.offset, &error_abort); ++ } ++ ++ ret = bdrv_create(drv, out.filename, opts, &local_err); ++ if (ret < 0) { ++ error_reportf_err(local_err, ++ "%s: error while creating output image: ", ++ out.filename); ++ ret = -1; ++ goto out; ++ } ++ ++ /* TODO, we can't honour --image-opts for the target, ++ * since it needs to be given in a format compatible ++ * with the bdrv_create() call above which does not ++ * support image-opts style. ++ */ ++ blk2 = img_open_file(out.filename, NULL, out_fmt, BDRV_O_RDWR, ++ false, false, false); ++ ++ if (!blk2) { ++ ret = -1; ++ goto out; ++ } ++ ++ if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz || ++ size < in.offset * in.bsz)) { ++ /* We give a warning if the skip option is bigger than the input ++ * size and create an empty output disk image (i.e. like dd(1)). ++ */ ++ error_report("%s: cannot skip to specified offset", in.filename); ++ in_pos = size; ++ } else { ++ in_pos = in.offset * in.bsz; ++ } ++ ++ in.buf = g_new(uint8_t, in.bsz); ++ ++ for (out_pos = 0; in_pos < size; ) { ++ int bytes = (in_pos + in.bsz > size) ? size - in_pos : in.bsz; ++ ++ ret = blk_pread(blk1, in_pos, bytes, in.buf, 0); ++ if (ret < 0) { ++ error_report("error while reading from input image file: %s", ++ strerror(-ret)); ++ goto out; ++ } ++ in_pos += bytes; ++ ++ ret = blk_pwrite(blk2, out_pos, bytes, in.buf, 0); ++ if (ret < 0) { ++ error_report("error while writing to output image file: %s", ++ strerror(-ret)); ++ goto out; ++ } ++ out_pos += bytes; ++ } ++ ++out: ++ g_free(arg); ++ qemu_opts_del(opts); ++ qemu_opts_free(create_opts); ++ blk_unref(blk1); ++ blk_unref(blk2); ++ g_free(in.filename); ++ g_free(out.filename); ++ g_free(in.buf); ++ g_free(out.buf); ++ ++ if (ret) { ++ return 1; ++ } ++ return 0; ++} ++ ++static void dump_json_block_measure_info(BlockMeasureInfo *info) ++{ ++ GString *str; ++ QObject *obj; ++ Visitor *v = qobject_output_visitor_new(&obj); ++ ++ visit_type_BlockMeasureInfo(v, NULL, &info, &error_abort); ++ visit_complete(v, &obj); ++ str = qobject_to_json_pretty(obj, true); ++ assert(str != NULL); ++ printf("%s\n", str->str); ++ qobject_unref(obj); ++ visit_free(v); ++ g_string_free(str, true); ++} ++ ++static int img_measure(int argc, char **argv) ++{ ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"output", required_argument, 0, OPTION_OUTPUT}, ++ {"size", required_argument, 0, OPTION_SIZE}, ++ {"force-share", no_argument, 0, 'U'}, ++ {0, 0, 0, 0} ++ }; ++ OutputFormat output_format = OFORMAT_HUMAN; ++ BlockBackend *in_blk = NULL; ++ BlockDriver *drv; ++ const char *filename = NULL; ++ const char *fmt = NULL; ++ const char *out_fmt = "raw"; ++ char *options = NULL; ++ char *snapshot_name = NULL; ++ bool force_share = false; ++ QemuOpts *opts = NULL; ++ QemuOpts *object_opts = NULL; ++ QemuOpts *sn_opts = NULL; ++ QemuOptsList *create_opts = NULL; ++ bool image_opts = false; ++ uint64_t img_size = UINT64_MAX; ++ BlockMeasureInfo *info = NULL; ++ Error *local_err = NULL; ++ int ret = 1; ++ int c; ++ ++ while ((c = getopt_long(argc, argv, "hf:O:o:l:U", ++ long_options, NULL)) != -1) { ++ switch (c) { ++ case '?': ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'O': ++ out_fmt = optarg; ++ break; ++ case 'o': ++ if (accumulate_options(&options, optarg) < 0) { ++ goto out; ++ } ++ break; ++ case 'l': ++ if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { ++ sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts, ++ optarg, false); ++ if (!sn_opts) { ++ error_report("Failed in parsing snapshot param '%s'", ++ optarg); ++ goto out; ++ } ++ } else { ++ snapshot_name = optarg; ++ } ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ case OPTION_OUTPUT: ++ if (!strcmp(optarg, "json")) { ++ output_format = OFORMAT_JSON; ++ } else if (!strcmp(optarg, "human")) { ++ output_format = OFORMAT_HUMAN; ++ } else { ++ error_report("--output must be used with human or json " ++ "as argument."); ++ goto out; ++ } ++ break; ++ case OPTION_SIZE: ++ { ++ int64_t sval; ++ ++ sval = cvtnum("image size", optarg); ++ if (sval < 0) { ++ goto out; ++ } ++ img_size = (uint64_t)sval; ++ } ++ break; ++ } ++ } ++ ++ if (argc - optind > 1) { ++ error_report("At most one filename argument is allowed."); ++ goto out; ++ } else if (argc - optind == 1) { ++ filename = argv[optind]; ++ } ++ ++ if (!filename && (image_opts || fmt || snapshot_name || sn_opts)) { ++ error_report("--image-opts, -f, and -l require a filename argument."); ++ goto out; ++ } ++ if (filename && img_size != UINT64_MAX) { ++ error_report("--size N cannot be used together with a filename."); ++ goto out; ++ } ++ if (!filename && img_size == UINT64_MAX) { ++ error_report("Either --size N or one filename must be specified."); ++ goto out; ++ } ++ ++ if (filename) { ++ in_blk = img_open(image_opts, filename, fmt, 0, ++ false, false, force_share); ++ if (!in_blk) { ++ goto out; ++ } ++ ++ if (sn_opts) { ++ bdrv_snapshot_load_tmp(blk_bs(in_blk), ++ qemu_opt_get(sn_opts, SNAPSHOT_OPT_ID), ++ qemu_opt_get(sn_opts, SNAPSHOT_OPT_NAME), ++ &local_err); ++ } else if (snapshot_name != NULL) { ++ bdrv_snapshot_load_tmp_by_id_or_name(blk_bs(in_blk), ++ snapshot_name, &local_err); ++ } ++ if (local_err) { ++ error_reportf_err(local_err, "Failed to load snapshot: "); ++ goto out; ++ } ++ } ++ ++ drv = bdrv_find_format(out_fmt); ++ if (!drv) { ++ error_report("Unknown file format '%s'", out_fmt); ++ goto out; ++ } ++ if (!drv->create_opts) { ++ error_report("Format driver '%s' does not support image creation", ++ drv->format_name); ++ goto out; ++ } ++ ++ create_opts = qemu_opts_append(create_opts, drv->create_opts); ++ create_opts = qemu_opts_append(create_opts, bdrv_file.create_opts); ++ opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); ++ if (options) { ++ if (!qemu_opts_do_parse(opts, options, NULL, &local_err)) { ++ error_report_err(local_err); ++ error_report("Invalid options for file format '%s'", out_fmt); ++ goto out; ++ } ++ } ++ if (img_size != UINT64_MAX) { ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort); ++ } ++ ++ info = bdrv_measure(drv, opts, in_blk ? blk_bs(in_blk) : NULL, &local_err); ++ if (local_err) { ++ error_report_err(local_err); ++ goto out; ++ } ++ ++ if (output_format == OFORMAT_HUMAN) { ++ printf("required size: %" PRIu64 "\n", info->required); ++ printf("fully allocated size: %" PRIu64 "\n", info->fully_allocated); ++ if (info->has_bitmaps) { ++ printf("bitmaps size: %" PRIu64 "\n", info->bitmaps); ++ } ++ } else { ++ dump_json_block_measure_info(info); ++ } ++ ++ ret = 0; ++ ++out: ++ qapi_free_BlockMeasureInfo(info); ++ qemu_opts_del(object_opts); ++ qemu_opts_del(opts); ++ qemu_opts_del(sn_opts); ++ qemu_opts_free(create_opts); ++ g_free(options); ++ blk_unref(in_blk); ++ return ret; ++} ++ ++static const img_cmd_t img_cmds[] = { ++#define DEF(option, callback, arg_string) \ ++ { option, callback }, ++#include "qemu-img-cmds.h" ++#undef DEF ++ { NULL, NULL, }, ++}; ++ ++int main(int argc, char **argv) ++{ ++ const img_cmd_t *cmd; ++ const char *cmdname; ++ int c; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"version", no_argument, 0, 'V'}, ++ {"trace", required_argument, NULL, 'T'}, ++ {0, 0, 0, 0} ++ }; ++ ++#ifdef CONFIG_POSIX ++ signal(SIGPIPE, SIG_IGN); ++#endif ++ ++ socket_init(); ++ error_init(argv[0]); ++ module_call_init(MODULE_INIT_TRACE); ++ qemu_init_exec_dir(argv[0]); ++ ++ qemu_init_main_loop(&error_fatal); ++ ++ qcrypto_init(&error_fatal); ++ ++ module_call_init(MODULE_INIT_QOM); ++ bdrv_init(); ++ if (argc < 2) { ++ error_exit("Not enough arguments"); ++ } ++ ++ qemu_add_opts(&qemu_source_opts); ++ qemu_add_opts(&qemu_trace_opts); ++ ++ while ((c = getopt_long(argc, argv, "+:hVT:", long_options, NULL)) != -1) { ++ switch (c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ return 0; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ return 0; ++ case 'h': ++ help(); ++ return 0; ++ case 'V': ++ printf(QEMU_IMG_VERSION); ++ return 0; ++ case 'T': ++ trace_opt_parse(optarg); ++ break; ++ } ++ } ++ ++ cmdname = argv[optind]; ++ ++ /* reset getopt_long scanning */ ++ argc -= optind; ++ if (argc < 1) { ++ return 0; ++ } ++ argv += optind; ++ qemu_reset_optind(); ++ ++ if (!trace_init_backends()) { ++ exit(1); ++ } ++ trace_init_file(); ++ qemu_set_log(LOG_TRACE, &error_fatal); ++ ++ /* find the command */ ++ for (cmd = img_cmds; cmd->name != NULL; cmd++) { ++ if (!strcmp(cmdname, cmd->name)) { ++ return cmd->handler(argc, argv); ++ } ++ } ++ ++ /* not found */ ++ error_exit("Command not found: %s", cmdname); ++} +diff --git a/qcow2/qemu-progress.c b/qcow2/qemu-progress.c +new file mode 100644 +index 00000000..aa994668 +--- /dev/null ++++ b/qcow2/qemu-progress.c +@@ -0,0 +1,162 @@ ++/* ++ * QEMU progress printing utility functions ++ * ++ * Copyright (C) 2011 Jes Sorensen ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/qemu-progress.h" ++ ++struct progress_state { ++ float current; ++ float last_print; ++ float min_skip; ++ void (*print)(void); ++ void (*end)(void); ++}; ++ ++static struct progress_state state; ++static volatile sig_atomic_t print_pending; ++ ++/* ++ * Simple progress print function. ++ * @percent relative percent of current operation ++ * @max percent of total operation ++ */ ++static void progress_simple_print(void) ++{ ++ printf(" (%3.2f/100%%)\r", state.current); ++ fflush(stdout); ++} ++ ++static void progress_simple_end(void) ++{ ++ printf("\n"); ++} ++ ++static void progress_simple_init(void) ++{ ++ state.print = progress_simple_print; ++ state.end = progress_simple_end; ++} ++ ++#ifdef CONFIG_POSIX ++static void sigusr_print(int signal) ++{ ++ print_pending = 1; ++} ++#endif ++ ++static void progress_dummy_print(void) ++{ ++ if (print_pending) { ++ fprintf(stderr, " (%3.2f/100%%)\n", state.current); ++ print_pending = 0; ++ } ++} ++ ++static void progress_dummy_end(void) ++{ ++} ++ ++static void progress_dummy_init(void) ++{ ++#ifdef CONFIG_POSIX ++ struct sigaction action; ++ sigset_t set; ++ ++ memset(&action, 0, sizeof(action)); ++ sigfillset(&action.sa_mask); ++ action.sa_handler = sigusr_print; ++ action.sa_flags = 0; ++ sigaction(SIGUSR1, &action, NULL); ++#ifdef SIGINFO ++ sigaction(SIGINFO, &action, NULL); ++#endif ++ ++ /* ++ * SIGUSR1 is SIG_IPI and gets blocked in qemu_init_main_loop(). In the ++ * tools that use the progress report SIGUSR1 isn't used in this meaning ++ * and instead should print the progress, so reenable it. ++ */ ++ sigemptyset(&set); ++ sigaddset(&set, SIGUSR1); ++ pthread_sigmask(SIG_UNBLOCK, &set, NULL); ++#endif ++ ++ state.print = progress_dummy_print; ++ state.end = progress_dummy_end; ++} ++ ++/* ++ * Initialize progress reporting. ++ * If @enabled is false, actual reporting is suppressed. The user can ++ * still trigger a report by sending a SIGUSR1. ++ * Reports are also suppressed unless we've had at least @min_skip ++ * percent progress since the last report. ++ */ ++void qemu_progress_init(int enabled, float min_skip) ++{ ++ state.min_skip = min_skip; ++ if (enabled) { ++ progress_simple_init(); ++ } else { ++ progress_dummy_init(); ++ } ++} ++ ++void qemu_progress_end(void) ++{ ++ state.end(); ++} ++ ++/* ++ * Report progress. ++ * @delta is how much progress we made. ++ * If @max is zero, @delta is an absolute value of the total job done. ++ * Else, @delta is a progress delta since the last call, as a fraction ++ * of @max. I.e. the delta is @delta * @max / 100. This allows ++ * relative accounting of functions which may be a different fraction of ++ * the full job, depending on the context they are called in. I.e. ++ * a function might be considered 40% of the full job if used from ++ * bdrv_img_create() but only 20% if called from img_convert(). ++ */ ++void qemu_progress_print(float delta, int max) ++{ ++ float current; ++ ++ if (max == 0) { ++ current = delta; ++ } else { ++ current = state.current + delta / 100 * max; ++ } ++ if (current > 100) { ++ current = 100; ++ } ++ state.current = current; ++ ++ if (current > (state.last_print + state.min_skip) || ++ current < (state.last_print - state.min_skip) || ++ current == 100 || current == 0) { ++ state.last_print = state.current; ++ state.print(); ++ } ++} diff --git a/SOURCES/0025-libqcow2-build-qcow2-library-for-tapdisk.patch b/SOURCES/0025-libqcow2-build-qcow2-library-for-tapdisk.patch new file mode 100644 index 0000000..01073e5 --- /dev/null +++ b/SOURCES/0025-libqcow2-build-qcow2-library-for-tapdisk.patch @@ -0,0 +1,6491 @@ +From 26429f3072a88c392ffade221422f2fc814d0bbf Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:28 +0100 +Subject: [PATCH] libqcow2: build qcow2 library for tapdisk + +This commit modify sources from qemu to disable some features +unneeded for tapdisk. + +Signed-off-by: Anthoine Bourgeois +--- + .gitignore | 4 + + Makefile.am | 1 + + configure.ac | 7 + + include/block/aio.h | 7 +- + include/block/trace.h | 12 + + include/compiler.h | 2 + + include/crypto/hash.h | 20 +- + include/hw/block/block.h | 2 +- + include/hw/qdev-core.h | 16 +- + include/list.h | 2 + + include/qapi/qapi-commands-block-core.h | 6 +- + include/qapi/qapi-commands-block.h | 2 +- + include/qapi/qapi-events-block-core.h | 4 +- + include/qapi/qapi-types-block-core.h | 50 +- + include/qapi/qapi-visit-block-core.h | 6 +- + include/qapi/qmp/dispatch.h | 2 +- + include/qcow2.h | 4 +- + include/qemu/atomic.h | 2 +- + include/qemu/bitops.h | 5 +- + include/qemu/config-host.h | 495 ++++++++++++++++++++ + include/qemu/hbitmap.h | 4 +- + include/qemu/host-utils.h | 6 +- + include/qemu/main-loop.h | 8 +- + include/qemu/osdep.h | 2 +- + include/sysemu/block-backend-common.h | 4 +- + include/sysemu/block-backend-global-state.h | 2 + + include/trace.h | 191 ++++++++ + include/trace/trace-root.h | 11 + + mk/blktap.spec.in | 1 + + qcow2/Makefile.am | 21 + + qcow2/lib/Makefile.am | 159 +++++++ + qcow2/lib/block.c | 20 +- + qcow2/lib/block/accounting.c | 3 +- + qcow2/lib/block/block-backend.c | 98 +++- + qcow2/lib/block/block-gen.c | 5 +- + qcow2/lib/block/dirty-bitmap.c | 2 + + qcow2/lib/block/file-posix.c | 20 +- + qcow2/lib/block/io.c | 40 +- + qcow2/lib/block/mirror.c | 6 + + qcow2/lib/block/qapi.c | 6 +- + qcow2/lib/block/raw-format.c | 4 + + qcow2/lib/blockdev.c | 24 +- + qcow2/lib/blockjob.c | 4 + + qcow2/lib/hw/block/block.c | 8 +- + qcow2/lib/qapi/qapi-events-block-core.c | 24 +- + qcow2/lib/qapi/qapi-events-job.c | 6 +- + qcow2/lib/qapi/qapi-types-block-core.c | 38 +- + qcow2/lib/qapi/qapi-types-common.c | 4 +- + qcow2/lib/qapi/qapi-types-crypto.c | 4 +- + qcow2/lib/qapi/qapi-types-job.c | 4 +- + qcow2/lib/qapi/qapi-types-sockets.c | 4 +- + qcow2/lib/qapi/qapi-types-yank.c | 4 +- + qcow2/lib/qapi/qapi-visit-block-core.c | 80 +++- + qcow2/lib/qapi/qapi-visit-common.c | 2 +- + qcow2/lib/qapi/qapi-visit-crypto.c | 2 +- + qcow2/lib/qapi/qapi-visit-job.c | 2 +- + qcow2/lib/qapi/qapi-visit-sockets.c | 2 +- + qcow2/lib/qapi/qapi-visit-yank.c | 2 +- + qcow2/lib/qapi/qmp-dispatch.c | 2 + + qcow2/lib/qapi/qobject-input-visitor.c | 4 +- + qcow2/lib/qcow2-cache.c | 2 +- + qcow2/lib/qcow2-cluster.c | 2 + + qcow2/lib/qcow2-refcount.c | 5 +- + qcow2/lib/qcow2-threads.c | 4 +- + qcow2/lib/qcow2.c | 158 +++++-- + qcow2/lib/qobject/block-qdict.c | 2 + + qcow2/lib/qom/object.c | 32 +- + qcow2/lib/system/cpus.c | 42 +- + qcow2/lib/util/aio-posix.c | 6 +- + qcow2/lib/util/async.c | 2 + + qcow2/lib/util/bufferiszero.c | 9 +- + qcow2/lib/util/cutils.c | 6 +- + qcow2/lib/util/error-report.c | 19 +- + qcow2/lib/util/error.c | 6 +- + qcow2/lib/util/hbitmap.c | 2 + + qcow2/lib/util/host-utils.c | 2 + + qcow2/lib/util/iov.c | 6 +- + qcow2/lib/util/main-loop.c | 18 +- + qcow2/lib/util/memalign.c | 3 +- + qcow2/lib/util/osdep.c | 22 +- + qcow2/lib/util/oslib-posix.c | 32 +- + qcow2/lib/util/qemu-print.c | 6 +- + qcow2/lib/util/qemu-sockets.c | 14 +- + qcow2/lib/util/qemu-thread-posix.c | 4 +- + qcow2/lib/util/qemu-timer.c | 25 +- + qcow2/lib/util/qsp.c | 10 +- + qcow2/lib/util/yank.c | 2 +- + qcow2/qemu-img.c | 76 +-- + 88 files changed, 1713 insertions(+), 286 deletions(-) + create mode 100644 include/block/trace.h + create mode 100644 include/qemu/config-host.h + create mode 100644 include/trace.h + create mode 100644 include/trace/trace-root.h + create mode 100644 qcow2/Makefile.am + create mode 100644 qcow2/lib/Makefile.am + +diff --git a/.gitignore b/.gitignore +index eb160772..c673495b 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -12,6 +12,7 @@ + *.trs + .deps + .libs ++.dirstamp + cscope.* + TAGS + VERSION +@@ -51,6 +52,7 @@ Makefile.in + /vhd/vhd-util + /vhd/lib/test/random-copy + /vhd/lib/test/test-snapshot ++/qcow2/qemu-img + /tapback/tapback + /test-driver + +@@ -73,6 +75,8 @@ Makefile.in + /vhd/Makefile + /vhd/lib/Makefile + /vhd/lib/test/Makefile ++/qcow2/Makefile ++/qcow2/lib/Makefile + /tapback/Makefile + mk/config.log + +diff --git a/Makefile.am b/Makefile.am +index 7133a020..78c56ba3 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -5,6 +5,7 @@ + + SUBDIRS = lvm + SUBDIRS += vhd ++SUBDIRS += qcow2 + SUBDIRS += cpumond + SUBDIRS += control + SUBDIRS += drivers +diff --git a/configure.ac b/configure.ac +index a419f8ae..26d3c321 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -29,6 +29,11 @@ AC_CHECK_LIB([uuid], [main], + [:], + AC_MSG_ERROR([Need uuid-dev])) + ++PKG_PROG_PKG_CONFIG ++PKG_CHECK_MODULES([GLIB], [glib-2.0]) ++PKG_CHECK_MODULES([ZLIB], [zlib]) ++PKG_CHECK_MODULES([LIBZSTD], [libzstd]) ++ + AS_IF([test x$with_libiconv != xno], + [AC_CHECK_LIB([iconv], [main], + [AC_SUBST([LIBICONV], ["-liconv"])], +@@ -64,6 +69,8 @@ cbt/Makefile + vhd/Makefile + vhd/lib/Makefile + vhd/lib/test/Makefile ++qcow2/Makefile ++qcow2/lib/Makefile + control/Makefile + drivers/Makefile + drivers/crypto/Makefile +diff --git a/include/block/aio.h b/include/block/aio.h +index 4ee81936..d9bdc698 100644 +--- a/include/block/aio.h ++++ b/include/block/aio.h +@@ -23,11 +23,15 @@ + #include "qemu/thread.h" + #include "qemu/timer.h" + #include "block/graph-lock.h" +-#include "hw/qdev-core.h" ++//#include "hw/qdev-core.h" + ++typedef struct { ++ bool engaged_in_io; ++} MemReentrancyGuard; + + typedef struct BlockAIOCB BlockAIOCB; + typedef void BlockCompletionFunc(void *opaque, int ret); ++typedef struct AioContext AioContext; + + typedef struct AIOCBInfo { + void (*cancel_async)(BlockAIOCB *acb); +@@ -294,6 +298,7 @@ void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, + * A convenience wrapper for aio_bh_schedule_oneshot_full() that uses cb as the + * name string. + */ ++#define replay_bh_schedule_oneshot_event(ctx, bh, acb) aio_bh_schedule_oneshot(ctx, bh, acb) + #define aio_bh_schedule_oneshot(ctx, cb, opaque) \ + aio_bh_schedule_oneshot_full((ctx), (cb), (opaque), (stringify(cb))) + +diff --git a/include/block/trace.h b/include/block/trace.h +new file mode 100644 +index 00000000..d55cc5eb +--- /dev/null ++++ b/include/block/trace.h +@@ -0,0 +1,12 @@ ++/* qcow2/lib/block.c */ ++#define trace_bdrv_open_common(...) do {} while(0) ++#define trace_bdrv_lock_medium(...) do {} while(0) ++/* qcow2/lib/blockdev.c */ ++#define trace_qmp_block_stream(...) do {} while(0) ++#define trace_qmp_block_job_cancel(...) do {} while(0) ++#define trace_qmp_block_job_pause(...) do {} while(0) ++#define trace_qmp_block_job_resume(...) do {} while(0) ++#define trace_qmp_block_job_complete(...) do {} while(0) ++#define trace_qmp_block_job_finalize(...) do {} while(0) ++#define trace_qmp_block_job_dismiss(...) do {} while(0) ++/* qcow2/lib/blockjob.c */ +diff --git a/include/compiler.h b/include/compiler.h +index 57ab2367..9ab73c76 100644 +--- a/include/compiler.h ++++ b/include/compiler.h +@@ -32,9 +32,11 @@ + #define _BLKTAP_COMPILER_H + + #ifdef __GNUC__ ++#ifndef likely + #define likely(_cond) __builtin_expect(!!(_cond), 1) + #define unlikely(_cond) __builtin_expect(!!(_cond), 0) + #endif ++#endif + + #ifndef likely + #define likely(_cond) (_cond) +diff --git a/include/crypto/hash.h b/include/crypto/hash.h +index 54d87aa2..1178ad29 100644 +--- a/include/crypto/hash.h ++++ b/include/crypto/hash.h +@@ -21,9 +21,27 @@ + #ifndef QCRYPTO_HASH_H + #define QCRYPTO_HASH_H + +-#include "qapi/qapi-types-crypto.h" ++//#include "qapi/qapi-types-crypto.h" + + /* See also "QCryptoHashAlgorithm" defined in qapi/crypto.json */ ++typedef enum QCryptoHashAlgorithm { ++ QCRYPTO_HASH_ALG_MD5 = 0, ++ QCRYPTO_HASH_ALG_SHA1 = 1, ++ QCRYPTO_HASH_ALG_SHA224 = 2, ++ QCRYPTO_HASH_ALG_SHA256 = 3, ++ QCRYPTO_HASH_ALG_SHA384 = 4, ++ QCRYPTO_HASH_ALG_SHA512 = 5, ++ QCRYPTO_HASH_ALG_RIPEMD160 = 6, ++ QCRYPTO_HASH_ALG__MAX = 7, ++} QCryptoHashAlgorithm; ++ ++#define QCRYPTO_HASH_DIGEST_LEN_MD5 16 ++#define QCRYPTO_HASH_DIGEST_LEN_SHA1 20 ++#define QCRYPTO_HASH_DIGEST_LEN_SHA224 28 ++#define QCRYPTO_HASH_DIGEST_LEN_SHA256 32 ++#define QCRYPTO_HASH_DIGEST_LEN_SHA384 48 ++#define QCRYPTO_HASH_DIGEST_LEN_SHA512 64 ++#define QCRYPTO_HASH_DIGEST_LEN_RIPEMD160 20 + + /** + * qcrypto_hash_supports: +diff --git a/include/hw/block/block.h b/include/hw/block/block.h +index de3946a5..7d533173 100644 +--- a/include/hw/block/block.h ++++ b/include/hw/block/block.h +@@ -13,7 +13,7 @@ + + #include "exec/hwaddr.h" + #include "qapi/qapi-types-block-core.h" +-#include "hw/qdev-properties-system.h" ++//#include "hw/qdev-properties-system.h" + + /* Configuration */ + +diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h +index 77bfcbdf..524fab7b 100644 +--- a/include/hw/qdev-core.h ++++ b/include/hw/qdev-core.h +@@ -7,8 +7,8 @@ + #include "qemu/rcu.h" + #include "qemu/rcu_queue.h" + #include "qom/object.h" +-#include "hw/hotplug.h" +-#include "hw/resettable.h" ++//#include "hw/hotplug.h" ++//#include "hw/resettable.h" + + /** + * DOC: The QEMU Device API +@@ -278,10 +278,12 @@ struct DeviceState { + * needed for migration + */ + int alias_required_for_version; ++#if 0 + /** + * @reset: ResettableState for the device; handled by Resettable interface. + */ + ResettableState reset; ++#endif + /** + * @unplug_blockers: list of reasons to block unplugging of device + */ +@@ -376,7 +378,9 @@ struct BusState { + /* public: */ + DeviceState *parent; + char *name; ++#if 0 + HotplugHandler *hotplug_handler; ++#endif + int max_index; + bool realized; + bool full; +@@ -391,10 +395,12 @@ struct BusState { + * @sibling: next bus + */ + BusStateEntry sibling; ++#if 0 + /** + * @reset: ResettableState for the bus; handled by Resettable interface. + */ + ResettableState reset; ++#endif + }; + + /** +@@ -531,8 +537,10 @@ bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp); + void qdev_unrealize(DeviceState *dev); + void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, + int required_for_version); ++#if 0 + HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); + HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); ++#endif + bool qdev_hotplug_allowed(DeviceState *dev, Error **errp); + + /** +@@ -545,10 +553,12 @@ bool qdev_hotplug_allowed(DeviceState *dev, Error **errp); + * Return: pointer to object that implements TYPE_HOTPLUG_HANDLER interface + * or NULL if there aren't any. + */ ++#if 0 + HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev); + void qdev_unplug(DeviceState *dev, Error **errp); + void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp); ++#endif + void qdev_machine_creation_done(void); + bool qdev_machine_modified(void); + +@@ -1014,6 +1024,7 @@ extern bool qdev_hot_removed; + + char *qdev_get_dev_path(DeviceState *dev); + ++#if 0 + void qbus_set_hotplug_handler(BusState *bus, Object *handler); + void qbus_set_bus_hotplug_handler(BusState *bus); + +@@ -1032,6 +1043,7 @@ static inline bool qbus_is_hotpluggable(BusState *bus) + } + return ret; + } ++#endif + + /** + * qbus_mark_full: Mark this bus as full, so no more devices can be attached +diff --git a/include/list.h b/include/list.h +index 01923d91..874e88a3 100644 +--- a/include/list.h ++++ b/include/list.h +@@ -211,11 +211,13 @@ list_del_init(struct list_head *node) + #define container_off(containing_type, member) \ + offsetof(containing_type, member) + ++#ifndef container_of + #define container_of(member_ptr, containing_type, member) \ + ((containing_type *) \ + ((char *)(member_ptr) \ + - container_off(containing_type, member)) \ + + check_types_match(*(member_ptr), ((containing_type *)0)->member)) ++#endif + + #define list_entry(PTR, TYPE, FIELD) container_of(PTR, TYPE, FIELD) + #define list_first_entry(PTR, TYPE, FIELD) \ +diff --git a/include/qapi/qapi-commands-block-core.h b/include/qapi/qapi-commands-block-core.h +index e698e23c..84c2dfa9 100644 +--- a/include/qapi/qapi-commands-block-core.h ++++ b/include/qapi/qapi-commands-block-core.h +@@ -14,9 +14,9 @@ + #define QAPI_COMMANDS_BLOCK_CORE_H + + #include "qapi-commands-common.h" +-#include "qapi-commands-crypto.h" +-#include "qapi-commands-job.h" +-#include "qapi-commands-sockets.h" ++//#include "qapi-commands-crypto.h" ++//#include "qapi-commands-job.h" ++//#include "qapi-commands-sockets.h" + #include "qapi-types-block-core.h" + + BlockInfoList *qmp_query_block(Error **errp); +diff --git a/include/qapi/qapi-commands-block.h b/include/qapi/qapi-commands-block.h +index 2c1adc60..4f072d47 100644 +--- a/include/qapi/qapi-commands-block.h ++++ b/include/qapi/qapi-commands-block.h +@@ -13,7 +13,7 @@ + #ifndef QAPI_COMMANDS_BLOCK_H + #define QAPI_COMMANDS_BLOCK_H + +-#include "qapi-commands-block-core.h" ++#include "qapi/qapi-commands-block-core.h" + #include "qapi-types-block.h" + + PRManagerInfoList *qmp_query_pr_managers(Error **errp); +diff --git a/include/qapi/qapi-events-block-core.h b/include/qapi/qapi-events-block-core.h +index 7f12beff..e9830b25 100644 +--- a/include/qapi/qapi-events-block-core.h ++++ b/include/qapi/qapi-events-block-core.h +@@ -14,9 +14,9 @@ + #define QAPI_EVENTS_BLOCK_CORE_H + + #include "qapi-events-common.h" +-#include "qapi-events-crypto.h" ++//#include "qapi-events-crypto.h" + #include "qapi-events-job.h" +-#include "qapi-events-sockets.h" ++//#include "qapi-events-sockets.h" + #include "qapi/util.h" + #include "qapi-types-block-core.h" + +diff --git a/include/qapi/qapi-types-block-core.h b/include/qapi/qapi-types-block-core.h +index 26585893..4a3960c6 100644 +--- a/include/qapi/qapi-types-block-core.h ++++ b/include/qapi/qapi-types-block-core.h +@@ -14,10 +14,10 @@ + #define QAPI_TYPES_BLOCK_CORE_H + + #include "qapi/qapi-builtin-types.h" +-#include "qapi-types-common.h" +-#include "qapi-types-crypto.h" ++#include "qapi/qapi-types-common.h" ++//#include "qapi-types-crypto.h" + #include "qapi-types-job.h" +-#include "qapi-types-sockets.h" ++//#include "qapi-types-sockets.h" + + typedef struct SnapshotInfo SnapshotInfo; + +@@ -41,9 +41,11 @@ typedef struct ImageInfoSpecificFile ImageInfoSpecificFile; + + typedef enum ImageInfoSpecificKind { + IMAGE_INFO_SPECIFIC_KIND_QCOW2, ++#if 0 + IMAGE_INFO_SPECIFIC_KIND_VMDK, + IMAGE_INFO_SPECIFIC_KIND_LUKS, + IMAGE_INFO_SPECIFIC_KIND_RBD, ++#endif + IMAGE_INFO_SPECIFIC_KIND_FILE, + IMAGE_INFO_SPECIFIC_KIND__MAX, + } ImageInfoSpecificKind; +@@ -1017,10 +1019,12 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2EncryptionBase, qapi_free_Im + struct ImageInfoSpecificQCow2Encryption { + /* Members inherited from ImageInfoSpecificQCow2EncryptionBase: */ + BlockdevQcow2EncryptionFormat format; ++#if 0 + /* Own members: */ + union { /* union tag is @format */ + QCryptoBlockInfoLUKS luks; + } u; ++#endif + }; + + static inline ImageInfoSpecificQCow2EncryptionBase *qapi_ImageInfoSpecificQCow2Encryption_base(const ImageInfoSpecificQCow2Encryption *obj) +@@ -1114,6 +1118,7 @@ struct ImageInfoSpecificQCow2Wrapper { + void qapi_free_ImageInfoSpecificQCow2Wrapper(ImageInfoSpecificQCow2Wrapper *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2Wrapper, qapi_free_ImageInfoSpecificQCow2Wrapper) + ++#if 0 + struct ImageInfoSpecificVmdkWrapper { + ImageInfoSpecificVmdk *data; + }; +@@ -1134,6 +1139,7 @@ struct ImageInfoSpecificRbdWrapper { + + void qapi_free_ImageInfoSpecificRbdWrapper(ImageInfoSpecificRbdWrapper *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificRbdWrapper, qapi_free_ImageInfoSpecificRbdWrapper) ++#endif + + struct ImageInfoSpecificFileWrapper { + ImageInfoSpecificFile *data; +@@ -1150,9 +1156,11 @@ struct ImageInfoSpecific { + ImageInfoSpecificKind type; + union { /* union tag is @type */ + ImageInfoSpecificQCow2Wrapper qcow2; ++#if 0 + ImageInfoSpecificVmdkWrapper vmdk; + ImageInfoSpecificLUKSWrapper luks; + ImageInfoSpecificRbdWrapper rbd; ++#endif + ImageInfoSpecificFileWrapper file; + } u; + }; +@@ -2347,9 +2355,11 @@ struct q_obj_BlockdevQcowEncryption_base { + + struct BlockdevQcowEncryption { + BlockdevQcowEncryptionFormat format; ++#if 0 + union { /* union tag is @format */ + QCryptoBlockOptionsQCow aes; + } u; ++#endif + }; + + void qapi_free_BlockdevQcowEncryption(BlockdevQcowEncryption *obj); +@@ -2377,10 +2387,12 @@ struct q_obj_BlockdevQcow2Encryption_base { + + struct BlockdevQcow2Encryption { + BlockdevQcow2EncryptionFormat format; ++#if 0 + union { /* union tag is @format */ + QCryptoBlockOptionsQCow aes; + QCryptoBlockOptionsLUKS luks; + } u; ++#endif + }; + + void qapi_free_BlockdevQcow2Encryption(BlockdevQcow2Encryption *obj); +@@ -2442,6 +2454,7 @@ static inline BlockdevOptionsGenericCOWFormat *qapi_BlockdevOptionsQcow2_base(co + void qapi_free_BlockdevOptionsQcow2(BlockdevOptionsQcow2 *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsQcow2, qapi_free_BlockdevOptionsQcow2) + ++#if 0 + struct SshHostKeyHash { + SshHostKeyCheckHashType type; + char *hash; +@@ -2575,6 +2588,7 @@ struct BlockdevOptionsBlkreplay { + + void qapi_free_BlockdevOptionsBlkreplay(BlockdevOptionsBlkreplay *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsBlkreplay, qapi_free_BlockdevOptionsBlkreplay) ++#endif + + struct BlockdevRefList { + BlockdevRefList *next; +@@ -2598,6 +2612,7 @@ struct BlockdevOptionsQuorum { + void qapi_free_BlockdevOptionsQuorum(BlockdevOptionsQuorum *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsQuorum, qapi_free_BlockdevOptionsQuorum) + ++#if 0 + struct BlockdevOptionsGluster { + char *volume; + char *path; +@@ -2609,6 +2624,7 @@ struct BlockdevOptionsGluster { + + void qapi_free_BlockdevOptionsGluster(BlockdevOptionsGluster *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsGluster, qapi_free_BlockdevOptionsGluster) ++#endif + + #if defined(CONFIG_BLKIO) + struct BlockdevOptionsIoUring { +@@ -2683,6 +2699,7 @@ struct BlockdevOptionsIscsi { + void qapi_free_BlockdevOptionsIscsi(BlockdevOptionsIscsi *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsIscsi, qapi_free_BlockdevOptionsIscsi) + ++#if 0 + struct RbdEncryptionOptionsLUKSBase { + char *key_secret; + }; +@@ -2848,6 +2865,7 @@ struct BlockdevOptionsReplication { + char *top_id; + }; + #endif /* defined(CONFIG_REPLICATION) */ ++#endif + + #if defined(CONFIG_REPLICATION) + static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsReplication_base(const BlockdevOptionsReplication *obj) +@@ -2998,6 +3016,7 @@ static inline BlockdevOptionsCurlBase *qapi_BlockdevOptionsCurlFtps_base(const B + void qapi_free_BlockdevOptionsCurlFtps(BlockdevOptionsCurlFtps *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCurlFtps, qapi_free_BlockdevOptionsCurlFtps) + ++#if 0 + struct BlockdevOptionsNbd { + SocketAddress *server; + char *export; +@@ -3012,6 +3031,7 @@ struct BlockdevOptionsNbd { + + void qapi_free_BlockdevOptionsNbd(BlockdevOptionsNbd *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsNbd, qapi_free_BlockdevOptionsNbd) ++#endif + + struct BlockdevOptionsRaw { + /* Members inherited from BlockdevOptionsGenericFormat: */ +@@ -3105,6 +3125,7 @@ struct BlockdevOptions { + bool has_detect_zeroes; + BlockdevDetectZeroesOptions detect_zeroes; + union { /* union tag is @driver */ ++#if 0 + BlockdevOptionsBlkdebug blkdebug; + BlockdevOptionsBlklogwrites blklogwrites; + BlockdevOptionsBlkverify blkverify; +@@ -3115,16 +3136,20 @@ struct BlockdevOptions { + BlockdevOptionsCbw copy_before_write; + BlockdevOptionsCor copy_on_read; + BlockdevOptionsGenericFormat dmg; ++#endif + BlockdevOptionsFile file; ++#if 0 + BlockdevOptionsCurlFtp ftp; + BlockdevOptionsCurlFtps ftps; + BlockdevOptionsGluster gluster; ++#endif + #if defined(HAVE_HOST_BLOCK_DEVICE) + BlockdevOptionsFile host_cdrom; + #endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ + #if defined(HAVE_HOST_BLOCK_DEVICE) + BlockdevOptionsFile host_device; + #endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if 0 + BlockdevOptionsCurlHttp http; + BlockdevOptionsCurlHttps https; + #if defined(CONFIG_BLKIO) +@@ -3141,8 +3166,10 @@ struct BlockdevOptions { + BlockdevOptionsNvmeIoUring nvme_io_uring; + #endif /* defined(CONFIG_BLKIO) */ + BlockdevOptionsGenericFormat parallels; ++#endif + BlockdevOptionsPreallocate preallocate; + BlockdevOptionsQcow2 qcow2; ++#if 0 + BlockdevOptionsQcow qcow; + BlockdevOptionsGenericCOWFormat qed; + BlockdevOptionsQuorum quorum; +@@ -3168,6 +3195,7 @@ struct BlockdevOptions { + BlockdevOptionsGenericCOWFormat vmdk; + BlockdevOptionsGenericFormat vpc; + BlockdevOptionsVVFAT vvfat; ++#endif + } u; + }; + +@@ -3227,6 +3255,7 @@ struct BlockdevCreateOptionsFile { + void qapi_free_BlockdevCreateOptionsFile(BlockdevCreateOptionsFile *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsFile, qapi_free_BlockdevCreateOptionsFile) + ++#if 0 + struct BlockdevCreateOptionsGluster { + BlockdevOptionsGluster *location; + uint64_t size; +@@ -3295,6 +3324,7 @@ struct BlockdevCreateOptionsQcow { + + void qapi_free_BlockdevCreateOptionsQcow(BlockdevCreateOptionsQcow *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsQcow, qapi_free_BlockdevCreateOptionsQcow) ++#endif + + struct BlockdevCreateOptionsQcow2 { + BlockdevRef *file; +@@ -3309,7 +3339,9 @@ struct BlockdevCreateOptionsQcow2 { + char *backing_file; + bool has_backing_fmt; + BlockdevDriver backing_fmt; ++#if 0 + QCryptoBlockCreateOptions *encrypt; ++#endif + bool has_cluster_size; + uint64_t cluster_size; + bool has_preallocation; +@@ -3424,12 +3456,15 @@ struct BlockdevCreateOptions { + BlockdevDriver driver; + union { /* union tag is @driver */ + BlockdevCreateOptionsFile file; ++#if 0 + BlockdevCreateOptionsGluster gluster; + BlockdevCreateOptionsLUKS luks; + BlockdevCreateOptionsNfs nfs; + BlockdevCreateOptionsParallels parallels; + BlockdevCreateOptionsQcow qcow; ++#endif + BlockdevCreateOptionsQcow2 qcow2; ++#if 0 + BlockdevCreateOptionsQed qed; + BlockdevCreateOptionsRbd rbd; + BlockdevCreateOptionsSsh ssh; +@@ -3437,6 +3472,7 @@ struct BlockdevCreateOptions { + BlockdevCreateOptionsVhdx vhdx; + BlockdevCreateOptionsVmdk vmdk; + BlockdevCreateOptionsVpc vpc; ++#endif + } u; + }; + +@@ -3448,6 +3484,7 @@ struct q_obj_blockdev_create_arg { + BlockdevCreateOptions *options; + }; + ++#if 0 + struct BlockdevAmendOptionsLUKS { + /* Members inherited from QCryptoBlockAmendOptionsLUKS: */ + QCryptoBlockLUKSKeyslotState state; +@@ -3468,9 +3505,12 @@ static inline QCryptoBlockAmendOptionsLUKS *qapi_BlockdevAmendOptionsLUKS_base(c + + void qapi_free_BlockdevAmendOptionsLUKS(BlockdevAmendOptionsLUKS *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevAmendOptionsLUKS, qapi_free_BlockdevAmendOptionsLUKS) ++#endif + + struct BlockdevAmendOptionsQcow2 { ++#if 0 + QCryptoBlockAmendOptions *encrypt; ++#endif + }; + + void qapi_free_BlockdevAmendOptionsQcow2(BlockdevAmendOptionsQcow2 *obj); +@@ -3483,7 +3523,9 @@ struct q_obj_BlockdevAmendOptions_base { + struct BlockdevAmendOptions { + BlockdevDriver driver; + union { /* union tag is @driver */ ++#if 0 + BlockdevAmendOptionsLUKS luks; ++#endif + BlockdevAmendOptionsQcow2 qcow2; + } u; + }; +@@ -3573,12 +3615,14 @@ struct q_obj_x_blockdev_change_arg { + char *node; + }; + ++#if 0 + struct q_obj_x_blockdev_set_iothread_arg { + char *node_name; + StrOrNull *iothread; + bool has_force; + bool force; + }; ++#endif + + struct q_obj_QUORUM_FAILURE_arg { + char *reference; +diff --git a/include/qapi/qapi-visit-block-core.h b/include/qapi/qapi-visit-block-core.h +index 9462b281..e689a244 100644 +--- a/include/qapi/qapi-visit-block-core.h ++++ b/include/qapi/qapi-visit-block-core.h +@@ -17,9 +17,9 @@ + #include "qapi-types-block-core.h" + + #include "qapi-visit-common.h" +-#include "qapi-visit-crypto.h" +-#include "qapi-visit-job.h" +-#include "qapi-visit-sockets.h" ++//#include "qapi-visit-crypto.h" ++#include "qapi/qapi-visit-job.h" ++//#include "qapi-visit-sockets.h" + + bool visit_type_SnapshotInfo_members(Visitor *v, SnapshotInfo *obj, Error **errp); + +diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h +index f2e95681..0a0c8361 100644 +--- a/include/qapi/qmp/dispatch.h ++++ b/include/qapi/qmp/dispatch.h +@@ -14,7 +14,7 @@ + #ifndef QAPI_QMP_DISPATCH_H + #define QAPI_QMP_DISPATCH_H + +-#include "monitor/monitor.h" ++//#include "monitor/monitor.h" + #include "qemu/queue.h" + + typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); +diff --git a/include/qcow2.h b/include/qcow2.h +index a9e3481c..12b3315b 100644 +--- a/include/qcow2.h ++++ b/include/qcow2.h +@@ -25,7 +25,7 @@ + #ifndef BLOCK_QCOW2_H + #define BLOCK_QCOW2_H + +-#include "crypto/block.h" ++//#include "crypto/block.h" + #include "qemu/coroutine.h" + #include "qemu/units.h" + #include "block/block_int.h" +@@ -360,8 +360,10 @@ typedef struct BDRVQcow2State { + CoMutex lock; + + Qcow2CryptoHeaderExtension crypto_header; /* QCow2 header extension */ ++#if 0 + QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ + QCryptoBlock *crypto; /* Disk encryption format driver */ ++#endif + bool crypt_physical_offset; /* Whether to use virtual or physical offset + for encryption initialization vector tweak */ + uint32_t crypt_method_header; +diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h +index 7a3f2e65..9e392dcd 100644 +--- a/include/qemu/atomic.h ++++ b/include/qemu/atomic.h +@@ -15,7 +15,7 @@ + #ifndef QEMU_ATOMIC_H + #define QEMU_ATOMIC_H + +-#include "compiler.h" ++#include "qemu/compiler.h" + + /* Compiler barrier */ + #define barrier() ({ asm volatile("" ::: "memory"); (void)0; }) +diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h +index 2c0a2fe7..69c31b5b 100644 +--- a/include/qemu/bitops.h ++++ b/include/qemu/bitops.h +@@ -12,9 +12,8 @@ + #ifndef BITOPS_H + #define BITOPS_H + +- +-#include "host-utils.h" +-#include "atomic.h" ++#include "qemu/host-utils.h" ++#include "qemu/atomic.h" + + #define BITS_PER_BYTE CHAR_BIT + #define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE) +diff --git a/include/qemu/config-host.h b/include/qemu/config-host.h +new file mode 100644 +index 00000000..c0b00bf6 +--- /dev/null ++++ b/include/qemu/config-host.h +@@ -0,0 +1,495 @@ ++/* ++ * Autogenerated by the Meson build system. ++ * Do not edit, your changes will be lost. ++ */ ++ ++#pragma once ++ ++#define CONFIG_ACCEPT4 ++ ++#undef CONFIG_AF_ALG ++ ++#define CONFIG_AF_VSOCK ++ ++#undef CONFIG_AF_XDP ++ ++#undef CONFIG_ALIGNED_MALLOC ++ ++#undef CONFIG_ARM_AES_BUILTIN ++ ++#define CONFIG_ASAN_IFACE_FIBER ++ ++#undef CONFIG_ASM_HWPROBE_H ++ ++#undef CONFIG_ATOMIC128 ++ ++#undef CONFIG_ATOMIC128_OPT ++ ++#define CONFIG_ATOMIC64 ++ ++#define CONFIG_ATTR ++ ++#define CONFIG_AUDIO_ALSA ++ ++#undef CONFIG_AUDIO_COREAUDIO ++ ++#define CONFIG_AUDIO_DRIVERS "pa", "oss", ++ ++#undef CONFIG_AUDIO_DSOUND ++ ++#undef CONFIG_AUDIO_JACK ++ ++#define CONFIG_AUDIO_OSS ++ ++#define CONFIG_AUDIO_PA ++ ++#define CONFIG_AUDIO_PIPEWIRE ++ ++#define CONFIG_AUDIO_SDL ++ ++#undef CONFIG_AUDIO_SNDIO ++ ++#define CONFIG_AVX2_OPT ++ ++#define CONFIG_AVX512BW_OPT ++ ++#define CONFIG_BDRV_RO_WHITELIST ++ ++#define CONFIG_BDRV_RW_WHITELIST ++ ++#undef CONFIG_BDRV_WHITELIST_TOOLS ++ ++#define CONFIG_BINDIR "/usr/local/bin" ++ ++#undef CONFIG_BLKIO ++ ++//#define CONFIG_BLKZONED ++ ++#undef CONFIG_BRLAPI ++ ++#undef CONFIG_BSD ++ ++#define CONFIG_CAPSTONE ++ ++#undef CONFIG_CFI ++ ++#define CONFIG_CLOCK_ADJTIME ++ ++#define CONFIG_CLOSE_RANGE ++ ++#define CONFIG_CMPXCHG128 ++ ++#undef CONFIG_COCOA ++ ++#define CONFIG_COROUTINE_POOL ++ ++#define CONFIG_CPUID_H ++ ++#undef CONFIG_CRYPTO_SM4 ++ ++#define CONFIG_CURL ++ ++#define CONFIG_CURSES ++ ++#undef CONFIG_DARWIN ++ ++#define CONFIG_DBUS_DISPLAY ++ ++#define CONFIG_DEBUG_GRAPH_LOCK ++ ++#define CONFIG_DEBUG_MUTEX ++ ++#undef CONFIG_DEBUG_REMAP ++ ++#undef CONFIG_DEBUG_STACK_USAGE ++ ++//#define CONFIG_DEBUG_TCG ++ ++#define CONFIG_DUP3 ++ ++#undef CONFIG_EBPF ++ ++#undef CONFIG_ELF_AUX_INFO ++ ++#define CONFIG_EPOLL ++ ++#define CONFIG_EPOLL_CREATE1 ++ ++#define CONFIG_EVENTFD ++ ++#define CONFIG_FALLOCATE ++ ++#define CONFIG_FALLOCATE_PUNCH_HOLE ++ ++#define CONFIG_FALLOCATE_ZERO_RANGE ++ ++#define CONFIG_FDATASYNC ++ ++#define CONFIG_FDT ++ ++#define CONFIG_FIEMAP ++ ++#undef CONFIG_FREEBSD ++ ++#define CONFIG_FSFREEZE ++ ++#define CONFIG_FSTRIM ++ ++#define CONFIG_FUSE ++ ++#define CONFIG_FUSE_LSEEK ++ ++#undef CONFIG_FUZZ ++ ++#define CONFIG_GBM ++ ++#undef CONFIG_GCOV ++ ++#undef CONFIG_GCRYPT ++ ++#define CONFIG_GETAUXVAL ++ ++#define CONFIG_GETCPU ++ ++#define CONFIG_GETRANDOM ++ ++#define CONFIG_GETTID ++ ++#define CONFIG_GIO ++ ++#undef CONFIG_GLUSTERFS ++ ++#define CONFIG_GNUTLS ++ ++#define CONFIG_GNUTLS_CRYPTO ++ ++#define CONFIG_GTK ++ ++#undef CONFIG_GTK_CLIPBOARD ++ ++#define CONFIG_HEXAGON_IDEF_PARSER ++ ++#undef CONFIG_HOGWEED ++ ++#define CONFIG_HOST_DSOSUF ".so" ++ ++#define CONFIG_IASL "/usr/bin/iasl" ++ ++#define CONFIG_INOTIFY ++ ++#define CONFIG_INOTIFY1 ++ ++#define CONFIG_INT128 ++ ++#define CONFIG_INT128_TYPE ++ ++#define CONFIG_IOVEC ++ ++#define CONFIG_KEYUTILS ++ ++#define CONFIG_KVM_TARGETS "i386-softmmu" ,"x86_64-softmmu" ++ ++#define CONFIG_L2TPV3 ++ ++#undef CONFIG_LIBATTR ++ ++#define CONFIG_LIBCAP_NG ++ ++#undef CONFIG_LIBDAXCTL ++ ++#define CONFIG_LIBDW ++ ++#undef CONFIG_LIBISCSI ++ ++#undef CONFIG_LIBNFS ++ ++#undef CONFIG_LIBPMEM ++ ++#undef CONFIG_LIBSSH ++ ++#define CONFIG_LIBUDEV ++ ++#define CONFIG_LINUX ++ ++#define CONFIG_LINUX_AIO ++ ++#undef CONFIG_LINUX_IO_URING ++ ++#define CONFIG_LINUX_MAGIC_H ++ ++#define CONFIG_LZO ++ ++#define CONFIG_MADVISE ++ ++#define CONFIG_MALLOC_TRIM ++ ++#define CONFIG_MEMALIGN ++ ++#undef CONFIG_MEMBARRIER ++ ++#define CONFIG_MEMFD ++ ++#undef CONFIG_MODULES ++ ++#undef CONFIG_MODULE_UPGRADES ++ ++#undef CONFIG_MPATH ++ ++#undef CONFIG_NETMAP ++ ++#undef CONFIG_NETTLE ++ ++#undef CONFIG_NUMA ++ ++#define CONFIG_OPENGL ++ ++#define CONFIG_OPEN_BY_HANDLE ++ ++#define CONFIG_PIXMAN ++ ++#define CONFIG_PLUGIN ++ ++#define CONFIG_PNG ++ ++#define CONFIG_POSIX ++ ++#define CONFIG_POSIX_FALLOCATE ++ ++#define CONFIG_POSIX_MADVISE ++ ++#define CONFIG_POSIX_MEMALIGN ++ ++#define CONFIG_PPOLL ++ ++#define CONFIG_PRCTL_PR_SET_TIMERSLACK ++ ++#define CONFIG_PREADV ++ ++#define CONFIG_PREFIX "/usr/local" ++ ++#define CONFIG_PTHREAD_AFFINITY_NP ++ ++#undef CONFIG_PTHREAD_CONDATTR_SETCLOCK ++ ++#undef CONFIG_PTHREAD_FCHDIR_NP ++ ++#undef CONFIG_PTHREAD_SETNAME_NP_WO_TID ++ ++#define CONFIG_PTHREAD_SETNAME_NP_W_TID ++ ++#undef CONFIG_PTHREAD_SET_NAME_NP ++ ++#define CONFIG_QEMU_CONFDIR "/usr/local/etc/qemu" ++ ++#define CONFIG_QEMU_DATADIR "/usr/local/share/qemu" ++ ++#define CONFIG_QEMU_DESKTOPDIR "/usr/local/share/applications" ++ ++#define CONFIG_QEMU_FIRMWAREPATH "/usr/local/share/qemu-firmware", ++ ++#define CONFIG_QEMU_HELPERDIR "/usr/local/libexec" ++ ++#define CONFIG_QEMU_ICONDIR "/usr/local/share/icons" ++ ++#define CONFIG_QEMU_LOCALEDIR "/usr/local/share/locale" ++ ++#define CONFIG_QEMU_LOCALSTATEDIR "/var/local" ++ ++#define CONFIG_QEMU_MODDIR "/usr/local/lib64/qemu" ++ ++#undef CONFIG_QEMU_PRIVATE_XTS ++ ++#define CONFIG_QOM_CAST_DEBUG ++ ++#undef CONFIG_QPL ++ ++#undef CONFIG_RBD ++ ++#undef CONFIG_RDMA ++ ++#define CONFIG_RELOCATABLE ++ ++#define CONFIG_REPLICATION ++ ++#define CONFIG_RTNETLINK ++ ++#undef CONFIG_SAFESTACK ++ ++#undef CONFIG_SCHED_GETCPU ++ ++#define CONFIG_SDL ++ ++#undef CONFIG_SDL_IMAGE ++ ++#define CONFIG_SECCOMP ++ ++#define CONFIG_SECCOMP_SYSRAWRC ++ ++#define CONFIG_SECRET_KEYRING ++ ++#undef CONFIG_SELINUX ++ ++#define CONFIG_SENDFILE ++ ++#define CONFIG_SETNS ++ ++#define CONFIG_SIGNALFD ++ ++#define CONFIG_SLIRP ++ ++#define CONFIG_SMBD_COMMAND "/usr/sbin/smbd" ++ ++#define CONFIG_SNAPPY ++ ++#undef CONFIG_SOLARIS ++ ++#undef CONFIG_SPICE ++ ++#undef CONFIG_SPICE_PROTOCOL ++ ++#define CONFIG_SPLICE ++ ++#define CONFIG_STATX ++ ++#define CONFIG_STATX_MNT_ID ++ ++#define CONFIG_SYNCFS ++ ++#define CONFIG_SYNC_FILE_RANGE ++ ++#define CONFIG_SYSCONFDIR "/usr/local/etc" ++ ++#define CONFIG_SYSMACROS ++ ++#define CONFIG_TASN1 ++ ++//#define CONFIG_TCG 1 ++ ++//#undef CONFIG_TCG_INTERPRETER ++ ++#define CONFIG_TIMERFD ++ ++#define CONFIG_TLS_PRIORITY "NORMAL" ++ ++#define CONFIG_TPM ++ ++#define CONFIG_TRACE_FILE "trace" ++ ++#define CONFIG_TRACE_LOG ++ ++#undef CONFIG_TSAN ++ ++#undef CONFIG_UADK ++ ++#undef CONFIG_USBFS ++ ++#define CONFIG_USB_LIBUSB ++ ++//#define CONFIG_VALGRIND_H ++ ++#define CONFIG_VALLOC ++ ++#undef CONFIG_VDE ++ ++#define CONFIG_VDUSE_BLK_EXPORT ++ ++#define CONFIG_VHOST ++ ++#define CONFIG_VHOST_CRYPTO ++ ++#define CONFIG_VHOST_KERNEL ++ ++#define CONFIG_VHOST_NET ++ ++#define CONFIG_VHOST_NET_USER ++ ++#define CONFIG_VHOST_NET_VDPA ++ ++#define CONFIG_VHOST_USER ++ ++#define CONFIG_VHOST_USER_BLK_SERVER ++ ++#define CONFIG_VHOST_VDPA ++ ++#define CONFIG_VIRTFS ++ ++#undef CONFIG_VMNET ++ ++#define CONFIG_VNC ++ ++#define CONFIG_VNC_JPEG ++ ++#define CONFIG_VNC_SASL ++ ++#define CONFIG_VTE ++ ++#undef CONFIG_WIN32 ++ ++#define CONFIG_X11 ++ ++#define CONFIG_XEN_BACKEND ++ ++#define CONFIG_XEN_CTRL_INTERFACE_VERSION 41700 ++ ++#define CONFIG_XKBCOMMON ++ ++#define CONFIG_ZSTD ++ ++#define HAVE_BLK_ZONE_REP_CAPACITY ++ ++#undef HAVE_BROKEN_SIZE_MAX ++ ++#define HAVE_BTRFS_H ++ ++#define HAVE_COPY_FILE_RANGE ++ ++#define HAVE_DRM_H ++ ++#define HAVE_FSXATTR ++ ++#define HAVE_GETIFADDRS ++ ++#undef HAVE_GLIB_WITH_SLICE_ALLOCATOR ++ ++#define HAVE_HOST_BLOCK_DEVICE ++ ++#undef HAVE_IPPROTO_MPTCP ++ ++#undef HAVE_MADVISE_WITHOUT_PROTOTYPE ++ ++#define HAVE_MLOCKALL ++ ++#define HAVE_OPENPTY ++ ++#undef HAVE_OPTRESET ++ ++#define HAVE_PTY_H ++ ++#undef HAVE_SIGEV_NOTIFY_THREAD_ID ++ ++#define HAVE_STRCHRNUL ++ ++#define HAVE_STRUCT_STAT_ST_ATIM ++ ++#define HAVE_SYSTEM_FUNCTION ++ ++#undef HAVE_SYS_DISK_H ++ ++#undef HAVE_SYS_IOCCOM_H ++ ++#undef HAVE_SYS_KCOV_H ++ ++#define HAVE_UTMPX ++ ++#undef HAVE_VSS_SDK ++ ++#define HOST_X86_64 1 ++ ++#define QEMU_VERSION "9.1.1" ++ ++#define QEMU_VERSION_MAJOR 9 ++ ++#define QEMU_VERSION_MICRO 1 ++ ++#define QEMU_VERSION_MINOR 1 ++ +diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h +index 8136e336..ef8eb355 100644 +--- a/include/qemu/hbitmap.h ++++ b/include/qemu/hbitmap.h +@@ -12,8 +12,8 @@ + #ifndef HBITMAP_H + #define HBITMAP_H + +-#include "bitops.h" +-#include "host-utils.h" ++#include "qemu/bitops.h" ++#include "qemu/host-utils.h" + + typedef struct HBitmap HBitmap; + typedef struct HBitmapIter HBitmapIter; +diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h +index ead97d35..12ae37af 100644 +--- a/include/qemu/host-utils.h ++++ b/include/qemu/host-utils.h +@@ -31,7 +31,7 @@ + #define HOST_UTILS_H + + #include "qemu/bswap.h" +-#include "qemu/int128.h" ++//#include "qemu/int128.h" + + #ifdef CONFIG_INT128 + static inline void mulu64(uint64_t *plow, uint64_t *phigh, +@@ -856,6 +856,6 @@ static inline uint64_t udiv_qrnnd(uint64_t *r, uint64_t n1, + #endif + } + +-Int128 divu256(Int128 *plow, Int128 *phigh, Int128 divisor); +-Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor); ++//Int128 divu256(Int128 *plow, Int128 *phigh, Int128 divisor); ++//Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor); + #endif +diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h +index 5764db15..feab3b96 100644 +--- a/include/qemu/main-loop.h ++++ b/include/qemu/main-loop.h +@@ -26,11 +26,12 @@ + #define QEMU_MAIN_LOOP_H + + #include "block/aio.h" +-#include "qom/object.h" +-#include "sysemu/event-loop-base.h" ++//#include "qom/object.h" ++//#include "sysemu/event-loop-base.h" + + #define SIG_IPI SIGUSR1 + ++#if 0 + #define TYPE_MAIN_LOOP "main-loop" + OBJECT_DECLARE_TYPE(MainLoop, MainLoopClass, MAIN_LOOP) + +@@ -38,6 +39,9 @@ struct MainLoop { + EventLoopBase parent_obj; + }; + typedef struct MainLoop MainLoop; ++#endif ++ ++void qemu_init_cpu_loop(void); + + /** + * qemu_init_main_loop: Set up the process so that it can run the main loop. +diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h +index fe7c3c5f..bdc90246 100644 +--- a/include/qemu/osdep.h ++++ b/include/qemu/osdep.h +@@ -35,7 +35,7 @@ + #ifdef COMPILING_PER_TARGET + #include CONFIG_TARGET + #else +-#include "exec/poison.h" ++//#include "exec/poison.h" + #endif + + /* +diff --git a/include/sysemu/block-backend-common.h b/include/sysemu/block-backend-common.h +index 780cea73..a8af5e5c 100644 +--- a/include/sysemu/block-backend-common.h ++++ b/include/sysemu/block-backend-common.h +@@ -14,7 +14,7 @@ + #define BLOCK_BACKEND_COMMON_H + + #include "qemu/iov.h" +-#include "block/throttle-groups.h" ++//#include "block/throttle-groups.h" + + /* + * TODO Have to include block/block.h for a bunch of block layer +@@ -97,7 +97,9 @@ typedef struct BlockDevOps { + * friends so that BlockBackends can be kept in lists outside block-backend.c + */ + typedef struct BlockBackendPublic { ++#if 0 + ThrottleGroupMember throttle_group_member; ++#endif + } BlockBackendPublic; + + #endif /* BLOCK_BACKEND_COMMON_H */ +diff --git a/include/sysemu/block-backend-global-state.h b/include/sysemu/block-backend-global-state.h +index 49c12b0f..c4d5e87b 100644 +--- a/include/sysemu/block-backend-global-state.h ++++ b/include/sysemu/block-backend-global-state.h +@@ -117,7 +117,9 @@ int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size); + int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz); + int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo); + ++#if 0 + void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg); ++#endif + void blk_io_limits_disable(BlockBackend *blk); + void blk_io_limits_enable(BlockBackend *blk, const char *group); + void blk_io_limits_update_group(BlockBackend *blk, const char *group); +diff --git a/include/trace.h b/include/trace.h +new file mode 100644 +index 00000000..d1b57619 +--- /dev/null ++++ b/include/trace.h +@@ -0,0 +1,191 @@ ++/* qcow2/lib/system/cpus.c */ ++#define trace_vm_stop_flush_all(...) do {} while(0) ++/* qcow2/lib/hw/block/hd-geometry.c */ ++#define trace_hd_geometry_lchs_guess(...) do {} while(0) ++#define trace_hd_geometry_guess(...) do {} while(0) ++/* qcow2/lib/qapi/qapi-visit-core.c */ ++#define trace_visit_complete(...) do {} while(0) ++#define trace_visit_free(...) do {} while(0) ++#define trace_visit_start_struct(...) do {} while(0) ++#define trace_visit_check_struct(...) do {} while(0) ++#define trace_visit_end_struct(...) do {} while(0) ++#define trace_visit_start_list(...) do {} while(0) ++#define trace_visit_next_list(...) do {} while(0) ++#define trace_visit_check_list(...) do {} while(0) ++#define trace_visit_end_list(...) do {} while(0) ++#define trace_visit_start_alternate(...) do {} while(0) ++#define trace_visit_end_alternate(...) do {} while(0) ++#define trace_visit_optional(...) do {} while(0) ++#define trace_visit_policy_reject(...) do {} while(0) ++#define trace_visit_policy_skip(...) do {} while(0) ++#define trace_visit_type_int(...) do {} while(0) ++#define trace_visit_type_uint8(...) do {} while(0) ++#define trace_visit_type_uint16(...) do {} while(0) ++#define trace_visit_type_uint32(...) do {} while(0) ++#define trace_visit_type_uint64(...) do {} while(0) ++#define trace_visit_type_int8(...) do {} while(0) ++#define trace_visit_type_int16(...) do {} while(0) ++#define trace_visit_type_int32(...) do {} while(0) ++#define trace_visit_type_int64(...) do {} while(0) ++#define trace_visit_type_size(...) do {} while(0) ++#define trace_visit_type_bool(...) do {} while(0) ++#define trace_visit_type_str(...) do {} while(0) ++#define trace_visit_type_number(...) do {} while(0) ++#define trace_visit_type_any(...) do {} while(0) ++#define trace_visit_type_null(...) do {} while(0) ++#define trace_visit_type_enum(...) do {} while(0) ++/* qcow2/lib/io/channel-file.c */ ++#define trace_qio_channel_file_new_fd(...) do {} while(0) ++#define trace_qio_channel_file_new_path(...) do {} while(0) ++/* qcow2/lib/io/channel-socket.c */ ++#define trace_qio_channel_socket_new(...) do {} while(0) ++#define trace_qio_channel_socket_new_fd(...) do {} while(0) ++#define trace_qio_channel_socket_connect_sync(...) do {} while(0) ++#define trace_qio_channel_socket_connect_fail(...) do {} while(0) ++#define trace_qio_channel_socket_connect_complete(...) do {} while(0) ++#define trace_qio_channel_socket_connect_async(...) do {} while(0) ++#define trace_qio_channel_socket_listen_sync(...) do {} while(0) ++#define trace_qio_channel_socket_listen_fail(...) do {} while(0) ++#define trace_qio_channel_socket_listen_complete(...) do {} while(0) ++#define trace_qio_channel_socket_listen_async(...) do {} while(0) ++#define trace_qio_channel_socket_dgram_sync(...) do {} while(0) ++#define trace_qio_channel_socket_dgram_fail(...) do {} while(0) ++#define trace_qio_channel_socket_dgram_complete(...) do {} while(0) ++#define trace_qio_channel_socket_dgram_async(...) do {} while(0) ++#define trace_qio_channel_socket_accept(...) do {} while(0) ++#define trace_qio_channel_socket_accept_fail(...) do {} while(0) ++#define trace_qio_channel_socket_accept_complete(...) do {} while(0) ++/* qcow2/lib/io/task.c */ ++#define trace_qio_task_new(...) do {} while(0) ++#define trace_qio_task_thread_result(...) do {} while(0) ++#define trace_qio_task_thread_run(...) do {} while(0) ++#define trace_qio_task_thread_exit(...) do {} while(0) ++#define trace_qio_task_thread_source_attach(...) do {} while(0) ++#define trace_qio_task_thread_start(...) do {} while(0) ++#define trace_qio_task_thread_source_cancel(...) do {} while(0) ++#define trace_qio_task_complete(...) do {} while(0) ++/* qcow2/lib/util/aio-posix.c */ ++#define trace_poll_add(...) do {} while(0) ++#define trace_poll_remove(...) do {} while(0) ++#define trace_run_poll_handlers_begin(...) do {} while(0) ++#define trace_run_poll_handlers_end(...) do {} while(0) ++#define trace_poll_shrink(...) do {} while(0) ++#define trace_poll_grow(...) do {} while(0) ++/* qcow2/lib/util/async.c */ ++#define trace_reentrant_aio(...) do {} while(0) ++#define trace_aio_co_schedule_bh_cb(...) do {} while(0) ++#define trace_aio_co_schedule(...) do {} while(0) ++/* qcow2/lib/util/hbitmap.c */ ++#define trace_hbitmap_iter_skip_words(...) do {} while(0) ++#define trace_hbitmap_set(...) do {} while(0) ++#define trace_hbitmap_reset(...) do {} while(0) ++/* qcow2/lib/util/lockcnt.c */ ++#define trace_lockcnt_fast_path_attempt(...) do {} while(0) ++#define trace_lockcnt_fast_path_success(...) do {} while(0) ++#define trace_lockcnt_futex_wait_prepare(...) do {} while(0) ++#define trace_lockcnt_futex_wait(...) do {} while(0) ++#define trace_lockcnt_futex_wait_resume(...) do {} while(0) ++#define trace_lockcnt_futex_wake(...) do {} while(0) ++#define trace_lockcnt_unlock_attempt(...) do {} while(0) ++#define trace_lockcnt_unlock_success(...) do {} while(0) ++/* qcow2/lib/util/memalign.c */ ++#define trace_qemu_memalign(...) do {} while(0) ++#define trace_qemu_vfree(...) do {} while(0) ++/* qcow2/lib/util/module.c */ ++#define trace_module_load_module(...) do {} while(0) ++#define trace_module_lookup_object_type(...) do {} while(0) ++/* qcow2/lib/util/oslib-posix.c */ ++#define trace_qemu_anon_ram_alloc(...) do {} while(0) ++#define trace_qemu_anon_ram_free(...) do {} while(0) ++/* qcow2/lib/util/qemu-coroutine-lock.c */ ++#define trace_qemu_co_mutex_lock_entry(...) do {} while(0) ++#define trace_qemu_co_mutex_lock_return(...) do {} while(0) ++#define trace_qemu_co_mutex_lock_uncontended(...) do {} while(0) ++#define trace_qemu_co_mutex_unlock_entry(...) do {} while(0) ++#define trace_qemu_co_mutex_unlock_return(...) do {} while(0) ++/* qcow2/lib/util/qemu-coroutine.c */ ++#define trace_qemu_aio_coroutine_enter(...) do {} while(0) ++#define trace_qemu_coroutine_terminate(...) do {} while(0) ++#define trace_qemu_coroutine_yield(...) do {} while(0) ++/* qcow2/lib/util/qemu-sockets.c */ ++#define trace_socket_listen(...) do {} while(0) ++/* qcow2/lib/util/qemu-thread-common.h */ ++#define trace_qemu_mutex_lock(...) do {} while(0) ++#define trace_qemu_mutex_locked(...) do {} while(0) ++#define trace_qemu_mutex_unlock(...) do {} while(0) ++/* qcow2/lib/util/thread-pool.c */ ++#define trace_thread_pool_complete(...) do {} while(0) ++#define trace_thread_pool_cancel(...) do {} while(0) ++#define trace_thread_pool_submit(...) do {} while(0) ++/* qcow2/lib/authz/base.c */ ++#define trace_qauthz_is_allowed(...) do {} while(0) ++/* qcow2/lib/block/block-backend.c */ ++#define trace_blk_root_attach(...) do {} while(0) ++#define trace_blk_root_detach(...) do {} while(0) ++#define trace_blk_co_preadv(...) do {} while(0) ++#define trace_blk_co_pwritev(...) do {} while(0) ++/* qcow2/lib/block/commit.c */ ++#define trace_commit_one_iteration(...) do {} while(0) ++#define trace_commit_start(...) do {} while(0) ++/* qcow2/lib/block/dirty-bitmap.c */ ++/* qcow2/lib/block/file-posix.c */ ++#define trace_file_flush_fdatasync_failed(...) do {} while(0) ++#define trace_file_copy_file_range(...) do {} while(0) ++#define trace_zbd_zone_append_complete(...) do {} while(0) ++#define trace_zbd_zone_report(...) do {} while(0) ++#define trace_zbd_zone_mgmt(...) do {} while(0) ++#define trace_zbd_zone_append(...) do {} while(0) ++#define trace_file_FindEjectableOpticalMedia(...) do {} while(0) ++#define trace_file_setup_cdrom(...) do {} while(0) ++#define trace_file_hdev_is_sg(...) do {} while(0) ++/* qcow2/lib/block/io.c */ ++#define trace_bdrv_co_do_copy_on_readv(...) do {} while(0) ++#define trace_bdrv_co_preadv_part(...) do {} while(0) ++#define trace_bdrv_co_pwritev_part(...) do {} while(0) ++#define trace_bdrv_co_pwrite_zeroes(...) do {} while(0) ++#define trace_bdrv_co_copy_range_from(...) do {} while(0) ++#define trace_bdrv_co_copy_range_to(...) do {} while(0) ++/* qcow2/lib/block/mirror.c */ ++#define trace_mirror_iteration_done(...) do {} while(0) ++#define trace_mirror_yield_in_flight(...) do {} while(0) ++#define trace_mirror_one_iteration(...) do {} while(0) ++#define trace_mirror_restart_iter(...) do {} while(0) ++#define trace_mirror_yield(...) do {} while(0) ++#define trace_mirror_before_flush(...) do {} while(0) ++#define trace_mirror_before_drain(...) do {} while(0) ++#define trace_mirror_before_sleep(...) do {} while(0) ++#define trace_mirror_start(...) do {} while(0) ++/* qcow2/lib/qom/object.c */ ++#define trace_object_dynamic_cast_assert(...) do {} while(0) ++#define trace_object_class_dynamic_cast_assert(...) do {} while(0) ++/* qcow2/lib/qcow2-cache.c */ ++#define trace_qcow2_cache_entry_flush(...) do {} while(0) ++#define trace_qcow2_cache_flush(...) do {} while(0) ++#define trace_qcow2_cache_get(...) do {} while(0) ++#define trace_qcow2_cache_get_done(...) do {} while(0) ++#define trace_qcow2_cache_get_read(...) do {} while(0) ++#define trace_qcow2_cache_get_replace_entry(...) do {} while(0) ++/* qcow2/lib/qcow2-cluster.c */ ++#define trace_qcow2_l2_allocate(...) do {} while(0) ++#define trace_qcow2_l2_allocate_get_empty(...) do {} while(0) ++#define trace_qcow2_l2_allocate_write_l2(...) do {} while(0) ++#define trace_qcow2_l2_allocate_write_l1(...) do {} while(0) ++#define trace_qcow2_l2_allocate_done(...) do {} while(0) ++#define trace_qcow2_cluster_link_l2(...) do {} while(0) ++#define trace_qcow2_handle_copied(...) do {} while(0) ++#define trace_qcow2_do_alloc_clusters_offset(...) do {} while(0) ++#define trace_qcow2_cluster_alloc_phys(...) do {} while(0) ++#define trace_qcow2_handle_alloc(...) do {} while(0) ++#define trace_qcow2_alloc_clusters_offset(...) do {} while(0) ++/* qcow2/lib/qcow2-refcount.c */ ++#define trace_qcow2_process_discards_failed_region(...) do {} while(0) ++/* qcow2/lib/qcow2.c */ ++#define trace_qcow2_add_task(...) do {} while(0) ++#define trace_qcow2_skip_cow(...) do {} while(0) ++#define trace_qcow2_writev_data(...) do {} while(0) ++#define trace_qcow2_writev_start_req(...) do {} while(0) ++#define trace_qcow2_writev_start_part(...) do {} while(0) ++#define trace_qcow2_writev_done_part(...) do {} while(0) ++#define trace_qcow2_writev_done_req(...) do {} while(0) ++#define trace_qcow2_pwrite_zeroes_start_req(...) do {} while(0) ++#define trace_qcow2_pwrite_zeroes(...) do {} while(0) +diff --git a/include/trace/trace-root.h b/include/trace/trace-root.h +new file mode 100644 +index 00000000..68037e50 +--- /dev/null ++++ b/include/trace/trace-root.h +@@ -0,0 +1,11 @@ ++/* qcow2/lib/job-qmp.c */ ++#define trace_qmp_job_cancel(...) do {} while(0) ++#define trace_qmp_job_pause(...) do {} while(0) ++#define trace_qmp_job_resume(...) do {} while(0) ++#define trace_qmp_job_complete(...) do {} while(0) ++#define trace_qmp_job_finalize(...) do {} while(0) ++#define trace_qmp_job_dismiss(...) do {} while(0) ++/* qcow2/lib/job.c */ ++#define trace_job_state_transition(...) do {} while(0) ++#define trace_job_apply_verb(...) do {} while(0) ++#define trace_job_completed(...) do {} while(0) +diff --git a/mk/blktap.spec.in b/mk/blktap.spec.in +index 2da6fe2c..fd1a4714 100644 +--- a/mk/blktap.spec.in ++++ b/mk/blktap.spec.in +@@ -11,6 +11,7 @@ BuildRoot: %{_tmppath}/%{name}-%{release}-buildroot + Obsoletes: xen-blktap + BuildRequires: e2fsprogs-devel, libaio-devel, systemd, autogen, autoconf, automake, libtool, libuuid-devel + BuildRequires: xen-devel, kernel-headers, xen-dom0-libs-devel, zlib-devel, xen-libs-devel, libcmocka-devel, lcov, git ++BuildRequires: glib2, glib2-devel, libzstd, libzstd-devel + BuildRequires: openssl-devel >= 1.1.1 + Requires(post): systemd + Requires(preun): systemd +diff --git a/qcow2/Makefile.am b/qcow2/Makefile.am +new file mode 100644 +index 00000000..69460e08 +--- /dev/null ++++ b/qcow2/Makefile.am +@@ -0,0 +1,21 @@ ++ ++SUBDIRS = lib ++ ++AM_CFLAGS = -Wall ++AM_CFLAGS += -Werror ++AM_CFLAGS += $(if $(GCOV),-fprofile-dir=/tmp/coverage/blktap/qcow2 -fprofile-arcs -ftest-coverage) ++ ++AM_CPPFLAGS = -D_GNU_SOURCE ++AM_CPPFLAGS += -I$(top_srcdir)/include ++AM_CPPFLAGS += $(GLIB_CFLAGS) ++ ++bin_PROGRAMS = qemu-img ++ ++qemu_img_LDADD = lib/libqcow2.la ++ ++qemu_img_SOURCES = qemu-img.c ++qemu_img_SOURCES += qemu-config.c ++qemu_img_SOURCES += qemu-progress.c ++ ++clean-local: ++ -rm -rf *.gc?? +diff --git a/qcow2/lib/Makefile.am b/qcow2/lib/Makefile.am +new file mode 100644 +index 00000000..55a007d9 +--- /dev/null ++++ b/qcow2/lib/Makefile.am +@@ -0,0 +1,159 @@ ++AUTOMAKE_OPTIONS = subdir-objects ++ ++SUBDIRS = . ++#$(MAYBE_test) ++ ++AM_CFLAGS = -Wall ++AM_CFLAGS += -Werror ++AM_CFLAGS += $(if $(GCOV),-fprofile-dir=/tmp/coverage/blktap/qcow2/lib -fprofile-arcs -ftest-coverage) ++ ++AM_CPPFLAGS = -D_GNU_SOURCE ++AM_CPPFLAGS += -I$(top_srcdir)/include ++AM_CPPFLAGS += -I$(top_srcdir)/lvm ++AM_CPPFLAGS += -I$(top_srcdir)/part ++AM_CPPFLAGS += $(GLIB_CFLAGS) ++AM_CPPFLAGS += $(ZLIB_CFLAGS) ++AM_CPPFLAGS += $(GNUTLS_CFLAGS) ++ ++lib_LTLIBRARIES = libqcow2.la ++lib_LTLIBRARIES += $(MAYBE_libqcow2io_la) ++ ++libqcow2_la_SOURCES = qcow2.c ++libqcow2_la_SOURCES += qcow2-refcount.c ++libqcow2_la_SOURCES += qcow2-cache.c ++libqcow2_la_SOURCES += qcow2-cluster.c ++libqcow2_la_SOURCES += qcow2-bitmap.c ++libqcow2_la_SOURCES += qcow2-threads.c ++libqcow2_la_SOURCES += qcow2-snapshot.c ++libqcow2_la_SOURCES += authz/base.c ++libqcow2_la_SOURCES += block.c ++libqcow2_la_SOURCES += blockdev.c ++libqcow2_la_SOURCES += blockjob.c ++libqcow2_la_SOURCES += block/accounting.c ++libqcow2_la_SOURCES += block/aio_task.c ++libqcow2_la_SOURCES += block/block-backend.c ++libqcow2_la_SOURCES += block/block-gen.c ++libqcow2_la_SOURCES += block/commit.c ++libqcow2_la_SOURCES += block/dirty-bitmap.c ++libqcow2_la_SOURCES += block/file-posix.c ++libqcow2_la_SOURCES += block/graph-lock.c ++libqcow2_la_SOURCES += block/io.c ++libqcow2_la_SOURCES += block/linux-aio.c ++libqcow2_la_SOURCES += block/mirror.c ++libqcow2_la_SOURCES += block/monitor/bitmap-qmp-cmds.c ++libqcow2_la_SOURCES += block/progress_meter.c ++libqcow2_la_SOURCES += block/qapi.c ++libqcow2_la_SOURCES += block/raw-format.c ++libqcow2_la_SOURCES += block/snapshot.c ++libqcow2_la_SOURCES += hw/block/block.c ++libqcow2_la_SOURCES += hw/block/hd-geometry.c ++libqcow2_la_SOURCES += io/channel.c ++libqcow2_la_SOURCES += io/channel-file.c ++libqcow2_la_SOURCES += io/channel-socket.c ++libqcow2_la_SOURCES += io/channel-util.c ++libqcow2_la_SOURCES += io/channel-watch.c ++libqcow2_la_SOURCES += io/task.c ++libqcow2_la_SOURCES += job.c ++libqcow2_la_SOURCES += job-qmp.c ++libqcow2_la_SOURCES += qapi/qapi-clone-visitor.c ++libqcow2_la_SOURCES += qapi/qapi-dealloc-visitor.c ++libqcow2_la_SOURCES += qapi/qapi-events-block-core.c ++libqcow2_la_SOURCES += qapi/qapi-events-job.c ++libqcow2_la_SOURCES += qapi/qapi-types-block-core.c ++libqcow2_la_SOURCES += qapi/qapi-types-crypto.c ++libqcow2_la_SOURCES += qapi/qapi-types-common.c ++libqcow2_la_SOURCES += qapi/qapi-types-job.c ++libqcow2_la_SOURCES += qapi/qapi-types-sockets.c ++libqcow2_la_SOURCES += qapi/qapi-types-yank.c ++libqcow2_la_SOURCES += qapi/qapi-util.c ++libqcow2_la_SOURCES += qapi/qapi-visit-block-core.c ++libqcow2_la_SOURCES += qapi/qapi-visit-common.c ++libqcow2_la_SOURCES += qapi/qapi-visit-crypto.c ++libqcow2_la_SOURCES += qapi/qapi-visit-core.c ++libqcow2_la_SOURCES += qapi/qapi-visit-job.c ++libqcow2_la_SOURCES += qapi/qapi-visit-sockets.c ++libqcow2_la_SOURCES += qapi/qapi-visit-yank.c ++libqcow2_la_SOURCES += qapi/qmp-dispatch.c ++libqcow2_la_SOURCES += qapi/qmp-event.c ++libqcow2_la_SOURCES += qapi/qobject-input-visitor.c ++libqcow2_la_SOURCES += qapi/qobject-output-visitor.c ++libqcow2_la_SOURCES += qapi/string-input-visitor.c ++libqcow2_la_SOURCES += qapi/string-output-visitor.c ++libqcow2_la_SOURCES += qobject/block-qdict.c ++libqcow2_la_SOURCES += qobject/json-lexer.c ++libqcow2_la_SOURCES += qobject/json-parser.c ++libqcow2_la_SOURCES += qobject/json-streamer.c ++libqcow2_la_SOURCES += qobject/json-writer.c ++libqcow2_la_SOURCES += qobject/qbool.c ++libqcow2_la_SOURCES += qobject/qdict.c ++libqcow2_la_SOURCES += qobject/qjson.c ++libqcow2_la_SOURCES += qobject/qlist.c ++libqcow2_la_SOURCES += qobject/qnull.c ++libqcow2_la_SOURCES += qobject/qnum.c ++libqcow2_la_SOURCES += qobject/qobject.c ++libqcow2_la_SOURCES += qobject/qstring.c ++libqcow2_la_SOURCES += qom/object.c ++libqcow2_la_SOURCES += qom/container.c ++libqcow2_la_SOURCES += qom/qom-qobject.c ++libqcow2_la_SOURCES += system/cpus.c ++libqcow2_la_SOURCES += util/aio-posix.c ++libqcow2_la_SOURCES += util/aio-wait.c ++libqcow2_la_SOURCES += util/aiocb.c ++libqcow2_la_SOURCES += util/async.c ++libqcow2_la_SOURCES += util/bitmap.c ++libqcow2_la_SOURCES += util/bitops.c ++libqcow2_la_SOURCES += util/bufferiszero.c ++libqcow2_la_SOURCES += util/compatfd.c ++libqcow2_la_SOURCES += util/coroutine-ucontext.c ++libqcow2_la_SOURCES += util/cutils.c ++libqcow2_la_SOURCES += util/defer-call.c ++libqcow2_la_SOURCES += util/error.c ++libqcow2_la_SOURCES += util/error-report.c ++libqcow2_la_SOURCES += util/event_notifier-posix.c ++libqcow2_la_SOURCES += util/fdmon-epoll.c ++libqcow2_la_SOURCES += util/fdmon-poll.c ++libqcow2_la_SOURCES += util/hbitmap.c ++libqcow2_la_SOURCES += util/host-utils.c ++libqcow2_la_SOURCES += util/id.c ++libqcow2_la_SOURCES += util/iov.c ++libqcow2_la_SOURCES += util/lockcnt.c ++libqcow2_la_SOURCES += util/main-loop.c ++libqcow2_la_SOURCES += util/memalign.c ++libqcow2_la_SOURCES += util/module.c ++libqcow2_la_SOURCES += util/notify.c ++libqcow2_la_SOURCES += util/osdep.c ++libqcow2_la_SOURCES += util/oslib-posix.c ++libqcow2_la_SOURCES += util/qemu-coroutine.c ++libqcow2_la_SOURCES += util/qemu-coroutine-lock.c ++libqcow2_la_SOURCES += util/qemu-option.c ++libqcow2_la_SOURCES += util/qemu-print.c ++libqcow2_la_SOURCES += util/qemu-sockets.c ++libqcow2_la_SOURCES += util/qemu-thread-posix.c ++libqcow2_la_SOURCES += util/qemu-timer-common.c ++libqcow2_la_SOURCES += util/qemu-timer.c ++libqcow2_la_SOURCES += util/qsp.c ++libqcow2_la_SOURCES += util/range.c ++libqcow2_la_SOURCES += util/rcu.c ++libqcow2_la_SOURCES += util/stats64.c ++libqcow2_la_SOURCES += util/thread-pool.c ++libqcow2_la_SOURCES += util/timed-average.c ++libqcow2_la_SOURCES += util/transactions.c ++libqcow2_la_SOURCES += util/unicode.c ++libqcow2_la_SOURCES += util/uri.c ++libqcow2_la_SOURCES += util/yank.c ++ ++ ++libqcow2_la_LDFLAGS = -version-info 1:1:1 ++ ++libqcow2_la_LIBADD = -lpthread -luuid -laio -ldl $(LIBICONV) $(top_srcdir)/lvm/liblvmutil.la ++libqcow2_la_LIBADD += $(GLIB_LIBS) ++libqcow2_la_LIBADD += $(ZLIB_LIBS) ++libqcow2_la_LIBADD += $(GNUTLS_LIBS) ++libqcow2_la_LIBADD += $(LIBZSTD_LIBS) ++ ++#if ENABLE_TESTS ++#MAYBE_test = test ++#endif ++ ++clean-local: ++ -rm -rf *.gc?? +diff --git a/qcow2/lib/block.c b/qcow2/lib/block.c +index c317de9e..de666676 100644 +--- a/qcow2/lib/block.c ++++ b/qcow2/lib/block.c +@@ -28,8 +28,8 @@ + #include "block/block_int.h" + #include "block/blockjob.h" + #include "block/dirty-bitmap.h" +-#include "block/fuse.h" +-#include "block/nbd.h" ++//#include "block/fuse.h" ++//#include "block/nbd.h" + #include "block/qdict.h" + #include "qemu/error-report.h" + #include "block/module_block.h" +@@ -5114,7 +5114,6 @@ static void GRAPH_UNLOCKED bdrv_reopen_abort(BDRVReopenState *reopen_state) + } + } + +- + static void bdrv_close(BlockDriverState *bs) + { + BdrvAioNotifier *ban, *ban_next; +@@ -5180,6 +5179,7 @@ static void bdrv_close(BlockDriverState *bs) + } + } + ++#if 0 + void bdrv_close_all(void) + { + GLOBAL_STATE_CODE(); +@@ -5194,6 +5194,7 @@ void bdrv_close_all(void) + + assert(QTAILQ_EMPTY(&all_bdrv_states)); + } ++#endif + + static bool GRAPH_RDLOCK should_update_child(BdrvChild *c, BlockDriverState *to) + { +@@ -5423,6 +5424,7 @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, + return bdrv_replace_node_common(from, to, true, false, errp); + } + ++#if 0 + int bdrv_drop_filter(BlockDriverState *bs, Error **errp) + { + BlockDriverState *child_bs; +@@ -5442,6 +5444,7 @@ int bdrv_drop_filter(BlockDriverState *bs, Error **errp) + + return ret; + } ++#endif + + /* + * Add new bs contents at the top of an image chain while the chain is +@@ -5499,6 +5502,7 @@ out: + return ret; + } + ++#if 0 + /* Not for empty child */ + int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, + Error **errp) +@@ -5531,6 +5535,7 @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, + + return ret; + } ++#endif + + static void bdrv_delete(BlockDriverState *bs) + { +@@ -5551,7 +5556,7 @@ static void bdrv_delete(BlockDriverState *bs) + g_free(bs); + } + +- ++#if 0 + /* + * Replace @bs by newly created block node. + * +@@ -5625,6 +5630,7 @@ fail: + bdrv_unref(new_node_bs); + return NULL; + } ++#endif + + /* + * Run consistency checks on an image +@@ -6113,6 +6119,7 @@ bool bdrv_is_sg(BlockDriverState *bs) + return bs->sg; + } + ++#if 0 + /** + * Return whether the given node supports compressed writes. + */ +@@ -6136,6 +6143,7 @@ bool bdrv_supports_compressed_writes(BlockDriverState *bs) + + return true; + } ++#endif + + const char *bdrv_get_format_name(BlockDriverState *bs) + { +@@ -6226,6 +6234,7 @@ BlockDriverState *bdrv_find_node(const char *node_name) + return NULL; + } + ++#if 0 + /* Put this QMP function here so it can access the static graph_bdrv_states. */ + BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, + Error **errp) +@@ -6382,6 +6391,7 @@ XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp) + + return xdbg_graph_finalize(gr); + } ++#endif + + BlockDriverState *bdrv_lookup_bs(const char *device, + const char *node_name, +@@ -7425,6 +7435,7 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs) + return bs ? bs->aio_context : qemu_get_aio_context(); + } + ++#if 0 + AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs) + { + Coroutine *self = qemu_coroutine_self(); +@@ -7449,6 +7460,7 @@ void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx) + aio_co_reschedule_self(old_ctx); + bdrv_dec_in_flight(bs); + } ++#endif + + static void bdrv_do_remove_aio_context_notifier(BdrvAioNotifier *ban) + { +diff --git a/qcow2/lib/block/accounting.c b/qcow2/lib/block/accounting.c +index 28297453..dea4f667 100644 +--- a/qcow2/lib/block/accounting.c ++++ b/qcow2/lib/block/accounting.c +@@ -27,7 +27,8 @@ + #include "block/accounting.h" + #include "block/block_int.h" + #include "qemu/timer.h" +-#include "sysemu/qtest.h" ++//#include "sysemu/qtest.h" ++#define qtest_enabled() 0 + + static QEMUClockType clock_type = QEMU_CLOCK_REALTIME; + static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000; +diff --git a/qcow2/lib/block/block-backend.c b/qcow2/lib/block/block-backend.c +index db6f9b92..0d08a07b 100644 +--- a/qcow2/lib/block/block-backend.c ++++ b/qcow2/lib/block/block-backend.c +@@ -15,18 +15,18 @@ + #include "block/block_int.h" + #include "block/blockjob.h" + #include "block/coroutines.h" +-#include "block/throttle-groups.h" +-#include "hw/qdev-core.h" +-#include "sysemu/blockdev.h" +-#include "sysemu/runstate.h" +-#include "sysemu/replay.h" ++//#include "block/throttle-groups.h" ++//#include "hw/qdev-core.h" ++//#include "sysemu/blockdev.h" ++//#include "sysemu/runstate.h" ++//#include "sysemu/replay.h" + #include "qapi/error.h" +-#include "qapi/qapi-events-block.h" ++//#include "qapi/qapi-events-block.h" + #include "qemu/id.h" + #include "qemu/main-loop.h" + #include "qemu/option.h" + #include "trace.h" +-#include "migration/misc.h" ++//#include "migration/misc.h" + + /* Number of coroutines to reserve per attached device model */ + #define COROUTINE_POOL_RESERVATION 64 +@@ -48,7 +48,7 @@ struct BlockBackend { + DriveInfo *legacy_dinfo; /* null unless created by drive_new() */ + QTAILQ_ENTRY(BlockBackend) link; /* for block_backends */ + QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ +- BlockBackendPublic public; ++ //BlockBackendPublic public; + + DeviceState *dev; /* attached device model, if any */ + const BlockDevOps *dev_ops; +@@ -163,6 +163,7 @@ static const char *blk_root_get_name(BdrvChild *child) + return blk_name(child->opaque); + } + ++#if 0 + static void blk_vm_state_changed(void *opaque, bool running, RunState state) + { + Error *local_err = NULL; +@@ -179,6 +180,7 @@ static void blk_vm_state_changed(void *opaque, bool running, RunState state) + error_report_err(local_err); + } + } ++#endif + + /* + * Notifies the user of the BlockBackend that migration has completed. qdev +@@ -216,6 +218,7 @@ static void GRAPH_RDLOCK blk_root_activate(BdrvChild *child, Error **errp) + } + blk->shared_perm = saved_shared_perm; + ++#if 0 + if (runstate_check(RUN_STATE_INMIGRATE)) { + /* Activation can happen when migration process is still active, for + * example when nbd_server_add is called during non-shared storage +@@ -226,6 +229,7 @@ static void GRAPH_RDLOCK blk_root_activate(BdrvChild *child, Error **errp) + } + return; + } ++#endif + + blk_set_perm_locked(blk, blk->perm, blk->shared_perm, &local_err); + if (local_err) { +@@ -480,16 +484,20 @@ static void blk_delete(BlockBackend *blk) + assert(!blk->refcnt); + assert(!blk->name); + assert(!blk->dev); ++#if 0 + if (blk->public.throttle_group_member.throttle_state) { + blk_io_limits_disable(blk); + } ++#endif + if (blk->root) { + blk_remove_bs(blk); + } ++#if 0 + if (blk->vmsh) { + qemu_del_vm_change_state_handler(blk->vmsh); + blk->vmsh = NULL; + } ++#endif + assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); + assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); + assert(QLIST_EMPTY(&blk->aio_notifiers)); +@@ -506,7 +514,7 @@ static void drive_info_del(DriveInfo *dinfo) + if (!dinfo) { + return; + } +- qemu_opts_del(dinfo->opts); ++ //qemu_opts_del(dinfo->opts); + g_free(dinfo); + } + +@@ -630,7 +638,7 @@ BlockDriverState *bdrv_next(BdrvNextIterator *it) + * BDSes that are attached to a BlockBackend here; they have been handled + * by the above block already */ + do { +- it->bs = bdrv_next_monitor_owned(it->bs); ++ it->bs = NULL /*bdrv_next_monitor_owned(it->bs)*/; + bs = it->bs; + } while (bs && bdrv_has_blk(bs)); + +@@ -672,6 +680,7 @@ void bdrv_next_cleanup(BdrvNextIterator *it) + bdrv_next_reset(it); + } + ++#if 0 + /* + * Add a BlockBackend into the list of backends referenced by the monitor, with + * the given @name acting as the handle for the monitor. +@@ -724,6 +733,7 @@ void monitor_remove_blk(BlockBackend *blk) + g_free(blk->name); + blk->name = NULL; + } ++#endif + + /* + * Return @blk's name, a non-null string. +@@ -806,6 +816,7 @@ bool bdrv_is_root_node(BlockDriverState *bs) + return true; + } + ++#if 0 + /* + * Return @blk's DriveInfo if any, else null. + */ +@@ -861,18 +872,20 @@ BlockBackend *blk_by_public(BlockBackendPublic *public) + GLOBAL_STATE_CODE(); + return container_of(public, BlockBackend, public); + } ++#endif + + /* + * Disassociates the currently associated BlockDriverState from @blk. + */ + void blk_remove_bs(BlockBackend *blk) + { +- ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ //ThrottleGroupMember *tgm = &blk->public.throttle_group_member; + BdrvChild *root; + + GLOBAL_STATE_CODE(); + + notifier_list_notify(&blk->remove_bs_notifiers, blk); ++#if 0 + if (tgm->throttle_state) { + BlockDriverState *bs = blk_bs(blk); + +@@ -887,6 +900,7 @@ void blk_remove_bs(BlockBackend *blk) + bdrv_drained_end(bs); + bdrv_unref(bs); + } ++#endif + + blk_update_root_state(blk); + +@@ -908,7 +922,7 @@ void blk_remove_bs(BlockBackend *blk) + */ + int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) + { +- ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ //ThrottleGroupMember *tgm = &blk->public.throttle_group_member; + + GLOBAL_STATE_CODE(); + bdrv_ref(bs); +@@ -923,14 +937,17 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) + } + + notifier_list_notify(&blk->insert_bs_notifiers, blk); ++#if 0 + if (tgm->throttle_state) { + throttle_group_detach_aio_context(tgm); + throttle_group_attach_aio_context(tgm, bdrv_get_aio_context(bs)); + } ++#endif + + return 0; + } + ++#if 0 + /* + * Change BlockDriverState associated with @blk. + */ +@@ -939,6 +956,7 @@ int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp) + GLOBAL_STATE_CODE(); + return bdrv_replace_child_bs(blk->root, new_bs, errp); + } ++#endif + + /* + * Sets the permission bitmasks that the user of the BlockBackend needs. +@@ -972,6 +990,7 @@ int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, + return blk_set_perm_locked(blk, perm, shared_perm, errp); + } + ++#if 0 + void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) + { + GLOBAL_STATE_CODE(); +@@ -1018,6 +1037,7 @@ void blk_detach_dev(BlockBackend *blk, DeviceState *dev) + blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort); + blk_unref(blk); + } ++#endif + + /* + * Return the device model attached to @blk if any, else null. +@@ -1032,18 +1052,19 @@ DeviceState *blk_get_attached_dev(BlockBackend *blk) + * device attached to the BlockBackend. */ + char *blk_get_attached_dev_id(BlockBackend *blk) + { +- DeviceState *dev = blk->dev; ++ //DeviceState *dev = blk->dev; + IO_CODE(); + +- if (!dev) { ++ //if (!dev) { + return g_strdup(""); +- } else if (dev->id) { +- return g_strdup(dev->id); +- } ++ //} else if (dev->id) { ++ // return g_strdup(dev->id); ++ //} + +- return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); ++ //return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); + } + ++#if 0 + /* + * Return the BlockBackend which has the device model @dev attached if it + * exists, else null. +@@ -1082,6 +1103,7 @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, + ops->drained_begin(opaque); + } + } ++#endif + + /* + * Notify @blk's attached device model of media change. +@@ -1111,7 +1133,7 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) + + if (tray_was_open != tray_is_open) { + char *id = blk_get_attached_dev_id(blk); +- qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open); ++ //qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open); + g_free(id); + } + } +@@ -1190,12 +1212,14 @@ static void blk_root_resize(BdrvChild *child) + } + } + ++#if 0 + void blk_iostatus_enable(BlockBackend *blk) + { + GLOBAL_STATE_CODE(); + blk->iostatus_enabled = true; + blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; + } ++#endif + + /* The I/O status is only enabled if the drive explicitly + * enables it _and_ the VM is configured to stop on errors */ +@@ -1214,6 +1238,7 @@ BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk) + return blk->iostatus; + } + ++#if 0 + void blk_iostatus_disable(BlockBackend *blk) + { + GLOBAL_STATE_CODE(); +@@ -1237,6 +1262,7 @@ void blk_iostatus_set_err(BlockBackend *blk, int error) + BLOCK_DEVICE_IO_STATUS_FAILED; + } + } ++#endif + + void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow) + { +@@ -1338,11 +1364,13 @@ blk_co_do_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes, + + bdrv_inc_in_flight(bs); + ++#if 0 + /* throttling disk I/O */ + if (blk->public.throttle_group_member.throttle_state) { + throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, + bytes, THROTTLE_READ); + } ++#endif + + ret = bdrv_co_preadv_part(blk->root, offset, bytes, qiov, qiov_offset, + flags); +@@ -1412,11 +1440,13 @@ blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, + } + + bdrv_inc_in_flight(bs); ++#if 0 + /* throttling disk I/O */ + if (blk->public.throttle_group_member.throttle_state) { + throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, + bytes, THROTTLE_WRITE); + } ++#endif + + if (!blk->enable_write_cache) { + flags |= BDRV_REQ_FUA; +@@ -1861,6 +1891,7 @@ int coroutine_fn blk_co_flush(BlockBackend *blk) + return ret; + } + ++#if 0 + static void coroutine_fn blk_aio_zone_report_entry(void *opaque) + { + BlkAioEmAIOCB *acb = opaque; +@@ -2058,6 +2089,7 @@ int coroutine_fn blk_co_zone_append(BlockBackend *blk, int64_t *offset, + blk_dec_in_flight(blk); + return ret; + } ++#endif + + void blk_drain(BlockBackend *blk) + { +@@ -2109,6 +2141,7 @@ BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read) + return is_read ? blk->on_read_error : blk->on_write_error; + } + ++#if 0 + BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, + int error) + { +@@ -2203,6 +2236,7 @@ bool blk_is_writable(BlockBackend *blk) + IO_CODE(); + return blk->perm & BLK_PERM_WRITE; + } ++#endif + + bool blk_is_sg(BlockBackend *blk) + { +@@ -2228,6 +2262,7 @@ void blk_set_enable_write_cache(BlockBackend *blk, bool wce) + blk->enable_write_cache = wce; + } + ++#if 0 + void blk_activate(BlockBackend *blk, Error **errp) + { + BlockDriverState *bs = blk_bs(blk); +@@ -2249,6 +2284,7 @@ void blk_activate(BlockBackend *blk, Error **errp) + bdrv_activate(bs, errp); + } + } ++#endif + + bool coroutine_fn blk_co_is_inserted(BlockBackend *blk) + { +@@ -2279,7 +2315,7 @@ void coroutine_fn blk_co_lock_medium(BlockBackend *blk, bool locked) + void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag) + { + BlockDriverState *bs = blk_bs(blk); +- char *id; ++ //char *id; + IO_CODE(); + GRAPH_RDLOCK_GUARD(); + +@@ -2289,12 +2325,15 @@ void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag) + + /* Whether or not we ejected on the backend, + * the frontend experienced a tray event. */ ++#if 0 + id = blk_get_attached_dev_id(blk); + qapi_event_send_device_tray_moved(blk_name(blk), id, + eject_flag); + g_free(id); ++#endif + } + ++#if 0 + int blk_get_flags(BlockBackend *blk) + { + BlockDriverState *bs = blk_bs(blk); +@@ -2354,6 +2393,7 @@ int blk_get_max_iov(BlockBackend *blk) + IO_CODE(); + return blk->root->bs->bl.max_iov; + } ++#endif + + void *blk_try_blockalign(BlockBackend *blk, size_t size) + { +@@ -2367,6 +2407,7 @@ void *blk_blockalign(BlockBackend *blk, size_t size) + return qemu_blockalign(blk ? blk_bs(blk) : NULL, size); + } + ++#if 0 + bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) + { + BlockDriverState *bs = blk_bs(blk); +@@ -2409,6 +2450,7 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason) + bdrv_op_unblock_all(bs, reason); + } + } ++#endif + + /** + * Return BB's current AioContext. Note that this context may change +@@ -2465,13 +2507,15 @@ static void blk_root_set_aio_ctx_commit(void *opaque) + BdrvStateBlkRootContext *s = opaque; + BlockBackend *blk = s->blk; + AioContext *new_context = s->new_ctx; +- ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ //ThrottleGroupMember *tgm = &blk->public.throttle_group_member; + + qatomic_set(&blk->ctx, new_context); ++#if 0 + if (tgm->throttle_state) { + throttle_group_detach_aio_context(tgm); + throttle_group_attach_aio_context(tgm, new_context); + } ++#endif + } + + static TransactionActionDrv set_blk_root_context = { +@@ -2614,6 +2658,7 @@ int coroutine_fn blk_co_truncate(BlockBackend *blk, int64_t offset, bool exact, + return bdrv_co_truncate(blk->root, offset, exact, prealloc, flags, errp); + } + ++#if 0 + int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, + int64_t pos, int size) + { +@@ -2645,6 +2690,7 @@ int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) + + return bdrv_load_vmstate(blk_bs(blk), buf, pos, size); + } ++#endif + + int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) + { +@@ -2681,6 +2727,7 @@ void blk_update_root_state(BlockBackend *blk) + blk->root_state.detect_zeroes = blk->root->bs->detect_zeroes; + } + ++#if 0 + /* + * Returns the detect-zeroes setting to be used for bdrv_open() of a + * BlockDriverState which is supposed to inherit the root state. +@@ -2780,11 +2827,12 @@ void blk_io_limits_update_group(BlockBackend *blk, const char *group) + blk_io_limits_disable(blk); + blk_io_limits_enable(blk, group); + } ++#endif + + static void blk_root_drained_begin(BdrvChild *child) + { + BlockBackend *blk = child->opaque; +- ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ //ThrottleGroupMember *tgm = &blk->public.throttle_group_member; + + if (qatomic_fetch_inc(&blk->quiesce_counter) == 0) { + if (blk->dev_ops && blk->dev_ops->drained_begin) { +@@ -2795,9 +2843,11 @@ static void blk_root_drained_begin(BdrvChild *child) + /* Note that blk->root may not be accessible here yet if we are just + * attaching to a BlockDriverState that is drained. Use child instead. */ + ++#if 0 + if (qatomic_fetch_inc(&tgm->io_limits_disabled) == 0) { + throttle_group_restart_tgm(tgm); + } ++#endif + } + + static bool blk_root_drained_poll(BdrvChild *child) +@@ -2817,8 +2867,8 @@ static void blk_root_drained_end(BdrvChild *child) + BlockBackend *blk = child->opaque; + assert(qatomic_read(&blk->quiesce_counter)); + +- assert(blk->public.throttle_group_member.io_limits_disabled); +- qatomic_dec(&blk->public.throttle_group_member.io_limits_disabled); ++ //assert(blk->public.throttle_group_member.io_limits_disabled); ++ //qatomic_dec(&blk->public.throttle_group_member.io_limits_disabled); + + if (qatomic_fetch_dec(&blk->quiesce_counter) == 1) { + if (blk->dev_ops && blk->dev_ops->drained_end) { +diff --git a/qcow2/lib/block/block-gen.c b/qcow2/lib/block/block-gen.c +index ff2832fa..6265dd52 100644 +--- a/qcow2/lib/block/block-gen.c ++++ b/qcow2/lib/block/block-gen.c +@@ -2190,6 +2190,7 @@ int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, int64_t bytes, BdrvRequ + } + + ++#if 0 + /* + * Wrappers for blk_co_zone_report + */ +@@ -2335,6 +2336,7 @@ int blk_zone_append(BlockBackend *blk, int64_t *offset, QEMUIOVector *qiov, Bdrv + return s.ret; + } + } ++#endif + + + /* +@@ -2592,7 +2594,7 @@ int bdrv_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, + /* + * Wrappers for nbd_co_do_establish_connection + */ +- ++#if 0 + typedef struct NbdDoEstablishConnection { + BdrvPollCo poll_state; + int ret; +@@ -2634,3 +2636,4 @@ int nbd_do_establish_connection(BlockDriverState *bs, bool blocking, Error **err + return s.ret; + } + } ++#endif +diff --git a/qcow2/lib/block/dirty-bitmap.c b/qcow2/lib/block/dirty-bitmap.c +index 13a19797..557059de 100644 +--- a/qcow2/lib/block/dirty-bitmap.c ++++ b/qcow2/lib/block/dirty-bitmap.c +@@ -770,10 +770,12 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BdrvDirtyBitmap *bitmap) + return QLIST_NEXT(bitmap, list); + } + ++#if 0 + char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp) + { + return hbitmap_sha256(bitmap->bitmap, errp); + } ++#endif + + int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset, + int64_t bytes) +diff --git a/qcow2/lib/block/file-posix.c b/qcow2/lib/block/file-posix.c +index ff928b5e..3223c75e 100644 +--- a/qcow2/lib/block/file-posix.c ++++ b/qcow2/lib/block/file-posix.c +@@ -39,8 +39,8 @@ + #include "qapi/qmp/qdict.h" + #include "qapi/qmp/qstring.h" + +-#include "scsi/pr-manager.h" +-#include "scsi/constants.h" ++//#include "scsi/pr-manager.h" ++//#include "scsi/constants.h" + + #if defined(__APPLE__) && (__MACH__) + #include +@@ -173,7 +173,7 @@ typedef struct BDRVRawState { + uint64_t discard_bytes_ok; + } stats; + +- PRManager *pr_mgr; ++ //PRManager *pr_mgr; + } BDRVRawState; + + typedef struct BDRVRawReopenState { +@@ -317,6 +317,7 @@ static int probe_logical_blocksize(int fd, unsigned int *sector_size_p) + return success ? 0 : -errno; + } + ++#if defined(HAVE_HOST_BLOCK_DEVICE) + /** + * Get physical block size of @fd. + * On success, store it in @blk_size and return 0. +@@ -333,6 +334,7 @@ static int probe_physical_blocksize(int fd, unsigned int *blk_size) + return -ENOTSUP; + #endif + } ++#endif + + /* + * Returns true if no alignment restrictions are necessary even for files +@@ -597,7 +599,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + QemuOpts *opts; + Error *local_err = NULL; + const char *filename = NULL; +- const char *str; ++ //const char *str; + BlockdevAioOptions aio, aio_default; + int fd, ret; + struct stat st; +@@ -670,6 +672,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + abort(); + } + ++#if 0 + str = qemu_opt_get(opts, "pr-manager"); + if (str) { + s->pr_mgr = pr_manager_lookup(str, &local_err); +@@ -679,6 +682,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + goto fail; + } + } ++#endif + + s->drop_cache = qemu_opt_get_bool(opts, "drop-cache", true); + s->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped", +@@ -1522,6 +1526,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) + raw_refresh_zoned_limits(bs, &st, errp); + } + ++#if defined(HAVE_HOST_BLOCK_DEVICE) + static int check_for_dasd(int fd) + { + #ifdef BIODASDINFO2 +@@ -1597,8 +1602,9 @@ static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) + return -ENOTSUP; + } + #endif ++#endif + +-#if defined(__linux__) ++#if defined(__linux__) && defined(HAVE_HOST_BLOCK_DEVICE) + static int handle_aiocb_ioctl(void *opaque) + { + RawPosixAIOData *aiocb = opaque; +@@ -3481,7 +3487,7 @@ static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, + }; + + trace_zbd_zone_mgmt(bs, op_name, offset >> BDRV_SECTOR_BITS, +- len >> BDRV_SECTOR_BITS); ++ // len >> BDRV_SECTOR_BITS); + ret = raw_thread_pool_submit(handle_aiocb_zone_mgmt, &acb); + if (ret != 0) { + update_zones_wp(bs, s->fd, offset, nrz); +@@ -4194,6 +4200,7 @@ hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) + return ret; + } + ++#if 0 + if (req == SG_IO && s->pr_mgr) { + struct sg_io_hdr *io_hdr = buf; + if (io_hdr->cmdp[0] == PERSISTENT_RESERVE_OUT || +@@ -4202,6 +4209,7 @@ hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) + s->fd, io_hdr); + } + } ++#endif + + acb = (RawPosixAIOData) { + .bs = bs, +diff --git a/qcow2/lib/block/io.c b/qcow2/lib/block/io.c +index 301514c8..f5aaade3 100644 +--- a/qcow2/lib/block/io.c ++++ b/qcow2/lib/block/io.c +@@ -27,17 +27,19 @@ + #include "sysemu/block-backend.h" + #include "block/aio-wait.h" + #include "block/blockjob.h" +-#include "block/blockjob_int.h" ++//#include "block/blockjob_int.h" + #include "block/block_int.h" + #include "block/coroutines.h" + #include "block/dirty-bitmap.h" +-#include "block/write-threshold.h" ++//#include "block/write-threshold.h" + #include "qemu/cutils.h" + #include "qemu/memalign.h" + #include "qapi/error.h" + #include "qemu/error-report.h" + #include "qemu/main-loop.h" +-#include "sysemu/replay.h" ++//#include "sysemu/replay.h" ++ ++#define replay_events_enabled() 0 + + /* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ + #define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) +@@ -1263,7 +1265,7 @@ bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes, + goto err; + } + +- bdrv_co_debug_event(bs, BLKDBG_COR_WRITE); ++ //bdrv_co_debug_event(bs, BLKDBG_COR_WRITE); + if (drv->bdrv_co_pwrite_zeroes && + buffer_is_zero(bounce_buffer, pnum)) { + /* FIXME: Should we (perhaps conditionally) be setting +@@ -1524,10 +1526,10 @@ bdrv_padding_rmw_read(BdrvChild *child, BdrvTrackedRequest *req, + qemu_iovec_init_buf(&local_qiov, pad->buf, bytes); + + if (pad->head) { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); + } + if (pad->merge_reads && pad->tail) { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); + } + ret = bdrv_aligned_preadv(child, req, req->overlap_offset, bytes, + align, &local_qiov, 0, 0); +@@ -1535,10 +1537,10 @@ bdrv_padding_rmw_read(BdrvChild *child, BdrvTrackedRequest *req, + return ret; + } + if (pad->head) { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); + } + if (pad->merge_reads && pad->tail) { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + } + + if (pad->merge_reads) { +@@ -1549,7 +1551,7 @@ bdrv_padding_rmw_read(BdrvChild *child, BdrvTrackedRequest *req, + if (pad->tail) { + qemu_iovec_init_buf(&local_qiov, pad->tail_buf, align); + +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); + ret = bdrv_aligned_preadv( + child, req, + req->overlap_offset + req->overlap_bytes - align, +@@ -1557,7 +1559,7 @@ bdrv_padding_rmw_read(BdrvChild *child, BdrvTrackedRequest *req, + if (ret < 0) { + return ret; + } +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + } + + zero_mem: +@@ -2001,7 +2003,7 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes, + } else { + assert(child->perm & BLK_PERM_WRITE); + } +- bdrv_write_threshold_check_write(bs, offset, bytes); ++ //bdrv_write_threshold_check_write(bs, offset, bytes); + return 0; + case BDRV_TRACKED_TRUNCATE: + assert(child->perm & BLK_PERM_RESIZE); +@@ -2101,16 +2103,16 @@ bdrv_aligned_pwritev(BdrvChild *child, BdrvTrackedRequest *req, + if (ret < 0) { + /* Do nothing, write notifier decided to fail this request */ + } else if (flags & BDRV_REQ_ZERO_WRITE) { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_ZERO); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_ZERO); + ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags); + } else if (flags & BDRV_REQ_WRITE_COMPRESSED) { + ret = bdrv_driver_pwritev_compressed(bs, offset, bytes, + qiov, qiov_offset); + } else if (bytes <= max_transfer) { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV); + ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, qiov_offset, flags); + } else { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV); + while (bytes_remaining) { + int num = MIN(bytes_remaining, max_transfer); + int local_flags = flags; +@@ -2133,7 +2135,7 @@ bdrv_aligned_pwritev(BdrvChild *child, BdrvTrackedRequest *req, + bytes_remaining -= num; + } + } +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_DONE); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_DONE); + + if (ret >= 0) { + ret = 0; +@@ -2324,6 +2326,7 @@ int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, + BDRV_REQ_ZERO_WRITE | flags); + } + ++#if 0 + /* + * Flush ALL BDSes regardless of if they are reachable via a BlkBackend or not. + */ +@@ -2354,6 +2357,7 @@ int bdrv_flush_all(void) + + return result; + } ++#endif + + /* + * Returns the allocation status of the specified sectors. +@@ -2892,6 +2896,7 @@ bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) + return ret; + } + ++#if 0 + int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, + int64_t pos, int size) + { +@@ -2911,6 +2916,7 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, + + return ret < 0 ? ret : size; + } ++#endif + + /**************************************************************/ + /* async I/Os */ +@@ -3221,6 +3227,7 @@ out: + return co.ret; + } + ++#if 0 + int coroutine_fn bdrv_co_zone_report(BlockDriverState *bs, int64_t offset, + unsigned int *nr_zones, + BlockZoneDescriptor *zones) +@@ -3288,6 +3295,7 @@ out: + bdrv_dec_in_flight(bs); + return co.ret; + } ++#endif + + void *qemu_blockalign(BlockDriverState *bs, size_t size) + { +@@ -3679,6 +3687,7 @@ void bdrv_cancel_in_flight(BlockDriverState *bs) + } + } + ++#if 0 + int coroutine_fn + bdrv_co_preadv_snapshot(BdrvChild *child, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset) +@@ -3753,3 +3762,4 @@ bdrv_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes) + + return ret; + } ++#endif +diff --git a/qcow2/lib/block/mirror.c b/qcow2/lib/block/mirror.c +index 61f0a717..1b34bb9e 100644 +--- a/qcow2/lib/block/mirror.c ++++ b/qcow2/lib/block/mirror.c +@@ -1235,6 +1235,7 @@ static bool mirror_drained_poll(BlockJob *job) + return !!s->in_flight; + } + ++#if 0 + static bool mirror_cancel(Job *job, bool force) + { + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); +@@ -1251,6 +1252,7 @@ static bool mirror_cancel(Job *job, bool force) + } + return force; + } ++#endif + + static bool commit_active_cancel(Job *job, bool force) + { +@@ -1258,6 +1260,7 @@ static bool commit_active_cancel(Job *job, bool force) + return force || !job_is_ready(job); + } + ++#if 0 + static void mirror_change(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp) + { +@@ -1317,6 +1320,7 @@ static const BlockJobDriver mirror_job_driver = { + .change = mirror_change, + .query = mirror_query, + }; ++#endif + + static const BlockJobDriver commit_active_job_driver = { + .job_driver = { +@@ -2004,6 +2008,7 @@ fail: + return NULL; + } + ++#if 0 + void mirror_start(const char *job_id, BlockDriverState *bs, + BlockDriverState *target, const char *replaces, + int creation_flags, int64_t speed, +@@ -2038,6 +2043,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, + &mirror_job_driver, is_none_mode, base, false, + filter_node_name, true, copy_mode, false, errp); + } ++#endif + + BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, + BlockDriverState *base, int creation_flags, +diff --git a/qcow2/lib/block/qapi.c b/qcow2/lib/block/qapi.c +index 2b5793f1..82e12895 100644 +--- a/qcow2/lib/block/qapi.c ++++ b/qcow2/lib/block/qapi.c +@@ -27,8 +27,8 @@ + #include "block/qapi.h" + #include "block/block_int.h" + #include "block/dirty-bitmap.h" +-#include "block/throttle-groups.h" +-#include "block/write-threshold.h" ++//#include "block/throttle-groups.h" ++//#include "block/write-threshold.h" + #include "qapi/error.h" + #include "qapi/qapi-commands-block-core.h" + #include "qapi/qobject-output-visitor.h" +@@ -88,6 +88,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, + + info->detect_zeroes = bs->detect_zeroes; + ++#if 0 + if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) { + ThrottleConfig cfg; + BlockBackendPublic *blkp = blk_get_public(blk); +@@ -144,6 +145,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, + } + + info->write_threshold = bdrv_write_threshold_get(bs); ++#endif + + p_image_info = &info->image; + info->backing_file_depth = 0; +diff --git a/qcow2/lib/block/raw-format.c b/qcow2/lib/block/raw-format.c +index ac7e8495..f3343e98 100644 +--- a/qcow2/lib/block/raw-format.c ++++ b/qcow2/lib/block/raw-format.c +@@ -319,6 +319,7 @@ raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) + return bdrv_co_pdiscard(bs->file, offset, bytes); + } + ++#if 0 + static int coroutine_fn GRAPH_RDLOCK + raw_co_zone_report(BlockDriverState *bs, int64_t offset, + unsigned int *nr_zones, +@@ -340,6 +341,7 @@ raw_co_zone_append(BlockDriverState *bs,int64_t *offset, QEMUIOVector *qiov, + { + return bdrv_co_zone_append(bs->file->bs, offset, qiov, flags); + } ++#endif + + static int64_t coroutine_fn GRAPH_RDLOCK + raw_co_getlength(BlockDriverState *bs) +@@ -650,9 +652,11 @@ BlockDriver bdrv_raw = { + .bdrv_co_pwritev = &raw_co_pwritev, + .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes, + .bdrv_co_pdiscard = &raw_co_pdiscard, ++#if 0 + .bdrv_co_zone_report = &raw_co_zone_report, + .bdrv_co_zone_mgmt = &raw_co_zone_mgmt, + .bdrv_co_zone_append = &raw_co_zone_append, ++#endif + .bdrv_co_block_status = &raw_co_block_status, + .bdrv_co_copy_range_from = &raw_co_copy_range_from, + .bdrv_co_copy_range_to = &raw_co_copy_range_to, +diff --git a/qcow2/lib/blockdev.c b/qcow2/lib/blockdev.c +index 835064ed..95cba97c 100644 +--- a/qcow2/lib/blockdev.c ++++ b/qcow2/lib/blockdev.c +@@ -37,14 +37,14 @@ + #include "block/blockjob.h" + #include "block/dirty-bitmap.h" + #include "block/qdict.h" +-#include "block/throttle-groups.h" +-#include "monitor/monitor.h" ++//#include "block/throttle-groups.h" ++//#include "monitor/monitor.h" + #include "qemu/error-report.h" + #include "qemu/option.h" + #include "qemu/qemu-print.h" + #include "qemu/config-file.h" + #include "qapi/qapi-commands-block.h" +-#include "qapi/qapi-commands-transaction.h" ++//#include "qapi/qapi-commands-transaction.h" + #include "qapi/qapi-visit-block-core.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qmp/qnum.h" +@@ -53,17 +53,18 @@ + #include "qapi/qmp/qerror.h" + #include "qapi/qmp/qlist.h" + #include "qapi/qobject-output-visitor.h" +-#include "sysemu/sysemu.h" +-#include "sysemu/iothread.h" ++//#include "sysemu/sysemu.h" ++//#include "sysemu/iothread.h" + #include "block/block_int.h" + #include "block/trace.h" +-#include "sysemu/runstate.h" +-#include "sysemu/replay.h" ++//#include "sysemu/runstate.h" ++//#include "sysemu/replay.h" + #include "qemu/cutils.h" + #include "qemu/help_option.h" + #include "qemu/main-loop.h" +-#include "qemu/throttle-options.h" ++//#include "qemu/throttle-options.h" + ++#if 0 + /* Protected by BQL */ + QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = + QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); +@@ -1035,6 +1036,7 @@ fail: + qobject_unref(bs_opts); + return dinfo; + } ++#endif + + static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) + { +@@ -1060,6 +1062,7 @@ static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) + return bs; + } + ++#if 0 + static void blockdev_do_action(TransactionAction *action, Error **errp) + { + TransactionActionList list; +@@ -2422,6 +2425,7 @@ void qmp_block_stream(const char *job_id, const char *device, + out_rdlock: + bdrv_graph_rdunlock_main_loop(); + } ++#endif + + void qmp_block_commit(const char *job_id, const char *device, + const char *base_node, +@@ -2614,6 +2618,7 @@ void qmp_block_commit(const char *job_id, const char *device, + } + } + ++#if 0 + /* Common QMP interface for drive-backup and blockdev-backup */ + static BlockJob *do_backup_common(BackupCommon *backup, + BlockDriverState *bs, +@@ -3510,6 +3515,7 @@ void qmp_x_blockdev_change(const char *parent, const char *child, + out: + bdrv_graph_wrunlock(); + } ++#endif + + BlockJobInfoList *qmp_query_block_jobs(Error **errp) + { +@@ -3536,6 +3542,7 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp) + return head; + } + ++#if 0 + void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, + bool has_force, bool force, Error **errp) + { +@@ -3647,3 +3654,4 @@ QemuOptsList qemu_drive_opts = { + { /* end of list */ } + }, + }; ++#endif +diff --git a/qcow2/lib/blockjob.c b/qcow2/lib/blockjob.c +index d5f29e14..071b511c 100644 +--- a/qcow2/lib/blockjob.c ++++ b/qcow2/lib/blockjob.c +@@ -213,6 +213,7 @@ void block_job_remove_all_bdrv(BlockJob *job) + bdrv_graph_wrunlock(); + } + ++#if 0 + bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) + { + GSList *el; +@@ -227,6 +228,7 @@ bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) + + return false; + } ++#endif + + int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, + uint64_t perm, uint64_t shared_perm, Error **errp) +@@ -312,6 +314,7 @@ static bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) + return block_job_set_speed_locked(job, speed, errp); + } + ++#if 0 + void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp) + { +@@ -331,6 +334,7 @@ void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, + error_setg(errp, "Job type does not support change"); + } + } ++#endif + + void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n) + { +diff --git a/qcow2/lib/hw/block/block.c b/qcow2/lib/hw/block/block.c +index 3ceca7dc..9a0da1e6 100644 +--- a/qcow2/lib/hw/block/block.c ++++ b/qcow2/lib/hw/block/block.c +@@ -9,12 +9,13 @@ + + #include "qemu/osdep.h" + #include "block/block_int-common.h" +-#include "sysemu/blockdev.h" ++//#include "sysemu/blockdev.h" + #include "sysemu/block-backend.h" + #include "hw/block/block.h" + #include "qapi/error.h" + #include "qapi/qapi-types-block.h" + ++#if 0 + /* + * Read the non-zeroes parts of @blk into @buf + * Reading all of the @blk is expensive if the zeroes parts of @blk +@@ -99,6 +100,7 @@ bool blk_check_size_and_read_all(BlockBackend *blk, DeviceState *dev, + } + return true; + } ++#endif + + bool blkconf_blocksizes(BlockConf *conf, Error **errp) + { +@@ -257,8 +259,8 @@ bool blkconf_geometry(BlockConf *conf, int *ptrans, + hd_geometry_guess(conf->blk, + &conf->cyls, &conf->heads, &conf->secs, + ptrans); +- } else if (ptrans && *ptrans == BIOS_ATA_TRANSLATION_AUTO) { +- *ptrans = hd_bios_chs_auto_trans(conf->cyls, conf->heads, conf->secs); ++ //} else if (ptrans && *ptrans == BIOS_ATA_TRANSLATION_AUTO) { ++ // *ptrans = hd_bios_chs_auto_trans(conf->cyls, conf->heads, conf->secs); + } + if (conf->cyls || conf->heads || conf->secs) { + if (conf->cyls < 1 || conf->cyls > cyls_max) { +diff --git a/qcow2/lib/qapi/qapi-events-block-core.c b/qcow2/lib/qapi/qapi-events-block-core.c +index a08a710c..2c8a538c 100644 +--- a/qcow2/lib/qapi/qapi-events-block-core.c ++++ b/qcow2/lib/qapi/qapi-events-block-core.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi-emit-events.h" +-#include "qapi-events-block-core.h" +-#include "qapi-visit-block-core.h" ++#include "qapi/qapi-events-block-core.h" ++#include "qapi/qapi-visit-block-core.h" + #include "qapi/compat-policy.h" + #include "qapi/error.h" + #include "qapi/qmp/qdict.h" +@@ -43,7 +43,7 @@ void qapi_event_send_block_image_corrupted(const char *device, const char *node_ + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_IMAGE_CORRUPTED, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_IMAGE_CORRUPTED, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -73,7 +73,7 @@ void qapi_event_send_block_io_error(const char *device, const char *node_name, I + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_IO_ERROR, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_IO_ERROR, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -103,7 +103,7 @@ void qapi_event_send_block_job_completed(JobType type, const char *device, int64 + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_JOB_COMPLETED, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_JOB_COMPLETED, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -133,7 +133,7 @@ void qapi_event_send_block_job_cancelled(JobType type, const char *device, int64 + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_JOB_CANCELLED, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_JOB_CANCELLED, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -163,7 +163,7 @@ void qapi_event_send_block_job_error(const char *device, IoOperationType operati + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_JOB_ERROR, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_JOB_ERROR, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -193,7 +193,7 @@ void qapi_event_send_block_job_ready(JobType type, const char *device, int64_t l + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_JOB_READY, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_JOB_READY, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -223,7 +223,7 @@ void qapi_event_send_block_job_pending(JobType type, const char *id) + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_JOB_PENDING, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_JOB_PENDING, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -253,7 +253,7 @@ void qapi_event_send_block_write_threshold(const char *node_name, uint64_t amoun + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_WRITE_THRESHOLD, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_WRITE_THRESHOLD, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -283,7 +283,7 @@ void qapi_event_send_quorum_failure(const char *reference, int64_t sector_num, i + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_QUORUM_FAILURE, qmp); ++ //qapi_event_emit(QAPI_EVENT_QUORUM_FAILURE, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -313,7 +313,7 @@ void qapi_event_send_quorum_report_bad(QuorumOpType type, const char *error, con + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_QUORUM_REPORT_BAD, qmp); ++ //qapi_event_emit(QAPI_EVENT_QUORUM_REPORT_BAD, qmp); + + visit_free(v); + qobject_unref(qmp); +diff --git a/qcow2/lib/qapi/qapi-events-job.c b/qcow2/lib/qapi/qapi-events-job.c +index cb5c3b45..f96b8a30 100644 +--- a/qcow2/lib/qapi/qapi-events-job.c ++++ b/qcow2/lib/qapi/qapi-events-job.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi-emit-events.h" +-#include "qapi-events-job.h" +-#include "qapi-visit-job.h" ++#include "qapi/qapi-events-job.h" ++#include "qapi/qapi-visit-job.h" + #include "qapi/compat-policy.h" + #include "qapi/error.h" + #include "qapi/qmp/qdict.h" +@@ -43,7 +43,7 @@ void qapi_event_send_job_status_change(const char *id, JobStatus status) + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_JOB_STATUS_CHANGE, qmp); ++ //qapi_event_emit(QAPI_EVENT_JOB_STATUS_CHANGE, qmp); + + visit_free(v); + qobject_unref(qmp); +diff --git a/qcow2/lib/qapi/qapi-types-block-core.c b/qcow2/lib/qapi/qapi-types-block-core.c +index f4ee0d21..7e9cf7c7 100644 +--- a/qcow2/lib/qapi/qapi-types-block-core.c ++++ b/qcow2/lib/qapi/qapi-types-block-core.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi/dealloc-visitor.h" +-#include "qapi-types-block-core.h" +-#include "qapi-visit-block-core.h" ++#include "qapi/qapi-types-block-core.h" ++#include "qapi/qapi-visit-block-core.h" + + void qapi_free_SnapshotInfo(SnapshotInfo *obj) + { +@@ -28,6 +28,7 @@ void qapi_free_SnapshotInfo(SnapshotInfo *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_ImageInfoSpecificQCow2EncryptionBase(ImageInfoSpecificQCow2EncryptionBase *obj) + { + Visitor *v; +@@ -53,6 +54,7 @@ void qapi_free_ImageInfoSpecificQCow2Encryption(ImageInfoSpecificQCow2Encryption + visit_type_ImageInfoSpecificQCow2Encryption(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_Qcow2BitmapInfoList(Qcow2BitmapInfoList *obj) + { +@@ -80,6 +82,7 @@ void qapi_free_ImageInfoSpecificQCow2(ImageInfoSpecificQCow2 *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_VmdkExtentInfoList(VmdkExtentInfoList *obj) + { + Visitor *v; +@@ -131,6 +134,7 @@ void qapi_free_ImageInfoSpecificRbd(ImageInfoSpecificRbd *obj) + visit_type_ImageInfoSpecificRbd(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_ImageInfoSpecificFile(ImageInfoSpecificFile *obj) + { +@@ -148,14 +152,17 @@ void qapi_free_ImageInfoSpecificFile(ImageInfoSpecificFile *obj) + const QEnumLookup ImageInfoSpecificKind_lookup = { + .array = (const char *const[]) { + [IMAGE_INFO_SPECIFIC_KIND_QCOW2] = "qcow2", ++#if 0 + [IMAGE_INFO_SPECIFIC_KIND_VMDK] = "vmdk", + [IMAGE_INFO_SPECIFIC_KIND_LUKS] = "luks", + [IMAGE_INFO_SPECIFIC_KIND_RBD] = "rbd", ++#endif + [IMAGE_INFO_SPECIFIC_KIND_FILE] = "file", + }, + .size = IMAGE_INFO_SPECIFIC_KIND__MAX + }; + ++#if 0 + void qapi_free_ImageInfoSpecificQCow2Wrapper(ImageInfoSpecificQCow2Wrapper *obj) + { + Visitor *v; +@@ -220,6 +227,7 @@ void qapi_free_ImageInfoSpecificFileWrapper(ImageInfoSpecificFileWrapper *obj) + visit_type_ImageInfoSpecificFileWrapper(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_ImageInfoSpecific(ImageInfoSpecific *obj) + { +@@ -433,6 +441,7 @@ void qapi_free_Qcow2BitmapInfo(Qcow2BitmapInfo *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockLatencyHistogramInfo(BlockLatencyHistogramInfo *obj) + { + Visitor *v; +@@ -445,6 +454,7 @@ void qapi_free_BlockLatencyHistogramInfo(BlockLatencyHistogramInfo *obj) + visit_type_BlockLatencyHistogramInfo(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockInfo(BlockInfo *obj) + { +@@ -485,6 +495,7 @@ void qapi_free_BlockInfoList(BlockInfoList *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockDeviceTimedStats(BlockDeviceTimedStats *obj) + { + Visitor *v; +@@ -640,6 +651,7 @@ void qapi_free_BlockJobInfoMirror(BlockJobInfoMirror *obj) + visit_type_BlockJobInfoMirror(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockJobInfo(BlockJobInfo *obj) + { +@@ -667,6 +679,7 @@ void qapi_free_BlockJobInfoList(BlockJobInfoList *obj) + visit_free(v); + } + ++#if 0 + const QEnumLookup NewImageMode_lookup = { + .array = (const char *const[]) { + [NEW_IMAGE_MODE_EXISTING] = "existing", +@@ -752,6 +765,7 @@ void qapi_free_BlockdevBackup(BlockdevBackup *obj) + visit_type_BlockdevBackup(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockDeviceInfoList(BlockDeviceInfoList *obj) + { +@@ -766,6 +780,7 @@ void qapi_free_BlockDeviceInfoList(BlockDeviceInfoList *obj) + visit_free(v); + } + ++#if 0 + const QEnumLookup XDbgBlockGraphNodeType_lookup = { + .array = (const char *const[]) { + [X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_BACKEND] = "block-backend", +@@ -875,6 +890,7 @@ void qapi_free_DriveMirror(DriveMirror *obj) + visit_type_DriveMirror(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockDirtyBitmap(BlockDirtyBitmap *obj) + { +@@ -928,6 +944,7 @@ void qapi_free_BlockDirtyBitmapOrStrList(BlockDirtyBitmapOrStrList *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockDirtyBitmapMerge(BlockDirtyBitmapMerge *obj) + { + Visitor *v; +@@ -1018,6 +1035,7 @@ void qapi_free_BlockJobChangeOptions(BlockJobChangeOptions *obj) + visit_type_BlockJobChangeOptions(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + const QEnumLookup BlockdevDiscardOptions_lookup = { + .array = (const char *const[]) { +@@ -1142,6 +1160,7 @@ void qapi_free_BlockdevOptionsFile(BlockdevOptionsFile *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockdevOptionsNull(BlockdevOptionsNull *obj) + { + Visitor *v; +@@ -1206,6 +1225,7 @@ void qapi_free_BlockdevOptionsLUKS(BlockdevOptionsLUKS *obj) + visit_type_BlockdevOptionsLUKS(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockdevOptionsGenericCOWFormat(BlockdevOptionsGenericCOWFormat *obj) + { +@@ -1256,6 +1276,7 @@ void qapi_free_Qcow2OverlapChecks(Qcow2OverlapChecks *obj) + visit_free(v); + } + ++#if 0 + const QEnumLookup BlockdevQcowEncryptionFormat_lookup = { + .array = (const char *const[]) { + [BLOCKDEV_QCOW_ENCRYPTION_FORMAT_AES] = "aes", +@@ -1322,6 +1343,7 @@ void qapi_free_BlockdevOptionsPreallocate(BlockdevOptionsPreallocate *obj) + visit_type_BlockdevOptionsPreallocate(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockdevOptionsQcow2(BlockdevOptionsQcow2 *obj) + { +@@ -1336,6 +1358,7 @@ void qapi_free_BlockdevOptionsQcow2(BlockdevOptionsQcow2 *obj) + visit_free(v); + } + ++#if 0 + const QEnumLookup SshHostKeyCheckMode_lookup = { + .array = (const char *const[]) { + [SSH_HOST_KEY_CHECK_MODE_NONE] = "none", +@@ -2071,6 +2094,7 @@ void qapi_free_BlockdevOptionsCbw(BlockdevOptionsCbw *obj) + visit_type_BlockdevOptionsCbw(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockdevOptions(BlockdevOptions *obj) + { +@@ -2111,6 +2135,7 @@ void qapi_free_BlockdevRefOrNull(BlockdevRefOrNull *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockdevOptionsList(BlockdevOptionsList *obj) + { + Visitor *v; +@@ -2123,6 +2148,7 @@ void qapi_free_BlockdevOptionsList(BlockdevOptionsList *obj) + visit_type_BlockdevOptionsList(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockdevCreateOptionsFile(BlockdevCreateOptionsFile *obj) + { +@@ -2137,6 +2163,7 @@ void qapi_free_BlockdevCreateOptionsFile(BlockdevCreateOptionsFile *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockdevCreateOptionsGluster(BlockdevCreateOptionsGluster *obj) + { + Visitor *v; +@@ -2201,6 +2228,7 @@ void qapi_free_BlockdevCreateOptionsQcow(BlockdevCreateOptionsQcow *obj) + visit_type_BlockdevCreateOptionsQcow(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + const QEnumLookup BlockdevQcow2Version_lookup = { + .array = (const char *const[]) { +@@ -2233,6 +2261,7 @@ void qapi_free_BlockdevCreateOptionsQcow2(BlockdevCreateOptionsQcow2 *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockdevCreateOptionsQed(BlockdevCreateOptionsQed *obj) + { + Visitor *v; +@@ -2360,6 +2389,7 @@ void qapi_free_BlockdevCreateOptionsVpc(BlockdevCreateOptionsVpc *obj) + visit_type_BlockdevCreateOptionsVpc(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockdevCreateOptions(BlockdevCreateOptions *obj) + { +@@ -2374,6 +2404,7 @@ void qapi_free_BlockdevCreateOptions(BlockdevCreateOptions *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockdevAmendOptionsLUKS(BlockdevAmendOptionsLUKS *obj) + { + Visitor *v; +@@ -2386,6 +2417,7 @@ void qapi_free_BlockdevAmendOptionsLUKS(BlockdevAmendOptionsLUKS *obj) + visit_type_BlockdevAmendOptionsLUKS(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockdevAmendOptionsQcow2(BlockdevAmendOptionsQcow2 *obj) + { +@@ -2441,6 +2473,7 @@ const QEnumLookup QuorumOpType_lookup = { + .size = QUORUM_OP_TYPE__MAX + }; + ++#if 0 + void qapi_free_BlockdevSnapshotInternal(BlockdevSnapshotInternal *obj) + { + Visitor *v; +@@ -2453,6 +2486,7 @@ void qapi_free_BlockdevSnapshotInternal(BlockdevSnapshotInternal *obj) + visit_type_BlockdevSnapshotInternal(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockGraphInfoList(BlockGraphInfoList *obj) + { +diff --git a/qcow2/lib/qapi/qapi-types-common.c b/qcow2/lib/qapi/qapi-types-common.c +index 16045e73..f00b36f1 100644 +--- a/qcow2/lib/qapi/qapi-types-common.c ++++ b/qcow2/lib/qapi/qapi-types-common.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi/dealloc-visitor.h" +-#include "qapi-types-common.h" +-#include "qapi-visit-common.h" ++#include "qapi/qapi-types-common.h" ++#include "qapi/qapi-visit-common.h" + + const QEnumLookup IoOperationType_lookup = { + .array = (const char *const[]) { +diff --git a/qcow2/lib/qapi/qapi-types-crypto.c b/qcow2/lib/qapi/qapi-types-crypto.c +index cb8f701a..509b0ac5 100644 +--- a/qcow2/lib/qapi/qapi-types-crypto.c ++++ b/qcow2/lib/qapi/qapi-types-crypto.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi/dealloc-visitor.h" +-#include "qapi-types-crypto.h" +-#include "qapi-visit-crypto.h" ++#include "qapi/qapi-types-crypto.h" ++#include "qapi/qapi-visit-crypto.h" + + const QEnumLookup QCryptoTLSCredsEndpoint_lookup = { + .array = (const char *const[]) { +diff --git a/qcow2/lib/qapi/qapi-types-job.c b/qcow2/lib/qapi/qapi-types-job.c +index eba27079..64010acb 100644 +--- a/qcow2/lib/qapi/qapi-types-job.c ++++ b/qcow2/lib/qapi/qapi-types-job.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi/dealloc-visitor.h" +-#include "qapi-types-job.h" +-#include "qapi-visit-job.h" ++#include "qapi/qapi-types-job.h" ++#include "qapi/qapi-visit-job.h" + + const QEnumLookup JobType_lookup = { + .array = (const char *const[]) { +diff --git a/qcow2/lib/qapi/qapi-types-sockets.c b/qcow2/lib/qapi/qapi-types-sockets.c +index 568f49e6..1d5556ca 100644 +--- a/qcow2/lib/qapi/qapi-types-sockets.c ++++ b/qcow2/lib/qapi/qapi-types-sockets.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi/dealloc-visitor.h" +-#include "qapi-types-sockets.h" +-#include "qapi-visit-sockets.h" ++#include "qapi/qapi-types-sockets.h" ++#include "qapi/qapi-visit-sockets.h" + + const QEnumLookup NetworkAddressFamily_lookup = { + .array = (const char *const[]) { +diff --git a/qcow2/lib/qapi/qapi-types-yank.c b/qcow2/lib/qapi/qapi-types-yank.c +index e75e13a2..09c68f5a 100644 +--- a/qcow2/lib/qapi/qapi-types-yank.c ++++ b/qcow2/lib/qapi/qapi-types-yank.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi/dealloc-visitor.h" +-#include "qapi-types-yank.h" +-#include "qapi-visit-yank.h" ++#include "qapi/qapi-types-yank.h" ++#include "qapi/qapi-visit-yank.h" + + const QEnumLookup YankInstanceType_lookup = { + .array = (const char *const[]) { +diff --git a/qcow2/lib/qapi/qapi-visit-block-core.c b/qcow2/lib/qapi/qapi-visit-block-core.c +index b8d709cb..7e63d3f5 100644 +--- a/qcow2/lib/qapi/qapi-visit-block-core.c ++++ b/qcow2/lib/qapi/qapi-visit-block-core.c +@@ -12,7 +12,7 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi-visit-block-core.h" ++#include "qapi/qapi-visit-block-core.h" + + bool visit_type_SnapshotInfo_members(Visitor *v, SnapshotInfo *obj, Error **errp) + { +@@ -72,6 +72,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_ImageInfoSpecificQCow2EncryptionBase_members(Visitor *v, ImageInfoSpecificQCow2EncryptionBase *obj, Error **errp) + { + if (!visit_type_BlockdevQcow2EncryptionFormat(v, "format", &obj->format, errp)) { +@@ -149,6 +150,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_Qcow2BitmapInfoList(Visitor *v, const char *name, + Qcow2BitmapInfoList **obj, Error **errp) +@@ -181,7 +183,7 @@ out_obj: + bool visit_type_ImageInfoSpecificQCow2_members(Visitor *v, ImageInfoSpecificQCow2 *obj, Error **errp) + { + bool has_data_file = !!obj->data_file; +- bool has_encrypt = !!obj->encrypt; ++ //bool has_encrypt = !!obj->encrypt; + + if (!visit_type_str(v, "compat", &obj->compat, errp)) { + return false; +@@ -214,11 +216,13 @@ bool visit_type_ImageInfoSpecificQCow2_members(Visitor *v, ImageInfoSpecificQCow + if (!visit_type_int(v, "refcount-bits", &obj->refcount_bits, errp)) { + return false; + } ++#if 0 + if (visit_optional(v, "encrypt", &has_encrypt)) { + if (!visit_type_ImageInfoSpecificQCow2Encryption(v, "encrypt", &obj->encrypt, errp)) { + return false; + } + } ++#endif + if (visit_optional(v, "bitmaps", &obj->has_bitmaps)) { + if (!visit_type_Qcow2BitmapInfoList(v, "bitmaps", &obj->bitmaps, errp)) { + return false; +@@ -257,6 +261,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_VmdkExtentInfoList(Visitor *v, const char *name, + VmdkExtentInfoList **obj, Error **errp) + { +@@ -416,6 +421,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_ImageInfoSpecificFile_members(Visitor *v, ImageInfoSpecificFile *obj, Error **errp) + { +@@ -471,6 +477,7 @@ bool visit_type_ImageInfoSpecificQCow2Wrapper_members(Visitor *v, ImageInfoSpeci + return true; + } + ++#if 0 + bool visit_type_ImageInfoSpecificQCow2Wrapper(Visitor *v, const char *name, + ImageInfoSpecificQCow2Wrapper **obj, Error **errp) + { +@@ -602,6 +609,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_ImageInfoSpecificFileWrapper_members(Visitor *v, ImageInfoSpecificFileWrapper *obj, Error **errp) + { +@@ -611,6 +619,7 @@ bool visit_type_ImageInfoSpecificFileWrapper_members(Visitor *v, ImageInfoSpecif + return true; + } + ++#if 0 + bool visit_type_ImageInfoSpecificFileWrapper(Visitor *v, const char *name, + ImageInfoSpecificFileWrapper **obj, Error **errp) + { +@@ -637,6 +646,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_q_obj_ImageInfoSpecific_base_members(Visitor *v, q_obj_ImageInfoSpecific_base *obj, Error **errp) + { +@@ -654,12 +664,14 @@ bool visit_type_ImageInfoSpecific_members(Visitor *v, ImageInfoSpecific *obj, Er + switch (obj->type) { + case IMAGE_INFO_SPECIFIC_KIND_QCOW2: + return visit_type_ImageInfoSpecificQCow2Wrapper_members(v, &obj->u.qcow2, errp); ++#if 0 + case IMAGE_INFO_SPECIFIC_KIND_VMDK: + return visit_type_ImageInfoSpecificVmdkWrapper_members(v, &obj->u.vmdk, errp); + case IMAGE_INFO_SPECIFIC_KIND_LUKS: + return visit_type_ImageInfoSpecificLUKSWrapper_members(v, &obj->u.luks, errp); + case IMAGE_INFO_SPECIFIC_KIND_RBD: + return visit_type_ImageInfoSpecificRbdWrapper_members(v, &obj->u.rbd, errp); ++#endif + case IMAGE_INFO_SPECIFIC_KIND_FILE: + return visit_type_ImageInfoSpecificFileWrapper_members(v, &obj->u.file, errp); + default: +@@ -1497,6 +1509,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockLatencyHistogramInfo_members(Visitor *v, BlockLatencyHistogramInfo *obj, Error **errp) + { + if (!visit_type_uint64List(v, "boundaries", &obj->boundaries, errp)) { +@@ -1534,6 +1547,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockInfo_members(Visitor *v, BlockInfo *obj, Error **errp) + { +@@ -1673,6 +1687,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockDeviceTimedStats_members(Visitor *v, BlockDeviceTimedStats *obj, Error **errp) + { + if (!visit_type_int(v, "interval_length", &obj->interval_length, errp)) { +@@ -2323,6 +2338,7 @@ bool visit_type_MirrorCopyMode(Visitor *v, const char *name, + *obj = value; + return ok; + } ++#endif + + bool visit_type_BlockJobInfoMirror_members(Visitor *v, BlockJobInfoMirror *obj, Error **errp) + { +@@ -2332,6 +2348,7 @@ bool visit_type_BlockJobInfoMirror_members(Visitor *v, BlockJobInfoMirror *obj, + return true; + } + ++#if 0 + bool visit_type_BlockJobInfoMirror(Visitor *v, const char *name, + BlockJobInfoMirror **obj, Error **errp) + { +@@ -2358,6 +2375,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_q_obj_BlockJobInfo_base_members(Visitor *v, q_obj_BlockJobInfo_base *obj, Error **errp) + { +@@ -2492,6 +2510,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_q_obj_block_resize_arg_members(Visitor *v, q_obj_block_resize_arg *obj, Error **errp) + { + bool has_device = !!obj->device; +@@ -2977,6 +2996,7 @@ bool visit_type_q_obj_query_named_block_nodes_arg_members(Visitor *v, q_obj_quer + } + return true; + } ++#endif + + bool visit_type_BlockDeviceInfoList(Visitor *v, const char *name, + BlockDeviceInfoList **obj, Error **errp) +@@ -3006,6 +3026,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_XDbgBlockGraphNodeType(Visitor *v, const char *name, + XDbgBlockGraphNodeType *obj, Error **errp) + { +@@ -3349,6 +3370,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockDirtyBitmap_members(Visitor *v, BlockDirtyBitmap *obj, Error **errp) + { +@@ -3517,6 +3539,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockDirtyBitmapMerge_members(Visitor *v, BlockDirtyBitmapMerge *obj, Error **errp) + { + if (!visit_type_str(v, "node", &obj->node, errp)) { +@@ -4397,6 +4420,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevDiscardOptions(Visitor *v, const char *name, + BlockdevDiscardOptions *obj, Error **errp) +@@ -4550,6 +4574,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevOptionsNull_members(Visitor *v, BlockdevOptionsNull *obj, Error **errp) + { + if (visit_optional(v, "size", &obj->has_size)) { +@@ -4691,6 +4716,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevOptionsGenericFormat_members(Visitor *v, BlockdevOptionsGenericFormat *obj, Error **errp) + { +@@ -4700,6 +4726,7 @@ bool visit_type_BlockdevOptionsGenericFormat_members(Visitor *v, BlockdevOptions + return true; + } + ++#if 0 + bool visit_type_BlockdevOptionsGenericFormat(Visitor *v, const char *name, + BlockdevOptionsGenericFormat **obj, Error **errp) + { +@@ -4774,6 +4801,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevOptionsGenericCOWFormat_members(Visitor *v, BlockdevOptionsGenericCOWFormat *obj, Error **errp) + { +@@ -4956,6 +4984,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevQcowEncryptionFormat(Visitor *v, const char *name, + BlockdevQcowEncryptionFormat *obj, Error **errp) + { +@@ -5160,11 +5189,12 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevOptionsQcow2_members(Visitor *v, BlockdevOptionsQcow2 *obj, Error **errp) + { + bool has_overlap_check = !!obj->overlap_check; +- bool has_encrypt = !!obj->encrypt; ++ //bool has_encrypt = !!obj->encrypt; + bool has_data_file = !!obj->data_file; + + if (!visit_type_BlockdevOptionsGenericCOWFormat_members(v, (BlockdevOptionsGenericCOWFormat *)obj, errp)) { +@@ -5225,11 +5255,13 @@ bool visit_type_BlockdevOptionsQcow2_members(Visitor *v, BlockdevOptionsQcow2 *o + return false; + } + } ++#if 0 + if (visit_optional(v, "encrypt", &has_encrypt)) { + if (!visit_type_BlockdevQcow2Encryption(v, "encrypt", &obj->encrypt, errp)) { + return false; + } + } ++#endif + if (visit_optional(v, "data-file", &has_data_file)) { + if (!visit_type_BlockdevRef(v, "data-file", &obj->data_file, errp)) { + return false; +@@ -5265,6 +5297,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_SshHostKeyCheckMode(Visitor *v, const char *name, + SshHostKeyCheckMode *obj, Error **errp) + { +@@ -5915,6 +5948,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevOptionsGluster_members(Visitor *v, BlockdevOptionsGluster *obj, Error **errp) + { + bool has_logfile = !!obj->logfile; +@@ -5967,6 +6001,7 @@ out_obj: + } + return ok; + } ++#endif + + #if defined(CONFIG_BLKIO) + bool visit_type_BlockdevOptionsIoUring_members(Visitor *v, BlockdevOptionsIoUring *obj, Error **errp) +@@ -6153,6 +6188,7 @@ out_obj: + } + #endif /* defined(CONFIG_BLKIO) */ + ++#if 0 + bool visit_type_IscsiTransport(Visitor *v, const char *name, + IscsiTransport *obj, Error **errp) + { +@@ -6739,6 +6775,7 @@ out_obj: + } + return ok; + } ++#endif + + #if defined(CONFIG_REPLICATION) + bool visit_type_ReplicationMode(Visitor *v, const char *name, +@@ -7159,6 +7196,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevOptionsNbd_members(Visitor *v, BlockdevOptionsNbd *obj, Error **errp) + { + bool has_export = !!obj->export; +@@ -7233,6 +7271,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevOptionsRaw_members(Visitor *v, BlockdevOptionsRaw *obj, Error **errp) + { +@@ -7422,6 +7461,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_q_obj_BlockdevOptions_base_members(Visitor *v, q_obj_BlockdevOptions_base *obj, Error **errp) + { +@@ -7475,6 +7515,7 @@ bool visit_type_BlockdevOptions_members(Visitor *v, BlockdevOptions *obj, Error + return false; + } + switch (obj->driver) { ++#if 0 + case BLOCKDEV_DRIVER_BLKDEBUG: + return visit_type_BlockdevOptionsBlkdebug_members(v, &obj->u.blkdebug, errp); + case BLOCKDEV_DRIVER_BLKLOGWRITES: +@@ -7495,8 +7536,10 @@ bool visit_type_BlockdevOptions_members(Visitor *v, BlockdevOptions *obj, Error + return visit_type_BlockdevOptionsCor_members(v, &obj->u.copy_on_read, errp); + case BLOCKDEV_DRIVER_DMG: + return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.dmg, errp); ++#endif + case BLOCKDEV_DRIVER_FILE: + return visit_type_BlockdevOptionsFile_members(v, &obj->u.file, errp); ++#if 0 + case BLOCKDEV_DRIVER_FTP: + return visit_type_BlockdevOptionsCurlFtp_members(v, &obj->u.ftp, errp); + case BLOCKDEV_DRIVER_FTPS: +@@ -7541,8 +7584,10 @@ bool visit_type_BlockdevOptions_members(Visitor *v, BlockdevOptions *obj, Error + return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.parallels, errp); + case BLOCKDEV_DRIVER_PREALLOCATE: + return visit_type_BlockdevOptionsPreallocate_members(v, &obj->u.preallocate, errp); ++#endif + case BLOCKDEV_DRIVER_QCOW2: + return visit_type_BlockdevOptionsQcow2_members(v, &obj->u.qcow2, errp); ++#if 0 + case BLOCKDEV_DRIVER_QCOW: + return visit_type_BlockdevOptionsQcow_members(v, &obj->u.qcow, errp); + case BLOCKDEV_DRIVER_QED: +@@ -7585,6 +7630,7 @@ bool visit_type_BlockdevOptions_members(Visitor *v, BlockdevOptions *obj, Error + return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.vpc, errp); + case BLOCKDEV_DRIVER_VVFAT: + return visit_type_BlockdevOptionsVVFAT_members(v, &obj->u.vvfat, errp); ++#endif + default: + abort(); + } +@@ -7717,6 +7763,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevOptionsList(Visitor *v, const char *name, + BlockdevOptionsList **obj, Error **errp) + { +@@ -7760,6 +7807,7 @@ bool visit_type_q_obj_blockdev_del_arg_members(Visitor *v, q_obj_blockdev_del_ar + } + return true; + } ++#endif + + bool visit_type_BlockdevCreateOptionsFile_members(Visitor *v, BlockdevCreateOptionsFile *obj, Error **errp) + { +@@ -7814,6 +7862,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevCreateOptionsGluster_members(Visitor *v, BlockdevCreateOptionsGluster *obj, Error **errp) + { + if (!visit_type_BlockdevOptionsGluster(v, "location", &obj->location, errp)) { +@@ -8044,6 +8093,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevQcow2Version(Visitor *v, const char *name, + BlockdevQcow2Version *obj, Error **errp) +@@ -8067,7 +8117,7 @@ bool visit_type_BlockdevCreateOptionsQcow2_members(Visitor *v, BlockdevCreateOpt + { + bool has_data_file = !!obj->data_file; + bool has_backing_file = !!obj->backing_file; +- bool has_encrypt = !!obj->encrypt; ++ //bool has_encrypt = !!obj->encrypt; + + if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { + return false; +@@ -8105,11 +8155,13 @@ bool visit_type_BlockdevCreateOptionsQcow2_members(Visitor *v, BlockdevCreateOpt + return false; + } + } ++#if 0 + if (visit_optional(v, "encrypt", &has_encrypt)) { + if (!visit_type_QCryptoBlockCreateOptions(v, "encrypt", &obj->encrypt, errp)) { + return false; + } + } ++#endif + if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { + if (!visit_type_size(v, "cluster-size", &obj->cluster_size, errp)) { + return false; +@@ -8165,6 +8217,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevCreateOptionsQed_members(Visitor *v, BlockdevCreateOptionsQed *obj, Error **errp) + { + bool has_backing_file = !!obj->backing_file; +@@ -8574,6 +8627,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_q_obj_BlockdevCreateOptions_base_members(Visitor *v, q_obj_BlockdevCreateOptions_base *obj, Error **errp) + { +@@ -8591,6 +8645,7 @@ bool visit_type_BlockdevCreateOptions_members(Visitor *v, BlockdevCreateOptions + switch (obj->driver) { + case BLOCKDEV_DRIVER_FILE: + return visit_type_BlockdevCreateOptionsFile_members(v, &obj->u.file, errp); ++#if 0 + case BLOCKDEV_DRIVER_GLUSTER: + return visit_type_BlockdevCreateOptionsGluster_members(v, &obj->u.gluster, errp); + case BLOCKDEV_DRIVER_LUKS: +@@ -8601,8 +8656,10 @@ bool visit_type_BlockdevCreateOptions_members(Visitor *v, BlockdevCreateOptions + return visit_type_BlockdevCreateOptionsParallels_members(v, &obj->u.parallels, errp); + case BLOCKDEV_DRIVER_QCOW: + return visit_type_BlockdevCreateOptionsQcow_members(v, &obj->u.qcow, errp); ++#endif + case BLOCKDEV_DRIVER_QCOW2: + return visit_type_BlockdevCreateOptionsQcow2_members(v, &obj->u.qcow2, errp); ++#if 0 + case BLOCKDEV_DRIVER_QED: + return visit_type_BlockdevCreateOptionsQed_members(v, &obj->u.qed, errp); + case BLOCKDEV_DRIVER_RBD: +@@ -8699,6 +8756,7 @@ bool visit_type_BlockdevCreateOptions_members(Visitor *v, BlockdevCreateOptions + #endif /* defined(CONFIG_BLKIO) */ + case BLOCKDEV_DRIVER_VVFAT: + break; ++#endif + default: + abort(); + } +@@ -8732,6 +8790,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_q_obj_blockdev_create_arg_members(Visitor *v, q_obj_blockdev_create_arg *obj, Error **errp) + { + if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { +@@ -8777,9 +8836,11 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevAmendOptionsQcow2_members(Visitor *v, BlockdevAmendOptionsQcow2 *obj, Error **errp) + { ++#if 0 + bool has_encrypt = !!obj->encrypt; + + if (visit_optional(v, "encrypt", &has_encrypt)) { +@@ -8787,6 +8848,7 @@ bool visit_type_BlockdevAmendOptionsQcow2_members(Visitor *v, BlockdevAmendOptio + return false; + } + } ++#endif + return true; + } + +@@ -8831,10 +8893,13 @@ bool visit_type_BlockdevAmendOptions_members(Visitor *v, BlockdevAmendOptions *o + return false; + } + switch (obj->driver) { ++#if 0 + case BLOCKDEV_DRIVER_LUKS: + return visit_type_BlockdevAmendOptionsLUKS_members(v, &obj->u.luks, errp); ++#endif + case BLOCKDEV_DRIVER_QCOW2: + return visit_type_BlockdevAmendOptionsQcow2_members(v, &obj->u.qcow2, errp); ++#if 0 + case BLOCKDEV_DRIVER_BLKDEBUG: + break; + case BLOCKDEV_DRIVER_BLKLOGWRITES: +@@ -8855,8 +8920,10 @@ bool visit_type_BlockdevAmendOptions_members(Visitor *v, BlockdevAmendOptions *o + break; + case BLOCKDEV_DRIVER_DMG: + break; ++#endif + case BLOCKDEV_DRIVER_FILE: + break; ++#if 0 + case BLOCKDEV_DRIVER_SNAPSHOT_ACCESS: + break; + case BLOCKDEV_DRIVER_FTP: +@@ -8941,6 +9008,7 @@ bool visit_type_BlockdevAmendOptions_members(Visitor *v, BlockdevAmendOptions *o + break; + case BLOCKDEV_DRIVER_VVFAT: + break; ++#endif + default: + abort(); + } +@@ -9177,6 +9245,7 @@ bool visit_type_q_obj_BLOCK_WRITE_THRESHOLD_arg_members(Visitor *v, q_obj_BLOCK_ + return true; + } + ++#if 0 + bool visit_type_q_obj_block_set_write_threshold_arg_members(Visitor *v, q_obj_block_set_write_threshold_arg *obj, Error **errp) + { + if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { +@@ -9224,6 +9293,7 @@ bool visit_type_q_obj_x_blockdev_set_iothread_arg_members(Visitor *v, q_obj_x_bl + } + return true; + } ++#endif + + bool visit_type_QuorumOpType(Visitor *v, const char *name, + QuorumOpType *obj, Error **errp) +@@ -9272,6 +9342,7 @@ bool visit_type_q_obj_QUORUM_REPORT_BAD_arg_members(Visitor *v, q_obj_QUORUM_REP + return true; + } + ++#if 0 + bool visit_type_BlockdevSnapshotInternal_members(Visitor *v, BlockdevSnapshotInternal *obj, Error **errp) + { + if (!visit_type_str(v, "device", &obj->device, errp)) { +@@ -9330,6 +9401,7 @@ bool visit_type_q_obj_blockdev_snapshot_delete_internal_sync_arg_members(Visitor + } + return true; + } ++#endif + + bool visit_type_BlockGraphInfoList(Visitor *v, const char *name, + BlockGraphInfoList **obj, Error **errp) +diff --git a/qcow2/lib/qapi/qapi-visit-common.c b/qcow2/lib/qapi/qapi-visit-common.c +index cacb730a..96e8d05f 100644 +--- a/qcow2/lib/qapi/qapi-visit-common.c ++++ b/qcow2/lib/qapi/qapi-visit-common.c +@@ -12,7 +12,7 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi-visit-common.h" ++#include "qapi/qapi-visit-common.h" + + bool visit_type_IoOperationType(Visitor *v, const char *name, + IoOperationType *obj, Error **errp) +diff --git a/qcow2/lib/qapi/qapi-visit-crypto.c b/qcow2/lib/qapi/qapi-visit-crypto.c +index 09eab7e3..b616ccbc 100644 +--- a/qcow2/lib/qapi/qapi-visit-crypto.c ++++ b/qcow2/lib/qapi/qapi-visit-crypto.c +@@ -12,7 +12,7 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi-visit-crypto.h" ++#include "qapi/qapi-visit-crypto.h" + + bool visit_type_QCryptoTLSCredsEndpoint(Visitor *v, const char *name, + QCryptoTLSCredsEndpoint *obj, Error **errp) +diff --git a/qcow2/lib/qapi/qapi-visit-job.c b/qcow2/lib/qapi/qapi-visit-job.c +index 092f9a03..77c6bcd0 100644 +--- a/qcow2/lib/qapi/qapi-visit-job.c ++++ b/qcow2/lib/qapi/qapi-visit-job.c +@@ -12,7 +12,7 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi-visit-job.h" ++#include "qapi/qapi-visit-job.h" + + bool visit_type_JobType(Visitor *v, const char *name, + JobType *obj, Error **errp) +diff --git a/qcow2/lib/qapi/qapi-visit-sockets.c b/qcow2/lib/qapi/qapi-visit-sockets.c +index 34d0b894..a1dcc91f 100644 +--- a/qcow2/lib/qapi/qapi-visit-sockets.c ++++ b/qcow2/lib/qapi/qapi-visit-sockets.c +@@ -12,7 +12,7 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi-visit-sockets.h" ++#include "qapi/qapi-visit-sockets.h" + + bool visit_type_NetworkAddressFamily(Visitor *v, const char *name, + NetworkAddressFamily *obj, Error **errp) +diff --git a/qcow2/lib/qapi/qapi-visit-yank.c b/qcow2/lib/qapi/qapi-visit-yank.c +index f5d5bf25..569a88db 100644 +--- a/qcow2/lib/qapi/qapi-visit-yank.c ++++ b/qcow2/lib/qapi/qapi-visit-yank.c +@@ -12,7 +12,7 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi-visit-yank.h" ++#include "qapi/qapi-visit-yank.h" + + bool visit_type_YankInstanceType(Visitor *v, const char *name, + YankInstanceType *obj, Error **errp) +diff --git a/qcow2/lib/qapi/qmp-dispatch.c b/qcow2/lib/qapi/qmp-dispatch.c +index 176b5494..1bce84ea 100644 +--- a/qcow2/lib/qapi/qmp-dispatch.c ++++ b/qcow2/lib/qapi/qmp-dispatch.c +@@ -41,6 +41,7 @@ Visitor *qobject_output_visitor_new_qmp(QObject **result) + return v; + } + ++#if 0 + static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob, + Error **errp) + { +@@ -294,3 +295,4 @@ out: + + return rsp; + } ++#endif +diff --git a/qcow2/lib/qapi/qobject-input-visitor.c b/qcow2/lib/qapi/qobject-input-visitor.c +index f110a804..408ade81 100644 +--- a/qcow2/lib/qapi/qobject-input-visitor.c ++++ b/qcow2/lib/qapi/qobject-input-visitor.c +@@ -28,7 +28,7 @@ + #include "qapi/qmp/qnum.h" + #include "qapi/qmp/qstring.h" + #include "qemu/cutils.h" +-#include "qemu/keyval.h" ++//#include "qemu/keyval.h" + + typedef struct StackObject { + const char *name; /* Name of @obj in its parent, if any */ +@@ -746,6 +746,7 @@ Visitor *qobject_input_visitor_new_keyval(QObject *obj) + return &v->visitor; + } + ++#if 0 + Visitor *qobject_input_visitor_new_str(const char *str, + const char *implied_key, + Error **errp) +@@ -774,3 +775,4 @@ Visitor *qobject_input_visitor_new_str(const char *str, + + return v; + } ++#endif +diff --git a/qcow2/lib/qcow2-cache.c b/qcow2/lib/qcow2-cache.c +index 23d9588b..aa269daf 100644 +--- a/qcow2/lib/qcow2-cache.c ++++ b/qcow2/lib/qcow2-cache.c +@@ -239,7 +239,7 @@ qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) + + int qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c) + { +- BDRVQcow2State *s = bs->opaque; ++ //BDRVQcow2State *s = bs->opaque; + int result = 0; + int ret; + int i; +diff --git a/qcow2/lib/qcow2-cluster.c b/qcow2/lib/qcow2-cluster.c +index ce8c0076..ad484e0c 100644 +--- a/qcow2/lib/qcow2-cluster.c ++++ b/qcow2/lib/qcow2-cluster.c +@@ -959,6 +959,7 @@ perform_cow(BlockDriverState *bs, QCowL2Meta *m) + goto fail; + } + ++#if 0 + /* Encrypt the data if necessary before writing it */ + if (bs->encrypted) { + ret = qcow2_co_encrypt(bs, +@@ -977,6 +978,7 @@ perform_cow(BlockDriverState *bs, QCowL2Meta *m) + goto fail; + } + } ++#endif + + /* And now we can write everything. If we have the guest data we + * can write everything in one single operation */ +diff --git a/qcow2/lib/qcow2-refcount.c b/qcow2/lib/qcow2-refcount.c +index 0266542c..f1895747 100644 +--- a/qcow2/lib/qcow2-refcount.c ++++ b/qcow2/lib/qcow2-refcount.c +@@ -745,8 +745,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret) + if (ret >= 0) { + int r2 = bdrv_pdiscard(bs->file, d->offset, d->bytes); + if (r2 < 0) { +- trace_qcow2_process_discards_failed_region(d->offset, d->bytes, +- r2); ++ trace_qcow2_process_discards_failed_region(d->offset, d->bytes, r2); + } + } + +@@ -2277,6 +2276,7 @@ calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + return ret; + } + ++#if 0 + /* encryption */ + if (s->crypto_header.length) { + ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, +@@ -2286,6 +2286,7 @@ calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + return ret; + } + } ++#endif + + /* bitmaps */ + ret = qcow2_check_bitmaps_refcounts(bs, res, refcount_table, nb_clusters); +diff --git a/qcow2/lib/qcow2-threads.c b/qcow2/lib/qcow2-threads.c +index d6071a1e..f6de6e60 100644 +--- a/qcow2/lib/qcow2-threads.c ++++ b/qcow2/lib/qcow2-threads.c +@@ -36,7 +36,7 @@ + #include "qcow2.h" + #include "block/block-io.h" + #include "block/thread-pool.h" +-#include "crypto.h" ++//#include "crypto.h" + + static int coroutine_fn + qcow2_co_process(BlockDriverState *bs, ThreadPoolFunc *func, void *arg) +@@ -426,6 +426,7 @@ qcow2_co_decompress(BlockDriverState *bs, void *dest, size_t dest_size, + } + + ++#if 0 + /* + * Cryptography + */ +@@ -525,3 +526,4 @@ qcow2_co_decrypt(BlockDriverState *bs, uint64_t host_offset, + return qcow2_co_encdec(bs, host_offset, guest_offset, buf, len, + qcrypto_block_decrypt); + } ++#endif +diff --git a/qcow2/lib/qcow2.c b/qcow2/lib/qcow2.c +index 70b19730..47c6032b 100644 +--- a/qcow2/lib/qcow2.c ++++ b/qcow2/lib/qcow2.c +@@ -31,7 +31,7 @@ + #include "qcow2.h" + #include "qemu/error-report.h" + #include "qapi/error.h" +-#include "qapi/qapi-events-block-core.h" ++//#include "qapi/qapi-events-block-core.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qmp/qstring.h" + #include "trace.h" +@@ -41,7 +41,7 @@ + #include "qemu/memalign.h" + #include "qapi/qobject-input-visitor.h" + #include "qapi/qapi-visit-block-core.h" +-#include "crypto.h" ++//#include "crypto.h" + #include "block/aio_task.h" + #include "block/dirty-bitmap.h" + +@@ -95,6 +95,7 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) + } + + ++#if 0 + static int GRAPH_RDLOCK + qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset, + uint8_t *buf, size_t buflen, +@@ -193,6 +194,7 @@ qcow2_extract_crypto_opts(QemuOpts *opts, const char *fmt, Error **errp) + qdict_put_str(cryptoopts_qdict, "format", fmt); + return cryptoopts_qdict; + } ++#endif + + /* + * read qcow2 extension and fill bs +@@ -287,6 +289,7 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, + break; + + case QCOW2_EXT_MAGIC_CRYPTO_HEADER: { ++#if 0 + unsigned int cflags = 0; + if (s->crypt_method_header != QCOW_CRYPT_LUKS) { + error_setg(errp, "CRYPTO header extension only " +@@ -325,6 +328,9 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, + if (!s->crypto) { + return -EINVAL; + } ++#else ++ assert(0); ++#endif + } break; + + case QCOW2_EXT_MAGIC_BITMAPS: +@@ -817,8 +823,10 @@ static QemuOptsList qcow2_runtime_opts = { + .type = QEMU_OPT_NUMBER, + .help = "Clean unused cache entries after this time (in seconds)", + }, ++#if 0 + BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", + "ID of secret providing qcow2 AES key or LUKS passphrase"), ++#endif + { /* end of list */ } + }, + }; +@@ -979,7 +987,7 @@ typedef struct Qcow2ReopenState { + bool discard_passthrough[QCOW2_DISCARD_MAX]; + bool discard_no_unref; + uint64_t cache_clean_interval; +- QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ ++ //QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ + } Qcow2ReopenState; + + static int GRAPH_RDLOCK +@@ -992,12 +1000,12 @@ qcow2_update_options_prepare(BlockDriverState *bs, Qcow2ReopenState *r, + int overlap_check_template = 0; + uint64_t l2_cache_size, l2_cache_entry_size, refcount_cache_size; + int i; +- const char *encryptfmt; +- QDict *encryptopts = NULL; ++ //const char *encryptfmt; ++ //QDict *encryptopts = NULL; + int ret; + +- qdict_extract_subqdict(options, &encryptopts, "encrypt."); +- encryptfmt = qdict_get_try_str(encryptopts, "format"); ++ //qdict_extract_subqdict(options, &encryptopts, "encrypt."); ++ //encryptfmt = qdict_get_try_str(encryptopts, "format"); + + opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort); + if (!qemu_opts_absorb_qdict(opts, options, errp)) { +@@ -1157,6 +1165,7 @@ qcow2_update_options_prepare(BlockDriverState *bs, Qcow2ReopenState *r, + goto fail; + } + ++#if 0 + switch (s->crypt_method_header) { + case QCOW_CRYPT_NONE: + if (encryptfmt) { +@@ -1205,10 +1214,11 @@ qcow2_update_options_prepare(BlockDriverState *bs, Qcow2ReopenState *r, + ret = -EINVAL; + goto fail; + } ++#endif + + ret = 0; + fail: +- qobject_unref(encryptopts); ++ //qobject_unref(encryptopts); + qemu_opts_del(opts); + opts = NULL; + return ret; +@@ -1245,8 +1255,8 @@ static void qcow2_update_options_commit(BlockDriverState *bs, + cache_clean_timer_init(bs, bdrv_get_aio_context(bs)); + } + +- qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); +- s->crypto_opts = r->crypto_opts; ++ //qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); ++ //s->crypto_opts = r->crypto_opts; + } + + static void qcow2_update_options_abort(BlockDriverState *bs, +@@ -1258,7 +1268,7 @@ static void qcow2_update_options_abort(BlockDriverState *bs, + if (r->refcount_block_cache) { + qcow2_cache_destroy(r->refcount_block_cache); + } +- qapi_free_QCryptoBlockOpenOptions(r->crypto_opts); ++ //qapi_free_QCryptoBlockOpenOptions(r->crypto_opts); + } + + static int coroutine_fn GRAPH_RDLOCK +@@ -1494,6 +1504,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, + s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1); + s->refcount_max += s->refcount_max - 1; + ++#if 0 + s->crypt_method_header = header.crypt_method; + if (s->crypt_method_header) { + if (bdrv_uses_whitelist() && +@@ -1521,6 +1532,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, + + bs->encrypted = true; + } ++#endif + + s->l2_bits = s->cluster_bits - ctz32(l2_entry_size(s)); + s->l2_size = 1 << s->l2_bits; +@@ -1705,6 +1717,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, + } + } + ++#if 0 + /* qcow2_read_extension may have set up the crypto context + * if the crypt method needs a header region, some methods + * don't need header extensions, so must check here +@@ -1728,6 +1741,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, + goto fail; + } + } ++#endif + + /* read the backing file name */ + if (header.backing_file_offset != 0) { +@@ -1913,8 +1927,8 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, + if (s->refcount_block_cache) { + qcow2_cache_destroy(s->refcount_block_cache); + } +- qcrypto_block_free(s->crypto); +- qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); ++ //qcrypto_block_free(s->crypto); ++ //qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); + return ret; + } + +@@ -1976,10 +1990,12 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) + { + BDRVQcow2State *s = bs->opaque; + ++#if 0 + if (bs->encrypted) { + /* Encryption works on a sector granularity */ + bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto); + } ++#endif + bs->bl.pwrite_zeroes_alignment = s->subcluster_size; + bs->bl.pdiscard_alignment = s->cluster_size; + } +@@ -2170,7 +2186,7 @@ qcow2_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, + + if ((type == QCOW2_SUBCLUSTER_NORMAL || + type == QCOW2_SUBCLUSTER_ZERO_ALLOC || +- type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC) && !s->crypto) { ++ type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC)/* && !s->crypto*/) { + *map = host_offset; + *file = s->data_file->bs; + status |= BDRV_BLOCK_OFFSET_VALID; +@@ -2225,6 +2241,7 @@ out: + return ret; + } + ++#if 0 + static int coroutine_fn GRAPH_RDLOCK + qcow2_co_preadv_encrypted(BlockDriverState *bs, + uint64_t host_offset, +@@ -2271,6 +2288,7 @@ fail: + + return ret; + } ++#endif + + typedef struct Qcow2AioTask { + AioTask task; +@@ -2353,8 +2371,10 @@ qcow2_co_preadv_task(BlockDriverState *bs, QCow2SubclusterType subc_type, + + case QCOW2_SUBCLUSTER_NORMAL: + if (bs->encrypted) { ++#if 0 + return qcow2_co_preadv_encrypted(bs, host_offset, + offset, bytes, qiov, qiov_offset); ++#endif + } + + BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_AIO); +@@ -2398,10 +2418,12 @@ qcow2_co_preadv_part(BlockDriverState *bs, int64_t offset, int64_t bytes, + while (bytes != 0 && aio_task_pool_status(aio) == 0) { + /* prepare next request */ + cur_bytes = MIN(bytes, INT_MAX); ++#if 0 + if (s->crypto) { + cur_bytes = MIN(cur_bytes, + QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); + } ++#endif + + qemu_co_mutex_lock(&s->lock); + ret = qcow2_get_host_offset(bs, offset, &cur_bytes, +@@ -2534,10 +2556,12 @@ handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) + if (!(s->data_file->bs->supported_zero_flags & BDRV_REQ_NO_FALLBACK)) { + return 0; + } ++#if 0 + + if (bs->encrypted) { + return 0; + } ++#endif + + for (m = l2meta; m != NULL; m = m->next) { + int ret; +@@ -2596,9 +2620,10 @@ int qcow2_co_pwritev_task(BlockDriverState *bs, uint64_t host_offset, + { + int ret; + BDRVQcow2State *s = bs->opaque; +- void *crypt_buf = NULL; +- QEMUIOVector encrypted_qiov; ++ //void *crypt_buf = NULL; ++ //QEMUIOVector encrypted_qiov; + ++#if 0 + if (bs->encrypted) { + assert(s->crypto); + assert(bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); +@@ -2618,6 +2643,7 @@ int qcow2_co_pwritev_task(BlockDriverState *bs, uint64_t host_offset, + qiov = &encrypted_qiov; + qiov_offset = 0; + } ++#endif + + /* Try to efficiently initialize the physical space with zeroes */ + ret = handle_alloc_space(bs, l2meta); +@@ -2653,7 +2679,7 @@ out_locked: + qcow2_handle_l2meta(bs, &l2meta, false); + qemu_co_mutex_unlock(&s->lock); + +- qemu_vfree(crypt_buf); ++ //qemu_vfree(crypt_buf); + + return ret; + } +@@ -2807,9 +2833,9 @@ qcow2_do_close(BlockDriverState *bs, bool close_data_file) + qcow2_cache_destroy(s->l2_table_cache); + qcow2_cache_destroy(s->refcount_block_cache); + +- qcrypto_block_free(s->crypto); +- s->crypto = NULL; +- qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); ++ //qcrypto_block_free(s->crypto); ++ //s->crypto = NULL; ++ //qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); + + g_free(s->unknown_header_fields); + cleanup_unknown_header_ext(bs); +@@ -2847,7 +2873,7 @@ qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp) + BDRVQcow2State *s = bs->opaque; + BdrvChild *data_file; + int flags = s->flags; +- QCryptoBlock *crypto = NULL; ++ // QCryptoBlock *crypto = NULL; + QDict *options; + int ret; + +@@ -2856,8 +2882,8 @@ qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp) + * that means we don't have to worry about reopening them here. + */ + +- crypto = s->crypto; +- s->crypto = NULL; ++// crypto = s->crypto; ++// s->crypto = NULL; + + /* + * Do not reopen s->data_file (i.e., have qcow2_do_close() not close it, +@@ -2885,7 +2911,7 @@ qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp) + return; + } + +- s->crypto = crypto; ++// s->crypto = crypto; + } + + static size_t header_ext_add(char *buf, uint32_t magic, const void *s, +@@ -2957,7 +2983,8 @@ int qcow2_update_header(BlockDriverState *bs) + .backing_file_size = 0, + .cluster_bits = cpu_to_be32(s->cluster_bits), + .size = cpu_to_be64(total_size), +- .crypt_method = cpu_to_be32(s->crypt_method_header), ++ //.crypt_method = cpu_to_be32(s->crypt_method_header), ++ .crypt_method = cpu_to_be32(QCOW_CRYPT_NONE), + .l1_size = cpu_to_be32(s->l1_size), + .l1_table_offset = cpu_to_be64(s->l1_table_offset), + .refcount_table_offset = cpu_to_be64(s->refcount_table_offset), +@@ -3030,6 +3057,7 @@ int qcow2_update_header(BlockDriverState *bs) + buflen -= ret; + } + ++#if 0 + /* Full disk encryption header pointer extension */ + if (s->crypto_header.offset != 0) { + s->crypto_header.offset = cpu_to_be64(s->crypto_header.offset); +@@ -3045,6 +3073,7 @@ int qcow2_update_header(BlockDriverState *bs) + buf += ret; + buflen -= ret; + } ++#endif + + /* + * Feature table. A mere 8 feature names occupies 392 bytes, and +@@ -3204,6 +3233,7 @@ qcow2_co_change_backing_file(BlockDriverState *bs, const char *backing_file, + return qcow2_update_header(bs); + } + ++#if 0 + static int coroutine_fn GRAPH_RDLOCK + qcow2_set_up_encryption(BlockDriverState *bs, + QCryptoBlockCreateOptions *cryptoopts, +@@ -3246,6 +3276,7 @@ qcow2_set_up_encryption(BlockDriverState *bs, + qcrypto_block_free(crypto); + return ret; + } ++#endif + + /** + * Preallocates metadata structures for data clusters between @offset (in the +@@ -3850,6 +3881,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) + } + } + ++#if 0 + /* Want encryption? There you go. */ + if (qcow2_opts->encrypt) { + bdrv_graph_co_rdlock(); +@@ -3860,7 +3892,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) + goto out; + } + } +- ++#endif + blk_co_unref(blk); + blk = NULL; + +@@ -5023,7 +5055,7 @@ static int GRAPH_RDLOCK qcow2_make_empty(BlockDriverState *bs) + + if (s->qcow_version >= 3 && !s->snapshots && !s->nb_bitmaps && + 3 + l1_clusters <= s->refcount_block_size && +- s->crypt_method_header != QCOW_CRYPT_LUKS && ++ /*s->crypt_method_header != QCOW_CRYPT_LUKS &&*/ + !has_data_file(bs)) { + /* The following function only works for qcow2 v3 images (it + * requires the dirty flag) and only as long as there are no +@@ -5083,7 +5115,7 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, + char *optstr; + PreallocMode prealloc; + bool has_backing_file; +- bool has_luks; ++ //bool has_luks; + bool extended_l2; + size_t l2e_size; + +@@ -5119,9 +5151,10 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, + g_free(optstr); + + optstr = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT); +- has_luks = optstr && strcmp(optstr, "luks") == 0; ++ //has_luks = optstr && strcmp(optstr, "luks") == 0; + g_free(optstr); + ++#if 0 + if (has_luks) { + g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL; + QDict *cryptoopts = qcow2_extract_crypto_opts(opts, "luks", errp); +@@ -5142,6 +5175,7 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, + + luks_payload_size = ROUND_UP(headerlen, cluster_size); + } ++#endif + + virtual_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + virtual_size = ROUND_UP(virtual_size, cluster_size); +@@ -5252,14 +5286,16 @@ qcow2_get_specific_info(BlockDriverState *bs, Error **errp) + { + BDRVQcow2State *s = bs->opaque; + ImageInfoSpecific *spec_info; +- QCryptoBlockInfo *encrypt_info = NULL; ++ //QCryptoBlockInfo *encrypt_info = NULL; + ++#if 0 + if (s->crypto != NULL) { + encrypt_info = qcrypto_block_get_info(s->crypto, errp); + if (!encrypt_info) { + return NULL; + } + } ++#endif + + spec_info = g_new(ImageInfoSpecific, 1); + *spec_info = (ImageInfoSpecific){ +@@ -5275,7 +5311,7 @@ qcow2_get_specific_info(BlockDriverState *bs, Error **errp) + Qcow2BitmapInfoList *bitmaps; + if (!qcow2_get_bitmap_info_list(bs, &bitmaps, errp)) { + qapi_free_ImageInfoSpecific(spec_info); +- qapi_free_QCryptoBlockInfo(encrypt_info); ++ // qapi_free_QCryptoBlockInfo(encrypt_info); + return NULL; + } + *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){ +@@ -5302,6 +5338,7 @@ qcow2_get_specific_info(BlockDriverState *bs, Error **errp) + assert(false); + } + ++#if 0 + if (encrypt_info) { + ImageInfoSpecificQCow2Encryption *qencrypt = + g_new(ImageInfoSpecificQCow2Encryption, 1); +@@ -5323,6 +5360,7 @@ qcow2_get_specific_info(BlockDriverState *bs, Error **errp) + + spec_info->u.qcow2.data->encrypt = qencrypt; + } ++#endif + + return spec_info; + } +@@ -5725,6 +5763,7 @@ qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); + } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FMT)) { + backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); ++#if 0 + } else if (g_str_has_prefix(desc->name, "encrypt.")) { + if (!s->crypto) { + error_setg(errp, +@@ -5737,6 +5776,7 @@ qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + return -ENOTSUP; + } + encryption_update = true; ++#endif + } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { + lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS, + lazy_refcounts); +@@ -5793,6 +5833,7 @@ qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + } + } + ++#if 0 + if (encryption_update) { + QDict *amend_opts_dict; + QCryptoBlockAmendOptions *amend_opts; +@@ -5819,6 +5860,7 @@ qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + return ret; + } + } ++#endif + + if (s->refcount_bits != refcount_bits) { + int refcount_order = ctz32(refcount_bits); +@@ -5938,10 +5980,11 @@ static int coroutine_fn qcow2_co_amend(BlockDriverState *bs, + bool force, + Error **errp) + { +- BlockdevAmendOptionsQcow2 *qopts = &opts->u.qcow2; +- BDRVQcow2State *s = bs->opaque; ++// BlockdevAmendOptionsQcow2 *qopts = &opts->u.qcow2; ++// BDRVQcow2State *s = bs->opaque; + int ret = 0; + ++#if 0 + if (qopts->encrypt) { + if (!s->crypto) { + error_setg(errp, "image is not encrypted, can't amend"); +@@ -5968,6 +6011,7 @@ static int coroutine_fn qcow2_co_amend(BlockDriverState *bs, + force, + errp); + } ++#endif + return ret; + } + +@@ -5981,7 +6025,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, + int64_t size, const char *message_format, ...) + { + BDRVQcow2State *s = bs->opaque; +- const char *node_name; ++ //const char *node_name; + char *message; + va_list ap; + +@@ -6005,12 +6049,14 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, + "corruption events will be suppressed\n", message); + } + ++#if 0 + node_name = bdrv_get_node_name(bs); + qapi_event_send_block_image_corrupted(bdrv_get_device_name(bs), + *node_name ? node_name : NULL, + message, offset >= 0, offset, + size >= 0, size, + fatal); ++#endif + g_free(message); + + if (fatal) { +@@ -6069,6 +6115,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, + static QemuOptsList qcow2_create_opts = { + .name = "qcow2-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head), ++#if 0 + .desc = { + { \ + .name = BLOCK_OPT_ENCRYPT, \ +@@ -6117,24 +6164,55 @@ static QemuOptsList qcow2_create_opts = { + QCOW_COMMON_OPTIONS, + { /* end of list */ } + } ++#else ++ .desc = { ++ { \ ++ .name = BLOCK_OPT_CLUSTER_SIZE, \ ++ .type = QEMU_OPT_SIZE, \ ++ .help = "qcow2 cluster size", \ ++ .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_EXTL2, \ ++ .type = QEMU_OPT_BOOL, \ ++ .help = "Extended L2 tables", \ ++ .def_value_str = "off" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_PREALLOC, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Preallocation mode (allowed values: off, " \ ++ "metadata, falloc, full)" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_COMPRESSION_TYPE, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Compression method used for image cluster " \ ++ "compression", \ ++ .def_value_str = "zlib" \ ++ }, ++ QCOW_COMMON_OPTIONS, ++ { /* end of list */ } ++ } ++#endif + }; + + static QemuOptsList qcow2_amend_opts = { + .name = "qcow2-amend-opts", + .head = QTAILQ_HEAD_INITIALIZER(qcow2_amend_opts.head), + .desc = { +- BLOCK_CRYPTO_OPT_DEF_LUKS_STATE("encrypt."), +- BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT("encrypt."), +- BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET("encrypt."), +- BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET("encrypt."), +- BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), ++// BLOCK_CRYPTO_OPT_DEF_LUKS_STATE("encrypt."), ++// BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT("encrypt."), ++// BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET("encrypt."), ++// BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET("encrypt."), ++// BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), + QCOW_COMMON_OPTIONS, + { /* end of list */ } + } + }; + + static const char *const qcow2_strong_runtime_opts[] = { +- "encrypt." BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET, ++// "encrypt." BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET, + + NULL + }; +diff --git a/qcow2/lib/qobject/block-qdict.c b/qcow2/lib/qobject/block-qdict.c +index 4a83bda2..0d2eba5b 100644 +--- a/qcow2/lib/qobject/block-qdict.c ++++ b/qcow2/lib/qobject/block-qdict.c +@@ -586,6 +586,7 @@ static QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) + return dst; + } + ++#if 0 + /** + * qdict_array_entries(): Returns the number of direct array entries if the + * sub-QDict of src specified by the prefix in subqdict (or src itself for +@@ -647,6 +648,7 @@ int qdict_array_entries(QDict *src, const char *subqdict) + + return i; + } ++#endif + + /** + * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all +diff --git a/qcow2/lib/qom/object.c b/qcow2/lib/qom/object.c +index 157a45c5..61b8556a 100644 +--- a/qcow2/lib/qom/object.c ++++ b/qcow2/lib/qom/object.c +@@ -14,14 +14,14 @@ + #include "hw/qdev-core.h" + #include "qapi/error.h" + #include "qom/object.h" +-#include "qom/object_interfaces.h" ++//#include "qom/object_interfaces.h" + #include "qemu/cutils.h" + #include "qemu/memalign.h" + #include "qapi/visitor.h" + #include "qapi/string-input-visitor.h" + #include "qapi/string-output-visitor.h" + #include "qapi/qobject-input-visitor.h" +-#include "qapi/forward-visitor.h" ++//#include "qapi/forward-visitor.h" + #include "qapi/qapi-builtin-visit.h" + #include "qapi/qmp/qjson.h" + #include "trace.h" +@@ -262,6 +262,7 @@ static size_t type_object_get_align(TypeImpl *ti) + return 0; + } + ++#if 0 + size_t object_type_get_instance_size(const char *typename) + { + TypeImpl *type = type_get_by_name(typename); +@@ -269,6 +270,7 @@ size_t object_type_get_instance_size(const char *typename) + g_assert(type != NULL); + return type_object_get_size(type); + } ++#endif + + static bool type_is_ancestor(TypeImpl *type, TypeImpl *target_type) + { +@@ -432,6 +434,7 @@ static void object_post_init_with_type(Object *obj, TypeImpl *ti) + } + } + ++#if 0 + bool object_apply_global_props(Object *obj, const GPtrArray *props, + Error **errp) + { +@@ -531,6 +534,7 @@ void object_apply_compat_props(Object *obj) + i == 2 ? &error_fatal : &error_abort); + } + } ++#endif + + static void object_class_property_init_all(Object *obj) + { +@@ -563,6 +567,7 @@ static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type + object_post_init_with_type(obj, type); + } + ++#if 0 + void object_initialize(void *data, size_t size, const char *typename) + { + TypeImpl *type = type_get_by_name(typename); +@@ -653,6 +658,7 @@ void object_initialize_child_internal(Object *parent, + object_initialize_child_with_props(parent, propname, child, size, type, + &error_abort, NULL); + } ++#endif + + static inline bool object_property_is_child(ObjectProperty *prop) + { +@@ -683,6 +689,7 @@ static void object_property_del_all(Object *obj) + g_hash_table_unref(obj->properties); + } + ++#if 0 + static void object_property_del_child(Object *obj, Object *child) + { + ObjectProperty *prop; +@@ -716,6 +723,7 @@ void object_unparent(Object *obj) + object_property_del_child(obj->parent, obj); + } + } ++#endif + + static void object_deinit(Object *obj, TypeImpl *type) + { +@@ -798,6 +806,7 @@ Object *object_new(const char *typename) + } + + ++#if 0 + Object *object_new_with_props(const char *typename, + Object *parent, + const char *id, +@@ -898,6 +907,7 @@ bool object_set_propv(Object *obj, + + return true; + } ++#endif + + + Object *object_dynamic_cast(Object *obj, const char *typename) +@@ -1051,16 +1061,19 @@ ObjectClass *object_get_class(Object *obj) + return obj->class; + } + ++#if 0 + bool object_class_is_abstract(ObjectClass *klass) + { + return klass->type->abstract; + } ++#endif + + const char *object_class_get_name(ObjectClass *klass) + { + return klass->type->name; + } + ++#if 0 + ObjectClass *object_class_by_name(const char *typename) + { + TypeImpl *type = type_get_by_name(typename); +@@ -1092,6 +1105,7 @@ ObjectClass *module_object_class_by_name(const char *typename) + #endif + return oc; + } ++#endif + + ObjectClass *object_class_get_parent(ObjectClass *class) + { +@@ -1106,6 +1120,7 @@ ObjectClass *object_class_get_parent(ObjectClass *class) + return type->class; + } + ++#if 0 + typedef struct OCFData + { + void (*fn)(ObjectClass *klass, void *opaque); +@@ -1217,6 +1232,7 @@ GSList *object_class_get_list_sorted(const char *implements_type, + return g_slist_sort(object_class_get_list(implements_type, include_abstract), + object_class_cmp); + } ++#endif + + Object *object_ref(void *objptr) + { +@@ -1403,6 +1419,7 @@ ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name) + return g_hash_table_lookup(klass->properties, name); + } + ++#if 0 + ObjectProperty *object_class_property_find_err(ObjectClass *klass, + const char *name, + Error **errp) +@@ -1424,6 +1441,7 @@ void object_property_del(Object *obj, const char *name) + } + g_hash_table_remove(obj->properties, name); + } ++#endif + + bool object_property_get(Object *obj, const char *name, Visitor *v, + Error **errp) +@@ -1464,6 +1482,7 @@ bool object_property_set(Object *obj, const char *name, Visitor *v, + return !*errp; + } + ++#if 0 + bool object_property_set_str(Object *obj, const char *name, + const char *value, Error **errp) + { +@@ -1473,6 +1492,7 @@ bool object_property_set_str(Object *obj, const char *name, + qobject_unref(qstr); + return ok; + } ++#endif + + char *object_property_get_str(Object *obj, const char *name, + Error **errp) +@@ -1497,6 +1517,7 @@ char *object_property_get_str(Object *obj, const char *name, + return retval; + } + ++#if 0 + bool object_property_set_link(Object *obj, const char *name, + Object *value, Error **errp) + { +@@ -1665,6 +1686,7 @@ uint64_t object_property_get_uint(Object *obj, const char *name, + qobject_unref(ret); + return retval; + } ++#endif + + typedef struct EnumProperty { + const QEnumLookup *lookup; +@@ -1829,6 +1851,7 @@ object_property_add_child(Object *obj, const char *name, + return object_property_try_add_child(obj, name, child, &error_abort); + } + ++#if 0 + void object_property_allow_set_link(const Object *obj, const char *name, + Object *val, Error **errp) + { +@@ -2056,6 +2079,7 @@ object_property_add_const_link(Object *obj, const char *name, + object_get_typename(target), target, + NULL, OBJ_PROP_LINK_DIRECT); + } ++#endif + + const char *object_get_canonical_path_component(const Object *obj) + { +@@ -2125,6 +2149,7 @@ Object *object_resolve_path_component(Object *parent, const char *part) + } + } + ++#if 0 + static Object *object_resolve_abs_path(Object *parent, + char **parts, + const char *typename) +@@ -2240,6 +2265,7 @@ Object *object_resolve_type_unambiguous(const char *typename, Error **errp) + } + return o; + } ++#endif + + typedef struct StringProperty + { +@@ -2771,6 +2797,7 @@ object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, + getter, setter, NULL, (void *)v); + } + ++#if 0 + typedef struct { + Object *target_obj; + char *target_name; +@@ -2849,6 +2876,7 @@ object_property_add_alias(Object *obj, const char *name, + target_prop->description); + return op; + } ++#endif + + void object_property_set_description(Object *obj, const char *name, + const char *description) +diff --git a/qcow2/lib/system/cpus.c b/qcow2/lib/system/cpus.c +index 1c818ff6..ed2b9ff5 100644 +--- a/qcow2/lib/system/cpus.c ++++ b/qcow2/lib/system/cpus.c +@@ -23,28 +23,28 @@ + */ + + #include "qemu/osdep.h" +-#include "monitor/monitor.h" ++//#include "monitor/monitor.h" + #include "qemu/coroutine-tls.h" + #include "qapi/error.h" +-#include "qapi/qapi-commands-machine.h" +-#include "qapi/qapi-commands-misc.h" +-#include "qapi/qapi-events-run-state.h" ++//#include "qapi/qapi-commands-machine.h" ++//#include "qapi/qapi-commands-misc.h" ++//#include "qapi/qapi-events-run-state.h" + #include "qapi/qmp/qerror.h" +-#include "exec/gdbstub.h" +-#include "sysemu/hw_accel.h" +-#include "exec/cpu-common.h" ++//#include "exec/gdbstub.h" ++//#include "sysemu/hw_accel.h" ++//#include "exec/cpu-common.h" + #include "qemu/thread.h" + #include "qemu/main-loop.h" +-#include "qemu/plugin.h" +-#include "sysemu/cpus.h" +-#include "qemu/guest-random.h" +-#include "hw/nmi.h" +-#include "sysemu/replay.h" +-#include "sysemu/runstate.h" ++//#include "qemu/plugin.h" ++//#include "sysemu/cpus.h" ++//#include "qemu/guest-random.h" ++//#include "hw/nmi.h" ++//#include "sysemu/replay.h" ++//#include "sysemu/runstate.h" + #include "sysemu/cpu-timers.h" +-#include "sysemu/whpx.h" +-#include "hw/boards.h" +-#include "hw/hw.h" ++//#include "sysemu/whpx.h" ++//#include "hw/boards.h" ++//#include "hw/hw.h" + #include "trace.h" + + #ifdef CONFIG_LINUX +@@ -68,6 +68,7 @@ + /* The Big QEMU Lock (BQL) */ + static QemuMutex bql; + ++#if 0 + /* + * The chosen accelerator is supposed to register this. + */ +@@ -414,17 +415,21 @@ static QemuThread io_thread; + static QemuCond qemu_cpu_cond; + /* system init */ + static QemuCond qemu_pause_cond; ++#endif + + void qemu_init_cpu_loop(void) + { ++#if 0 + qemu_init_sigbus(); + qemu_cond_init(&qemu_cpu_cond); + qemu_cond_init(&qemu_pause_cond); ++#endif + qemu_mutex_init(&bql); + +- qemu_thread_get_self(&io_thread); ++ //qemu_thread_get_self(&io_thread); + } + ++#if 0 + void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data) + { + do_run_on_cpu(cpu, func, data, &bql); +@@ -511,6 +516,7 @@ bool qemu_in_vcpu_thread(void) + { + return current_cpu && qemu_cpu_is_self(current_cpu); + } ++#endif + + QEMU_DEFINE_STATIC_CO_TLS(bool, bql_locked) + +@@ -544,6 +550,7 @@ void bql_unlock(void) + qemu_mutex_unlock(&bql); + } + ++#if 0 + void qemu_cond_wait_bql(QemuCond *cond) + { + qemu_cond_wait(cond, &bql); +@@ -875,4 +882,5 @@ void qmp_inject_nmi(Error **errp) + { + nmi_monitor_handle(monitor_get_cpu_index(monitor_cur()), errp); + } ++#endif + +diff --git a/qcow2/lib/util/aio-posix.c b/qcow2/lib/util/aio-posix.c +index 266c9dd3..fe40072f 100644 +--- a/qcow2/lib/util/aio-posix.c ++++ b/qcow2/lib/util/aio-posix.c +@@ -19,7 +19,7 @@ + #include "qemu/main-loop.h" + #include "qemu/rcu.h" + #include "qemu/rcu_queue.h" +-#include "qemu/sockets.h" ++//#include "qemu/sockets.h" + #include "qemu/cutils.h" + #include "trace.h" + #include "aio-posix.h" +@@ -686,7 +686,7 @@ bool aio_poll(AioContext *ctx, bool blocking) + /* This is the sweet spot, no adjustment needed */ + } else if (block_ns > ctx->poll_max_ns) { + /* We'd have to poll for too long, poll less */ +- int64_t old = ctx->poll_ns; ++ //int64_t old = ctx->poll_ns; + + if (ctx->poll_shrink) { + ctx->poll_ns /= ctx->poll_shrink; +@@ -698,7 +698,7 @@ bool aio_poll(AioContext *ctx, bool blocking) + } else if (ctx->poll_ns < ctx->poll_max_ns && + block_ns < ctx->poll_max_ns) { + /* There is room to grow, poll longer */ +- int64_t old = ctx->poll_ns; ++ //int64_t old = ctx->poll_ns; + int64_t grow = ctx->poll_grow; + + if (grow == 0) { +diff --git a/qcow2/lib/util/async.c b/qcow2/lib/util/async.c +index 3e3e4fc7..ce52d0a8 100644 +--- a/qcow2/lib/util/async.c ++++ b/qcow2/lib/util/async.c +@@ -162,9 +162,11 @@ void aio_bh_call(QEMUBH *bh) + MemReentrancyGuard *reentrancy_guard = bh->reentrancy_guard; + if (reentrancy_guard) { + last_engaged_in_io = reentrancy_guard->engaged_in_io; ++#if 0 + if (reentrancy_guard->engaged_in_io) { + trace_reentrant_aio(bh->ctx, bh->name); + } ++#endif + reentrancy_guard->engaged_in_io = true; + } + +diff --git a/qcow2/lib/util/bufferiszero.c b/qcow2/lib/util/bufferiszero.c +index 522146da..b86b90a2 100644 +--- a/qcow2/lib/util/bufferiszero.c ++++ b/qcow2/lib/util/bufferiszero.c +@@ -24,7 +24,7 @@ + #include "qemu/osdep.h" + #include "qemu/cutils.h" + #include "qemu/bswap.h" +-#include "host/cpuinfo.h" ++//#include "host/cpuinfo.h" + + typedef bool (*biz_accel_fn)(const void *, size_t); + +@@ -81,7 +81,12 @@ static bool buffer_is_zero_int_ge256(const void *buf, size_t len) + return t == 0; + } + +-#include "host/bufferiszero.c.inc" ++//#include "host/bufferiszero.c.inc" ++static biz_accel_fn const accel_table[1] = { ++ buffer_is_zero_int_ge256 ++}; ++ ++#define best_accel() 0 + + static biz_accel_fn buffer_is_zero_accel; + static unsigned accel_index; +diff --git a/qcow2/lib/util/cutils.c b/qcow2/lib/util/cutils.c +index 42364039..a0f76614 100644 +--- a/qcow2/lib/util/cutils.c ++++ b/qcow2/lib/util/cutils.c +@@ -1009,6 +1009,7 @@ int qemu_pstrcmp0(const char **str1, const char **str2) + return g_strcmp0(*str1, *str2); + } + ++#if 0 + static inline bool starts_with_prefix(const char *dir) + { + size_t prefix_len = strlen(CONFIG_PREFIX); +@@ -1040,6 +1041,7 @@ static inline const char *next_component(const char *dir, int *p_len) + *p_len = len; + return dir; + } ++#endif + + static const char *exec_dir; + +@@ -1139,11 +1141,12 @@ void qemu_init_exec_dir(const char *argv0) + if (p) { + exec_dir = g_path_get_dirname(p); + } else { +- exec_dir = CONFIG_BINDIR; ++ exec_dir = "/usr/bin" /*CONFIG_BINDIR*/; + } + #endif + } + ++#if 0 + const char *qemu_get_exec_dir(void) + { + return exec_dir; +@@ -1216,3 +1219,4 @@ char *get_relocated_path(const char *dir) + out: + return g_string_free(result, false); + } ++#endif +diff --git a/qcow2/lib/util/error-report.c b/qcow2/lib/util/error-report.c +index 1b17c11d..0554f186 100644 +--- a/qcow2/lib/util/error-report.c ++++ b/qcow2/lib/util/error-report.c +@@ -10,8 +10,10 @@ + * See the COPYING file in the top-level directory. + */ + ++#include ++ + #include "qemu/osdep.h" +-#include "monitor/monitor.h" ++//#include "monitor/monitor.h" + #include "qemu/error-report.h" + + /* +@@ -32,12 +34,11 @@ const char *error_guest_name; + int error_printf(const char *fmt, ...) + { + va_list ap; +- int ret; + + va_start(ap, fmt); +- ret = error_vprintf(fmt, ap); ++ vsyslog(LOG_ERR, fmt, ap); + va_end(ap); +- return ret; ++ return 0; + } + + static Location std_loc = { +@@ -144,7 +145,7 @@ static void print_loc(void) + int i; + const char *const *argp; + +- if (!monitor_cur() && g_get_prgname()) { ++ if (/*!monitor_cur() && */g_get_prgname()) { + error_printf("%s:", g_get_prgname()); + sep = " "; + } +@@ -188,14 +189,14 @@ static void vreport(report_type type, const char *fmt, va_list ap) + { + gchar *timestr; + +- if (message_with_timestamp && !monitor_cur()) { ++ if (message_with_timestamp/* && !monitor_cur()*/) { + timestr = real_time_iso8601(); + error_printf("%s ", timestr); + g_free(timestr); + } + + /* Only prepend guest name if -msg guest-name and -name guest=... are set */ +- if (error_with_guestname && error_guest_name && !monitor_cur()) { ++ if (error_with_guestname && error_guest_name/* && !monitor_cur()*/) { + error_printf("%s ", error_guest_name); + } + +@@ -212,8 +213,8 @@ static void vreport(report_type type, const char *fmt, va_list ap) + break; + } + +- error_vprintf(fmt, ap); +- error_printf("\n"); ++ vsyslog(LOG_ERR, fmt, ap); ++ //error_printf("\n"); + } + + /* +diff --git a/qcow2/lib/util/error.c b/qcow2/lib/util/error.c +index e5e24720..e84b4b16 100644 +--- a/qcow2/lib/util/error.c ++++ b/qcow2/lib/util/error.c +@@ -32,7 +32,7 @@ Error *error_warn; + static void error_handle(Error **errp, Error *err) + { + if (errp == &error_abort) { +- fprintf(stderr, "Unexpected error in %s() at %s:%d:\n", ++ error_report("Unexpected error in %s() at %s:%d:\n", + err->func, err->src, err->line); + error_report("%s", error_get_pretty(err)); + if (err->hint) { +@@ -231,6 +231,8 @@ const char *error_get_pretty(const Error *err) + + void error_report_err(Error *err) + { ++ error_report("libqcow2: error in %s() at %s:%d:\n", ++ err->func, err->src, err->line); + error_report("%s", error_get_pretty(err)); + if (err->hint) { + error_printf("%s", err->hint->str); +@@ -240,6 +242,8 @@ void error_report_err(Error *err) + + void warn_report_err(Error *err) + { ++ warn_report("libqcow2: error in %s() at %s:%d:\n", ++ err->func, err->src, err->line); + warn_report("%s", error_get_pretty(err)); + if (err->hint) { + error_printf("%s", err->hint->str); +diff --git a/qcow2/lib/util/hbitmap.c b/qcow2/lib/util/hbitmap.c +index 6d6e1b59..37cb7bc7 100644 +--- a/qcow2/lib/util/hbitmap.c ++++ b/qcow2/lib/util/hbitmap.c +@@ -944,6 +944,7 @@ void hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) + result->count = hb_count_between(result, 0, result->size - 1); + } + ++#if 0 + char *hbitmap_sha256(const HBitmap *bitmap, Error **errp) + { + size_t size = bitmap->sizes[HBITMAP_LEVELS - 1] * sizeof(unsigned long); +@@ -953,3 +954,4 @@ char *hbitmap_sha256(const HBitmap *bitmap, Error **errp) + + return hash; + } ++#endif +diff --git a/qcow2/lib/util/host-utils.c b/qcow2/lib/util/host-utils.c +index fb91bcba..6d0e2825 100644 +--- a/qcow2/lib/util/host-utils.c ++++ b/qcow2/lib/util/host-utils.c +@@ -267,6 +267,7 @@ void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow) + } + } + ++#if 0 + /* + * Unsigned 256-by-128 division. + * Returns the remainder via r. +@@ -446,3 +447,4 @@ Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor) + return rem; + } + } ++#endif +diff --git a/qcow2/lib/util/iov.c b/qcow2/lib/util/iov.c +index 7e73948f..14eb3cd1 100644 +--- a/qcow2/lib/util/iov.c ++++ b/qcow2/lib/util/iov.c +@@ -18,7 +18,7 @@ + + #include "qemu/osdep.h" + #include "qemu/iov.h" +-#include "qemu/sockets.h" ++//#include "qemu/sockets.h" + #include "qemu/cutils.h" + + size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt, +@@ -229,6 +229,7 @@ ssize_t iov_send_recv(int sockfd, const struct iovec *_iov, unsigned iov_cnt, + } + + ++#if 0 + void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, + FILE *fp, const char *prefix, size_t limit) + { +@@ -245,6 +246,7 @@ void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, + qemu_hexdump(fp, prefix, buf, size); + g_free(buf); + } ++#endif + + unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt, + const struct iovec *iov, unsigned int iov_cnt, +@@ -432,7 +434,7 @@ bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t offset, size_t bytes) + uint8_t *base = (uint8_t *)iov->iov_base + current_offset; + size_t len = MIN(iov->iov_len - current_offset, bytes); + +- if (!buffer_is_zero(base, len)) { ++ if (!buffer_is_zero((char*)base, len)) { + return false; + } + +diff --git a/qcow2/lib/util/main-loop.c b/qcow2/lib/util/main-loop.c +index a0386cfe..817e65a6 100644 +--- a/qcow2/lib/util/main-loop.c ++++ b/qcow2/lib/util/main-loop.c +@@ -27,13 +27,13 @@ + #include "qemu/cutils.h" + #include "qemu/timer.h" + #include "sysemu/cpu-timers.h" +-#include "sysemu/replay.h" ++//#include "sysemu/replay.h" + #include "qemu/main-loop.h" + #include "block/aio.h" + #include "block/thread-pool.h" + #include "qemu/error-report.h" + #include "qemu/queue.h" +-#include "qom/object.h" ++//#include "qom/object.h" + + #ifndef _WIN32 + #include +@@ -153,6 +153,14 @@ void qemu_notify_event(void) + + static GArray *gpollfds; + ++ ++void qemu_timer_notify_cb(void *opaque, QEMUClockType type) ++{ ++ if (!icount_enabled() || type != QEMU_CLOCK_VIRTUAL) { ++ qemu_notify_event(); ++ } ++} ++ + int qemu_init_main_loop(Error **errp) + { + int ret; +@@ -183,6 +191,7 @@ int qemu_init_main_loop(Error **errp) + return 0; + } + ++#if 0 + static void main_loop_update_params(EventLoopBase *base, Error **errp) + { + ERRP_GUARD(); +@@ -242,6 +251,7 @@ static void main_loop_register_types(void) + } + + type_init(main_loop_register_types) ++#endif + + static int max_priority; + +@@ -300,11 +310,11 @@ static int os_host_main_loop_wait(int64_t timeout) + glib_pollfds_fill(&timeout); + + bql_unlock(); +- replay_mutex_unlock(); ++ //replay_mutex_unlock(); + + ret = qemu_poll_ns((GPollFD *)gpollfds->data, gpollfds->len, timeout); + +- replay_mutex_lock(); ++ //replay_mutex_lock(); + bql_lock(); + + glib_pollfds_poll(); +diff --git a/qcow2/lib/util/memalign.c b/qcow2/lib/util/memalign.c +index c199ae70..622e805c 100644 +--- a/qcow2/lib/util/memalign.c ++++ b/qcow2/lib/util/memalign.c +@@ -62,7 +62,8 @@ void *qemu_try_memalign(size_t alignment, size_t size) + #elif defined(CONFIG_MEMALIGN) + ptr = memalign(alignment, size); + #else +- #error No function to allocate aligned memory available ++ ptr = aligned_alloc(alignment, size); ++ //#error No function to allocate aligned memory available + #endif + trace_qemu_memalign(alignment, size, ptr); + return ptr; +diff --git a/qcow2/lib/util/osdep.c b/qcow2/lib/util/osdep.c +index 77036983..f78d6cd3 100644 +--- a/qcow2/lib/util/osdep.c ++++ b/qcow2/lib/util/osdep.c +@@ -24,14 +24,14 @@ + #include "qemu/osdep.h" + #include "qapi/error.h" + #include "qemu/cutils.h" +-#include "qemu/sockets.h" ++//#include "qemu/sockets.h" + #include "qemu/error-report.h" +-#include "qemu/madvise.h" +-#include "qemu/mprotect.h" +-#include "qemu/hw-version.h" +-#include "monitor/monitor.h" ++//#include "qemu/madvise.h" ++//#include "qemu/mprotect.h" ++//#include "qemu/hw-version.h" ++//#include "monitor/monitor.h" + +-static const char *hw_version = QEMU_HW_VERSION; ++//static const char *hw_version = QEMU_HW_VERSION; + + int socket_set_cork(int fd, int v) + { +@@ -42,6 +42,7 @@ int socket_set_cork(int fd, int v) + #endif + } + ++#if 0 + int socket_set_nodelay(int fd) + { + int v = 1; +@@ -118,6 +119,7 @@ int qemu_mprotect_none(void *addr, size_t size) + return qemu_mprotect__osdep(addr, size, PROT_NONE); + #endif + } ++#endif + + #ifndef _WIN32 + +@@ -327,7 +329,7 @@ qemu_open_internal(const char *name, int flags, mode_t mode, Error **errp) + return -1; + } + +- return monitor_fdset_dup_fd_add(fdset_id, flags, errp); ++ return 0 /*monitor_fdset_dup_fd_add(fdset_id, flags, errp)*/; + } + #endif + +@@ -400,7 +402,7 @@ int qemu_open_old(const char *name, int flags, ...) + int qemu_close(int fd) + { + /* Close fd that was dup'd from an fdset */ +- monitor_fdset_dup_fd_remove(fd); ++ //monitor_fdset_dup_fd_remove(fd); + return close(fd); + } + +@@ -493,6 +495,7 @@ int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen) + return ret; + } + ++#if 0 + ssize_t qemu_send_full(int s, const void *buf, size_t count) + { + ssize_t ret = 0; +@@ -514,7 +517,9 @@ ssize_t qemu_send_full(int s, const void *buf, size_t count) + + return total; + } ++#endif + ++#if 0 + void qemu_set_hw_version(const char *version) + { + hw_version = version; +@@ -524,6 +529,7 @@ const char *qemu_hw_version(void) + { + return hw_version; + } ++#endif + + #ifdef _WIN32 + static void socket_cleanup(void) +diff --git a/qcow2/lib/util/oslib-posix.c b/qcow2/lib/util/oslib-posix.c +index 11b35e48..ee979a44 100644 +--- a/qcow2/lib/util/oslib-posix.c ++++ b/qcow2/lib/util/oslib-posix.c +@@ -31,20 +31,20 @@ + + #include + +-#include "sysemu/sysemu.h" ++//#include "sysemu/sysemu.h" + #include "trace.h" + #include "qapi/error.h" + #include "qemu/error-report.h" +-#include "qemu/madvise.h" +-#include "qemu/sockets.h" ++//#include "qemu/madvise.h" ++//#include "qemu/sockets.h" + #include "qemu/thread.h" + #include + #include "qemu/cutils.h" + #include "qemu/units.h" +-#include "qemu/thread-context.h" ++//#include "qemu/thread-context.h" + #include "qemu/main-loop.h" + +-#ifdef CONFIG_LINUX ++#if defined(__linux__) + #include + #endif + +@@ -58,14 +58,14 @@ + #include + #endif + +-#include "qemu/mmap-alloc.h" ++//#include "qemu/mmap-alloc.h" + + #define MAX_MEM_PREALLOC_THREAD_COUNT 16 + + struct MemsetThread; + +-static QLIST_HEAD(, MemsetContext) memset_contexts = +- QLIST_HEAD_INITIALIZER(memset_contexts); ++//static QLIST_HEAD(, MemsetContext) memset_contexts = ++// QLIST_HEAD_INITIALIZER(memset_contexts); + + typedef struct MemsetContext { + bool all_threads_created; +@@ -86,12 +86,12 @@ struct MemsetThread { + typedef struct MemsetThread MemsetThread; + + /* used by sigbus_handler() */ +-static MemsetContext *sigbus_memset_context; +-struct sigaction sigbus_oldact; +-static QemuMutex sigbus_mutex; ++//static MemsetContext *sigbus_memset_context; ++//struct sigaction sigbus_oldact; ++//static QemuMutex sigbus_mutex; + +-static QemuMutex page_mutex; +-static QemuCond page_cond; ++//static QemuMutex page_mutex; ++//static QemuCond page_cond; + + int qemu_get_thread_id(void) + { +@@ -188,6 +188,7 @@ fail_close: + return false; + } + ++#if 0 + /* alloc shared memory pages */ + void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared, + bool noreserve) +@@ -214,6 +215,7 @@ void qemu_anon_ram_free(void *ptr, size_t size) + trace_qemu_anon_ram_free(ptr, size); + qemu_ram_munmap(-1, ptr, size); + } ++#endif + + void qemu_socket_set_block(int fd) + { +@@ -253,6 +255,7 @@ void qemu_set_cloexec(int fd) + assert(f != -1); + } + ++#if 0 + int qemu_socketpair(int domain, int type, int protocol, int sv[2]) + { + int ret; +@@ -655,6 +658,7 @@ char *qemu_get_pid_name(pid_t pid) + + return name; + } ++#endif + + + void *qemu_alloc_stack(size_t *sz) +@@ -775,6 +779,7 @@ void sigaction_invoke(struct sigaction *action, + action->sa_sigaction(info->ssi_signo, &si, NULL); + } + ++#if 0 + size_t qemu_get_host_physmem(void) + { + #ifdef _SC_PHYS_PAGES +@@ -931,3 +936,4 @@ void qemu_close_all_open_fd(const int *skip, unsigned int nskip) + qemu_close_all_open_fd_fallback(skip, nskip, open_max); + } + } ++#endif +diff --git a/qcow2/lib/util/qemu-print.c b/qcow2/lib/util/qemu-print.c +index 69ba612f..98ca5474 100644 +--- a/qcow2/lib/util/qemu-print.c ++++ b/qcow2/lib/util/qemu-print.c +@@ -11,7 +11,7 @@ + */ + + #include "qemu/osdep.h" +-#include "monitor/monitor.h" ++//#include "monitor/monitor.h" + #include "qemu/qemu-print.h" + + /* +@@ -20,10 +20,12 @@ + */ + int qemu_vprintf(const char *fmt, va_list ap) + { ++#if 0 + Monitor *cur_mon = monitor_cur(); + if (cur_mon) { + return monitor_vprintf(cur_mon, fmt, ap); + } ++#endif + return vprintf(fmt, ap); + } + +@@ -48,9 +50,11 @@ int qemu_printf(const char *fmt, ...) + */ + int qemu_vfprintf(FILE *stream, const char *fmt, va_list ap) + { ++#if 0 + if (!stream) { + return monitor_vprintf(monitor_cur(), fmt, ap); + } ++#endif + return vfprintf(stream, fmt, ap); + } + +diff --git a/qcow2/lib/util/qemu-sockets.c b/qcow2/lib/util/qemu-sockets.c +index 60c44b2b..a54af944 100644 +--- a/qcow2/lib/util/qemu-sockets.c ++++ b/qcow2/lib/util/qemu-sockets.c +@@ -21,7 +21,7 @@ + #include + #endif /* CONFIG_AF_VSOCK */ + +-#include "monitor/monitor.h" ++//#include "monitor/monitor.h" + #include "qapi/clone-visitor.h" + #include "qapi/error.h" + #include "qapi/qapi-visit-sockets.h" +@@ -1137,21 +1137,23 @@ fail: + + static int socket_get_fd(const char *fdstr, Error **errp) + { +- Monitor *cur_mon = monitor_cur(); ++ //Monitor *cur_mon = monitor_cur(); + int fd; ++#if 0 + if (cur_mon) { + fd = monitor_get_fd(cur_mon, fdstr, errp); + if (fd < 0) { + return -1; + } + } else { ++#endif + if (qemu_strtoi(fdstr, NULL, 10, &fd) < 0) { + error_setg_errno(errp, errno, + "Unable to parse FD number %s", + fdstr); + return -1; + } +- } ++ //} + if (!fd_is_socket(fd)) { + error_setg(errp, "File descriptor '%s' is not a socket", fdstr); + close(fd); +@@ -1192,9 +1194,11 @@ int socket_connect(SocketAddress *addr, Error **errp) + fd = unix_connect_saddr(&addr->u.q_unix, errp); + break; + ++#if 0 + case SOCKET_ADDRESS_TYPE_FD: + fd = socket_get_fd(addr->u.fd.str, errp); + break; ++#endif + + case SOCKET_ADDRESS_TYPE_VSOCK: + fd = vsock_connect_saddr(&addr->u.vsock, errp); +@@ -1220,6 +1224,7 @@ int socket_listen(SocketAddress *addr, int num, Error **errp) + fd = unix_listen_saddr(&addr->u.q_unix, num, errp); + break; + ++#if 0 + case SOCKET_ADDRESS_TYPE_FD: + fd = socket_get_fd(addr->u.fd.str, errp); + if (fd < 0) { +@@ -1242,6 +1247,7 @@ int socket_listen(SocketAddress *addr, int num, Error **errp) + return -1; + } + break; ++#endif + + case SOCKET_ADDRESS_TYPE_VSOCK: + fd = vsock_listen_saddr(&addr->u.vsock, num, errp); +@@ -1436,6 +1442,7 @@ SocketAddress *socket_remote_address(int fd, Error **errp) + } + + ++#if 0 + SocketAddress *socket_address_flatten(SocketAddressLegacy *addr_legacy) + { + SocketAddress *addr; +@@ -1473,3 +1480,4 @@ SocketAddress *socket_address_flatten(SocketAddressLegacy *addr_legacy) + + return addr; + } ++#endif +diff --git a/qcow2/lib/util/qemu-thread-posix.c b/qcow2/lib/util/qemu-thread-posix.c +index b2e26e21..f02bf387 100644 +--- a/qcow2/lib/util/qemu-thread-posix.c ++++ b/qcow2/lib/util/qemu-thread-posix.c +@@ -15,7 +15,7 @@ + #include "qemu/atomic.h" + #include "qemu/notify.h" + #include "qemu-thread-common.h" +-#include "qemu/tsan.h" ++//#include "qemu/tsan.h" + #include "qemu/bitmap.h" + + #ifdef CONFIG_PTHREAD_SET_NAME_NP +@@ -518,7 +518,7 @@ static void *qemu_thread_start(void *args) + pthread_set_name_np(pthread_self(), qemu_thread_args->name); + # endif + } +- QEMU_TSAN_ANNOTATE_THREAD_NAME(qemu_thread_args->name); ++ //QEMU_TSAN_ANNOTATE_THREAD_NAME(qemu_thread_args->name); + g_free(qemu_thread_args->name); + g_free(qemu_thread_args); + +diff --git a/qcow2/lib/util/qemu-timer.c b/qcow2/lib/util/qemu-timer.c +index 6b1533bc..bdef6238 100644 +--- a/qcow2/lib/util/qemu-timer.c ++++ b/qcow2/lib/util/qemu-timer.c +@@ -27,8 +27,8 @@ + #include "qemu/timer.h" + #include "qemu/lockable.h" + #include "sysemu/cpu-timers.h" +-#include "sysemu/replay.h" +-#include "sysemu/cpus.h" ++//#include "sysemu/replay.h" ++//#include "sysemu/cpus.h" + + #ifdef CONFIG_POSIX + #include +@@ -522,14 +522,18 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) + case QEMU_CLOCK_VIRTUAL: + break; + case QEMU_CLOCK_HOST: ++#if 0 + if (!replay_checkpoint(CHECKPOINT_CLOCK_HOST)) { + goto out; + } ++#endif + break; + case QEMU_CLOCK_VIRTUAL_RT: ++#if 0 + if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL_RT)) { + goto out; + } ++#endif + break; + } + +@@ -552,6 +556,7 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) + */ + break; + } ++#if 0 + /* Checkpoint for virtual clock is redundant in cases where + * it's being triggered with only non-EXTERNAL timers, because + * these timers don't change guest state directly. +@@ -563,6 +568,7 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) + qemu_mutex_unlock(&timer_list->active_timers_lock); + goto out; + } ++#endif + + /* remove timer from the list before calling the callback */ + timer_list->active_timers = ts->next; +@@ -604,6 +610,7 @@ void timerlistgroup_deinit(QEMUTimerListGroup *tlg) + QEMUClockType type; + for (type = 0; type < QEMU_CLOCK_MAX; type++) { + timerlist_free(tlg->tl[type]); ++ tlg->tl[type] = NULL; + } + } + +@@ -637,18 +644,28 @@ int64_t qemu_clock_get_ns(QEMUClockType type) + return get_clock(); + default: + case QEMU_CLOCK_VIRTUAL: ++#if 0 + return cpus_get_virtual_clock(); ++#else ++ return get_clock(); ++#endif + case QEMU_CLOCK_HOST: +- return REPLAY_CLOCK(REPLAY_CLOCK_HOST, get_clock_realtime()); ++ return /*REPLAY_CLOCK(REPLAY_CLOCK_HOST,*/ get_clock_realtime()/*)*/; + case QEMU_CLOCK_VIRTUAL_RT: ++#if 0 + return REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, cpu_get_clock()); ++#else ++ assert(0); ++#endif + } + } + ++#if 0 + static void qemu_virtual_clock_set_ns(int64_t time) + { + return cpus_set_virtual_clock(time); + } ++#endif + + void init_clocks(QEMUTimerListNotifyCB *notify_cb) + { +@@ -681,6 +698,7 @@ bool qemu_clock_run_all_timers(void) + return progress; + } + ++#if 0 + int64_t qemu_clock_advance_virtual_time(int64_t dest) + { + int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +@@ -711,3 +729,4 @@ int64_t qemu_clock_advance_virtual_time(int64_t dest) + + return clock; + } ++#endif +diff --git a/qcow2/lib/util/qsp.c b/qcow2/lib/util/qsp.c +index 6b783e2e..5e54c4be 100644 +--- a/qcow2/lib/util/qsp.c ++++ b/qcow2/lib/util/qsp.c +@@ -61,10 +61,11 @@ + #include "qemu/qemu-print.h" + #include "qemu/thread.h" + #include "qemu/timer.h" +-#include "qemu/qht.h" ++//#include "qemu/qht.h" + #include "qemu/rcu.h" +-#include "qemu/xxhash.h" ++//#include "qemu/xxhash.h" + ++#if 0 + enum QSPType { + QSP_MUTEX, + QSP_BQL_MUTEX, +@@ -124,15 +125,19 @@ static const char * const qsp_typenames[] = { + [QSP_CONDVAR] = "condvar", + }; + ++#endif + QemuMutexLockFunc bql_mutex_lock_func = qemu_mutex_lock_impl; + QemuMutexLockFunc qemu_mutex_lock_func = qemu_mutex_lock_impl; ++#if 0 + QemuMutexTrylockFunc qemu_mutex_trylock_func = qemu_mutex_trylock_impl; + QemuRecMutexLockFunc qemu_rec_mutex_lock_func = qemu_rec_mutex_lock_impl; + QemuRecMutexTrylockFunc qemu_rec_mutex_trylock_func = + qemu_rec_mutex_trylock_impl; ++#endif + QemuCondWaitFunc qemu_cond_wait_func = qemu_cond_wait_impl; + QemuCondTimedWaitFunc qemu_cond_timedwait_func = qemu_cond_timedwait_impl; + ++#if 0 + /* + * It pays off to _not_ hash callsite->file; hashing a string is slow, and + * without it we still get a pretty unique hash. +@@ -811,3 +816,4 @@ void qsp_reset(void) + call_rcu(old, qsp_snapshot_destroy, rcu); + } + } ++#endif +diff --git a/qcow2/lib/util/yank.c b/qcow2/lib/util/yank.c +index eaac5053..be696c91 100644 +--- a/qcow2/lib/util/yank.c ++++ b/qcow2/lib/util/yank.c +@@ -12,7 +12,7 @@ + #include "qemu/thread.h" + #include "qemu/queue.h" + #include "qemu/lockable.h" +-#include "qapi/qapi-commands-yank.h" ++//#include "qapi/qapi-commands-yank.h" + #include "qapi/qapi-visit-yank.h" + #include "qapi/clone-visitor.h" + #include "qemu/yank.h" +diff --git a/qcow2/qemu-img.c b/qcow2/qemu-img.c +index 7668f867..60db40b2 100644 +--- a/qcow2/qemu-img.c ++++ b/qcow2/qemu-img.c +@@ -25,9 +25,9 @@ + #include "qemu/osdep.h" + #include + +-#include "qemu/help-texts.h" ++//#include "qemu/help-texts.h" + #include "qemu/qemu-progress.h" +-#include "qemu-version.h" ++//#include "qemu-version.h" + #include "qapi/error.h" + #include "qapi/qapi-commands-block-core.h" + #include "qapi/qapi-visit-block-core.h" +@@ -38,25 +38,24 @@ + #include "qemu/config-file.h" + #include "qemu/option.h" + #include "qemu/error-report.h" +-#include "qemu/log.h" ++//#include "qemu/log.h" + #include "qemu/main-loop.h" + #include "qemu/module.h" +-#include "qemu/sockets.h" ++//#include "qemu/sockets.h" + #include "qemu/units.h" + #include "qemu/memalign.h" +-#include "qom/object_interfaces.h" ++//#include "qom/object_interfaces.h" + #include "sysemu/block-backend.h" + #include "block/block_int.h" + #include "block/blockjob.h" + #include "block/dirty-bitmap.h" + #include "block/qapi.h" +-#include "crypto/init.h" +-#include "trace/control.h" +-#include "qemu/throttle.h" +-#include "block/throttle-groups.h" ++//#include "crypto/init.h" ++//#include "trace/control.h" ++//#include "qemu/throttle.h" ++//#include "block/throttle-groups.h" + +-#define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \ +- "\n" QEMU_COPYRIGHT "\n" ++#define QEMU_IMG_VERSION "qemu-img version 9.1.1" + + typedef struct img_cmd_t { + const char *name; +@@ -230,7 +229,7 @@ void help(void) + + printf("%s\nSupported formats:", help_msg); + bdrv_iterate_format(format_print, NULL, false); +- printf("\n\n" QEMU_HELP_BOTTOM "\n"); ++ //printf("\n\n" QEMU_HELP_BOTTOM "\n"); + exit(EXIT_SUCCESS); + } + +@@ -563,7 +562,7 @@ static int img_create(int argc, char **argv) + flags |= BDRV_O_NO_BACKING; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + } + } +@@ -790,7 +789,7 @@ static int img_check(int argc, char **argv) + force_share = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -930,6 +929,8 @@ static void run_block_job(BlockJob *job, Error **errp) + progress = (float)progress_current / progress_total * 100.f; + } + qemu_progress_print(progress, 0); ++ printf("(%ld/%ld)\r", progress_current, progress_total); ++ fflush(stdout); + job_lock(); + } while (!job_is_ready_locked(&job->job) && + !job_is_completed_locked(&job->job)); +@@ -1014,7 +1015,7 @@ static int img_commit(int argc, char **argv) + } + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -1124,6 +1125,7 @@ unref_backing: + + done: + qemu_progress_end(); ++ printf("\n"); + + /* + * Manually inactivate the image first because this way we can know whether +@@ -1423,6 +1425,7 @@ static int img_compare(int argc, char **argv) + break; + case OPTION_OBJECT: + { ++#if 0 + Error *local_err = NULL; + + if (!user_creatable_add_from_str(optarg, &local_err)) { +@@ -1434,6 +1437,7 @@ static int img_compare(int argc, char **argv) + exit(EXIT_SUCCESS); + } + } ++#endif + break; + } + case OPTION_IMAGE_OPTS: +@@ -2222,6 +2226,7 @@ static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst, + + static void set_rate_limit(BlockBackend *blk, int64_t rate_limit) + { ++#if 0 + ThrottleConfig cfg; + + throttle_config_init(&cfg); +@@ -2229,6 +2234,7 @@ static void set_rate_limit(BlockBackend *blk, int64_t rate_limit) + + blk_io_limits_enable(blk, CONVERT_THROTTLE_GROUP); + blk_set_io_limits(blk, &cfg); ++#endif + } + + static int img_convert(int argc, char **argv) +@@ -2384,7 +2390,7 @@ static int img_convert(int argc, char **argv) + } + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -3051,7 +3057,7 @@ static int img_info(int argc, char **argv) + chain = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -3291,7 +3297,7 @@ static int img_map(int argc, char **argv) + } + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -3451,7 +3457,7 @@ static int img_snapshot(int argc, char **argv) + force_share = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -3608,7 +3614,7 @@ static int img_rebase(int argc, char **argv) + quiet = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -4093,7 +4099,7 @@ static int img_resize(int argc, char **argv) + quiet = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -4297,7 +4303,7 @@ static int img_amend(int argc, char **argv) + quiet = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -4870,7 +4876,7 @@ static int img_bitmap(int argc, char **argv) + merge = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -5145,7 +5151,7 @@ static int img_dd(int argc, char **argv) + force_share = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -5405,7 +5411,7 @@ static int img_measure(int argc, char **argv) + force_share = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -5555,14 +5561,16 @@ int main(int argc, char **argv) + signal(SIGPIPE, SIG_IGN); + #endif + +- socket_init(); ++ //socket_init(); + error_init(argv[0]); + module_call_init(MODULE_INIT_TRACE); + qemu_init_exec_dir(argv[0]); + + qemu_init_main_loop(&error_fatal); ++ qemu_init_cpu_loop(); ++ bql_lock(); + +- qcrypto_init(&error_fatal); ++ //qcrypto_init(&error_fatal); + + module_call_init(MODULE_INIT_QOM); + bdrv_init(); +@@ -5571,7 +5579,7 @@ int main(int argc, char **argv) + } + + qemu_add_opts(&qemu_source_opts); +- qemu_add_opts(&qemu_trace_opts); ++ //qemu_add_opts(&qemu_trace_opts); + + while ((c = getopt_long(argc, argv, "+:hVT:", long_options, NULL)) != -1) { + switch (c) { +@@ -5588,7 +5596,7 @@ int main(int argc, char **argv) + printf(QEMU_IMG_VERSION); + return 0; + case 'T': +- trace_opt_parse(optarg); ++ //trace_opt_parse(optarg); + break; + } + } +@@ -5603,11 +5611,11 @@ int main(int argc, char **argv) + argv += optind; + qemu_reset_optind(); + +- if (!trace_init_backends()) { +- exit(1); +- } +- trace_init_file(); +- qemu_set_log(LOG_TRACE, &error_fatal); ++ //if (!trace_init_backends()) { ++ // exit(1); ++ //} ++ //trace_init_file(); ++ //qemu_set_log(LOG_TRACE, &error_fatal); + + /* find the command */ + for (cmd = img_cmds; cmd->name != NULL; cmd++) { diff --git a/SOURCES/0026-libqcow2-fix-support-for-old-components-gcc-glibc-gl.patch b/SOURCES/0026-libqcow2-fix-support-for-old-components-gcc-glibc-gl.patch new file mode 100644 index 0000000..ecfd51c --- /dev/null +++ b/SOURCES/0026-libqcow2-fix-support-for-old-components-gcc-glibc-gl.patch @@ -0,0 +1,145 @@ +From 844e8cb3a6c61f4ad805982caf3b62470e080b95 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Tue, 14 Jan 2025 11:41:26 +0100 +Subject: [PATCH] libqcow2: fix support for old components (gcc, glibc, glib, + gnutls) + +XCP-ng-8.3 doesn't have components recent enough for qemu sources. +Fix a few things to support old tools. + +Signed-off-by: Anthoine Bourgeois +--- + include/glib-compat.h | 4 ++-- + include/qemu/config-host.h | 2 +- + include/qemu/host-utils.h | 2 ++ + qcow2/lib/block/file-posix.c | 2 ++ + qcow2/lib/util/error-report.c | 2 +- + qcow2/lib/util/qemu-thread-posix.c | 2 ++ + qcow2/lib/util/thread-pool.c | 5 +++-- + 7 files changed, 13 insertions(+), 6 deletions(-) + +diff --git a/include/glib-compat.h b/include/glib-compat.h +index 86be439b..e4a23999 100644 +--- a/include/glib-compat.h ++++ b/include/glib-compat.h +@@ -19,12 +19,12 @@ + /* Ask for warnings for anything that was marked deprecated in + * the defined version, or before. It is a candidate for rewrite. + */ +-#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_66 ++#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_56 + + /* Ask for warnings if code tries to use function that did not + * exist in the defined version. These risk breaking builds + */ +-#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_66 ++#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_56 + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +diff --git a/include/qemu/config-host.h b/include/qemu/config-host.h +index c0b00bf6..838d4bcc 100644 +--- a/include/qemu/config-host.h ++++ b/include/qemu/config-host.h +@@ -441,7 +441,7 @@ + + #define HAVE_BTRFS_H + +-#define HAVE_COPY_FILE_RANGE ++//#define HAVE_COPY_FILE_RANGE + + #define HAVE_DRM_H + +diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h +index 12ae37af..b78a2b21 100644 +--- a/include/qemu/host-utils.h ++++ b/include/qemu/host-utils.h +@@ -438,6 +438,7 @@ static inline uint64_t uabs64(int64_t v) + return v < 0 ? -v : v; + } + ++#if 0 + /** + * sadd32_overflow - addition with overflow indication + * @x, @y: addends +@@ -681,6 +682,7 @@ static inline uint64_t usub64_borrow(uint64_t x, uint64_t y, bool *pborrow) + return x; + #endif + } ++#endif + + /* Host type specific sizes of these routines. */ + +diff --git a/qcow2/lib/block/file-posix.c b/qcow2/lib/block/file-posix.c +index 3223c75e..0960f25f 100644 +--- a/qcow2/lib/block/file-posix.c ++++ b/qcow2/lib/block/file-posix.c +@@ -2008,6 +2008,7 @@ static int handle_aiocb_write_zeroes_unmap(void *opaque) + return handle_aiocb_write_zeroes(aiocb); + } + ++#if __GLIBC_MINOR__ < 27 + #ifndef HAVE_COPY_FILE_RANGE + static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, + off_t *out_off, size_t len, unsigned int flags) +@@ -2021,6 +2022,7 @@ static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, + #endif + } + #endif ++#endif + + /* + * parse_zone - Fill a zone descriptor +diff --git a/qcow2/lib/util/error-report.c b/qcow2/lib/util/error-report.c +index 0554f186..b3936a3c 100644 +--- a/qcow2/lib/util/error-report.c ++++ b/qcow2/lib/util/error-report.c +@@ -174,7 +174,7 @@ static char * + real_time_iso8601(void) + { + g_autoptr(GDateTime) dt = g_date_time_new_now_utc(); +- return g_date_time_format_iso8601(dt); ++ return g_date_time_format (dt, "%Y-%m-%dT%H:%M:%S"); + } + + /* +diff --git a/qcow2/lib/util/qemu-thread-posix.c b/qcow2/lib/util/qemu-thread-posix.c +index f02bf387..078833c7 100644 +--- a/qcow2/lib/util/qemu-thread-posix.c ++++ b/qcow2/lib/util/qemu-thread-posix.c +@@ -534,7 +534,9 @@ static void *qemu_thread_start(void *args) + */ + #pragma GCC diagnostic push + #ifndef __clang__ ++#if __GNUC_PREREQ(8,0) + #pragma GCC diagnostic ignored "-Wstringop-overflow" ++#endif + #endif + + pthread_cleanup_push(qemu_thread_atexit_notify, NULL); +diff --git a/qcow2/lib/util/thread-pool.c b/qcow2/lib/util/thread-pool.c +index 27eb777e..baa5adcc 100644 +--- a/qcow2/lib/util/thread-pool.c ++++ b/qcow2/lib/util/thread-pool.c +@@ -297,6 +297,7 @@ void thread_pool_submit(ThreadPoolFunc *func, void *arg) + + void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) + { ++ int i; + qemu_mutex_lock(&pool->lock); + + pool->min_threads = ctx->thread_pool_min; +@@ -311,11 +312,11 @@ void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) + * - Do nothing. The current number of threads fall in between the min and + * max thresholds. We'll let the pool manage itself. + */ +- for (int i = pool->cur_threads; i < pool->min_threads; i++) { ++ for (i = pool->cur_threads; i < pool->min_threads; i++) { + spawn_thread(pool); + } + +- for (int i = pool->cur_threads; i > pool->max_threads; i--) { ++ for (i = pool->cur_threads; i > pool->max_threads; i--) { + qemu_cond_signal(&pool->request_cond); + } + diff --git a/SOURCES/0027-tapdisk-protect-td_vbd_t-structure.patch b/SOURCES/0027-tapdisk-protect-td_vbd_t-structure.patch new file mode 100644 index 0000000..81b0dfc --- /dev/null +++ b/SOURCES/0027-tapdisk-protect-td_vbd_t-structure.patch @@ -0,0 +1,562 @@ +From 37450a2c1d3c5b520c3f38ecb709b8e89b128803 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:33 +0100 +Subject: [PATCH] tapdisk: protect td_vbd_t structure + +libqcow2 spawns a thread to handle IO requests to qcow2 layer. +Add a mutex to protect vbd structure from libqcow2 concurrency. + +This commit also voids access to request after being issued. + +This is the first part of tapdisk threading protection. + +Signed-off-by: Anthoine Bourgeois +Signed-off-by: Emmanuel Varagnat +--- + drivers/tapdisk-vbd.c | 138 ++++++++++++++++++++++++++++++++++++------ + drivers/tapdisk-vbd.h | 2 + + drivers/tapdisk.h | 2 +- + 3 files changed, 122 insertions(+), 20 deletions(-) + +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 433375d6..6e1f95d3 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -114,6 +114,8 @@ tapdisk_vbd_create(uint16_t uuid) + INIT_LIST_HEAD(&vbd->next); + INIT_LIST_HEAD(&vbd->rings); + INIT_LIST_HEAD(&vbd->dead_rings); ++ pthread_mutex_init(&vbd->mutex, NULL); ++ + tapdisk_vbd_mark_progress(vbd); + + return vbd; +@@ -279,7 +281,9 @@ tapdisk_vbd_close_vdi(td_vbd_t *vbd) + vbd->retired = NULL; + } + ++ pthread_mutex_lock(&vbd->mutex); + td_flag_set(vbd->state, TD_VBD_CLOSED); ++ pthread_mutex_unlock(&vbd->mutex); + } + + static int +@@ -724,6 +728,7 @@ tapdisk_vbd_queue_count(td_vbd_t *vbd, int *new, + f = 0; + c = 0; + ++ pthread_mutex_lock(&vbd->mutex); + tapdisk_vbd_for_each_request(vreq, tvreq, &vbd->new_requests) + n++; + +@@ -735,6 +740,7 @@ tapdisk_vbd_queue_count(td_vbd_t *vbd, int *new, + + tapdisk_vbd_for_each_request(vreq, tvreq, &vbd->completed_requests) + c++; ++ pthread_mutex_unlock(&vbd->mutex); + + *new = n; + *pending = p; +@@ -747,8 +753,12 @@ tapdisk_vbd_shutdown(td_vbd_t *vbd) + { + int new, pending, failed, completed; + +- if (!list_empty(&vbd->pending_requests)) ++ pthread_mutex_lock(&vbd->mutex); ++ if (!list_empty(&vbd->pending_requests)) { ++ pthread_mutex_unlock(&vbd->mutex); + return -EAGAIN; ++ } ++ pthread_mutex_unlock(&vbd->mutex); + + tapdisk_vbd_queue_count(vbd, &new, &pending, &failed, &completed); + +@@ -773,6 +783,7 @@ tapdisk_vbd_shutdown(td_vbd_t *vbd) + void + tapdisk_vbd_free(td_vbd_t *vbd) + { ++ pthread_mutex_destroy(&vbd->mutex); + free(vbd->name); + free(vbd->encryption.encryption_key); + free(vbd); +@@ -781,6 +792,7 @@ tapdisk_vbd_free(td_vbd_t *vbd) + int + tapdisk_vbd_close(td_vbd_t *vbd) + { ++ pthread_mutex_lock(&vbd->mutex); + /* + * don't close if any requests are pending in the aio layer + */ +@@ -797,10 +809,12 @@ tapdisk_vbd_close(td_vbd_t *vbd) + !list_empty(&vbd->completed_requests))) + goto fail; + ++ pthread_mutex_unlock(&vbd->mutex); + return tapdisk_vbd_shutdown(vbd); + + fail: + td_flag_set(vbd->state, TD_VBD_SHUTDOWN_REQUESTED); ++ pthread_mutex_unlock(&vbd->mutex); + DBG(TLOG_WARN, "%s: requests pending\n", vbd->name); + return -EAGAIN; + } +@@ -833,12 +847,18 @@ tapdisk_vbd_debug(td_vbd_t *vbd) + static void + tapdisk_vbd_drop_log(td_vbd_t *vbd) + { +- if (td_flag_test(vbd->state, TD_VBD_LOG_DROPPED)) ++ pthread_mutex_lock(&vbd->mutex); ++ if (td_flag_test(vbd->state, TD_VBD_LOG_DROPPED)) { ++ pthread_mutex_unlock(&vbd->mutex); + return; ++ } ++ pthread_mutex_unlock(&vbd->mutex); + + tapdisk_vbd_debug(vbd); + tlog_precious(0); ++ pthread_mutex_lock(&vbd->mutex); + td_flag_set(vbd->state, TD_VBD_LOG_DROPPED); ++ pthread_mutex_unlock(&vbd->mutex); + } + + int +@@ -863,8 +883,12 @@ tapdisk_vbd_queue_ready(td_vbd_t *vbd) + int + tapdisk_vbd_retry_needed(td_vbd_t *vbd) + { +- return !(list_empty(&vbd->failed_requests) && ++ bool retry; ++ pthread_mutex_lock(&vbd->mutex); ++ retry = !(list_empty(&vbd->failed_requests) && + list_empty(&vbd->new_requests)); ++ pthread_mutex_unlock(&vbd->mutex); ++ return retry; + } + + int +@@ -876,13 +900,16 @@ tapdisk_vbd_lock(td_vbd_t *vbd) + int + tapdisk_vbd_quiesce_queue(td_vbd_t *vbd) + { ++ pthread_mutex_lock(&vbd->mutex); + if (!list_empty(&vbd->pending_requests)) { + td_flag_set(vbd->state, TD_VBD_QUIESCE_REQUESTED); ++ pthread_mutex_unlock(&vbd->mutex); + return -EAGAIN; + } + + td_flag_clear(vbd->state, TD_VBD_QUIESCE_REQUESTED); + td_flag_set(vbd->state, TD_VBD_QUIESCED); ++ pthread_mutex_unlock(&vbd->mutex); + return 0; + } + +@@ -967,11 +994,13 @@ tapdisk_vbd_pause(td_vbd_t *vbd) + /* Don't guard this one as at this point the pause operation is complete */ + INFO("pause completed\n"); + ++ pthread_mutex_lock(&vbd->mutex); + if (!list_empty(&vbd->failed_requests)) + INFO("warning: failed requests pending\n"); + + td_flag_clear(vbd->state, TD_VBD_PAUSE_REQUESTED); + td_flag_set(vbd->state, TD_VBD_PAUSED); ++ pthread_mutex_unlock(&vbd->mutex); + + return 0; + } +@@ -984,10 +1013,13 @@ tapdisk_vbd_resume(td_vbd_t *vbd, const char *name) + + DBG(TLOG_DBG, "resume requested\n"); + ++ pthread_mutex_lock(&vbd->mutex); + if (!td_flag_test(vbd->state, TD_VBD_PAUSED)) { ++ pthread_mutex_unlock(&vbd->mutex); + EPRINTF("resume request for unpaused vbd %s\n", vbd->name); + return -EINVAL; + } ++ pthread_mutex_unlock(&vbd->mutex); + + for (i = 0; i < TD_VBD_EIO_RETRIES; i++) { + err = tapdisk_vbd_open_vdi(vbd, name, vbd->flags | TD_OPEN_STRICT, -1); +@@ -1014,8 +1046,10 @@ tapdisk_vbd_resume(td_vbd_t *vbd, const char *name) + } + } + resume_failed: ++ pthread_mutex_lock(&vbd->mutex); + if (err) { + td_flag_set(vbd->state, TD_VBD_RESUME_FAILED); ++ pthread_mutex_unlock(&vbd->mutex); + tapdisk_vbd_close_vdi(vbd); + return err; + } +@@ -1026,6 +1060,7 @@ resume_failed: + tapdisk_vbd_start_queue(vbd); + td_flag_clear(vbd->state, TD_VBD_PAUSED); + td_flag_clear(vbd->state, TD_VBD_PAUSE_REQUESTED); ++ pthread_mutex_unlock(&vbd->mutex); + tapdisk_vbd_check_state(vbd); + + if (vbd->nbdserver) +@@ -1081,17 +1116,24 @@ tapdisk_vbd_check_complete_requests(td_vbd_t *vbd) + struct timeval now; + + gettimeofday(&now, NULL); ++ pthread_mutex_lock(&vbd->mutex); + tapdisk_vbd_for_each_request(vreq, tmp, &vbd->failed_requests) + if (__tapdisk_vbd_request_timeout(vreq, &now)) + tapdisk_vbd_complete_vbd_request(vbd, vreq); ++ pthread_mutex_unlock(&vbd->mutex); + } + + static void + tapdisk_vbd_check_requests_for_issue(td_vbd_t *vbd) + { ++ pthread_mutex_lock(&vbd->mutex); + if (!list_empty(&vbd->new_requests) || +- !list_empty(&vbd->failed_requests)) ++ !list_empty(&vbd->failed_requests)) { ++ pthread_mutex_unlock(&vbd->mutex); + tapdisk_vbd_issue_requests(vbd); ++ return; ++ } ++ pthread_mutex_unlock(&vbd->mutex); + } + + void +@@ -1141,10 +1183,13 @@ tapdisk_vbd_check_progress(td_vbd_t *vbd) + time_t diff; + struct timeval now, delta; + ++ pthread_mutex_lock(&vbd->mutex); + if (list_empty(&vbd->pending_requests)) { ++ pthread_mutex_unlock(&vbd->mutex); + watchdog_cleared(vbd); + return; + } ++ pthread_mutex_unlock(&vbd->mutex); + + gettimeofday(&now, NULL); + timersub(&now, &vbd->ts, &delta); +@@ -1152,6 +1197,7 @@ tapdisk_vbd_check_progress(td_vbd_t *vbd) + + if (diff >= TD_VBD_WATCHDOG_TIMEOUT) + { ++ pthread_mutex_lock(&vbd->mutex); + if(tapdisk_vbd_queue_ready(vbd)) + { + if (!vbd->watchdog_warned) { +@@ -1159,8 +1205,11 @@ tapdisk_vbd_check_progress(td_vbd_t *vbd) + "idle for %ld seconds\n", vbd->name, diff); + vbd->watchdog_warned = true; + } ++ pthread_mutex_unlock(&vbd->mutex); + tapdisk_vbd_drop_log(vbd); ++ pthread_mutex_lock(&vbd->mutex); + } ++ pthread_mutex_unlock(&vbd->mutex); + return; + } + +@@ -1237,6 +1286,7 @@ __tapdisk_vbd_complete_td_request(td_vbd_t *vbd, td_vbd_request_t *vreq, + long long interval; + + err = (res <= 0 ? res : -res); ++ pthread_mutex_lock(&vbd->mutex); + vbd->secs_pending -= treq.secs; + vreq->secs_pending -= treq.secs; + +@@ -1280,6 +1330,7 @@ __tapdisk_vbd_complete_td_request(td_vbd_t *vbd, td_vbd_request_t *vreq, + } + + tapdisk_vbd_complete_vbd_request(vbd, vreq); ++ pthread_mutex_unlock(&vbd->mutex); + } + + static void +@@ -1292,7 +1343,9 @@ __tapdisk_vbd_reissue_td_request(td_vbd_t *vbd, + vreq = treq.vreq; + gettimeofday(&vreq->last_try, NULL); + ++ pthread_mutex_lock(&vbd->mutex); + vreq->submitting++; ++ pthread_mutex_unlock(&vbd->mutex); + + if (tapdisk_vbd_is_last_image(vbd, image)) { + if (unlikely(treq.op == TD_OP_BLOCK_STATUS)) { +@@ -1348,9 +1401,11 @@ __tapdisk_vbd_reissue_td_request(td_vbd_t *vbd, + } + + done: ++ pthread_mutex_lock(&vbd->mutex); + vreq->submitting--; + if (!vreq->secs_pending) + tapdisk_vbd_complete_vbd_request(vbd, vreq); ++ pthread_mutex_unlock(&vbd->mutex); + } + + void +@@ -1518,6 +1573,7 @@ tapdisk_vbd_issue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + sec = vreq->sec; + image = tapdisk_vbd_first_image(vbd); + ++ pthread_mutex_lock(&vbd->mutex); + vreq->submitting = 1; + + tapdisk_vbd_mark_progress(vbd); +@@ -1526,6 +1582,7 @@ tapdisk_vbd_issue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + tapdisk_vbd_move_request(vreq, &vbd->pending_requests); + + err = tapdisk_vbd_check_queue(vbd); ++ pthread_mutex_unlock(&vbd->mutex); + if (err) { + goto fail; + } +@@ -1548,6 +1605,7 @@ tapdisk_vbd_issue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + treq.vreq = vreq; + + ++ pthread_mutex_lock(&vbd->mutex); + vreq->secs_pending += iov->secs; + vbd->secs_pending += iov->secs; + if (vbd->secondary_mode == TD_VBD_SECONDARY_MIRROR && +@@ -1557,6 +1615,7 @@ tapdisk_vbd_issue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + vreq->secs_pending += iov->secs; + vbd->secs_pending += iov->secs; + } ++ pthread_mutex_unlock(&vbd->mutex); + + switch (vreq->op) { + case TD_OP_WRITE: +@@ -1602,11 +1661,13 @@ tapdisk_vbd_issue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + err = 0; + + out: ++ pthread_mutex_lock(&vbd->mutex); + vreq->submitting--; + if (!vreq->secs_pending) { + err = (err ? : vreq->error); + tapdisk_vbd_complete_vbd_request(vbd, vreq); + } ++ pthread_mutex_unlock(&vbd->mutex); + + return err; + +@@ -1631,6 +1692,7 @@ tapdisk_vbd_reissue_failed_requests(td_vbd_t *vbd) + err = 0; + gettimeofday(&now, NULL); + ++ pthread_mutex_lock(&vbd->mutex); + tapdisk_vbd_for_each_request(vreq, tmp, &vbd->failed_requests) { + if (vreq->secs_pending) + continue; +@@ -1650,11 +1712,14 @@ tapdisk_vbd_reissue_failed_requests(td_vbd_t *vbd) + vreq->prev_error = vreq->error; + vreq->error = 0; + ++ pthread_mutex_unlock(&vbd->mutex); + DBG(TLOG_DBG, "retry #%d of req %s, " + "sec 0x%08"PRIx64", iovcnt: %d\n", vreq->num_retries, + vreq->name, vreq->sec, vreq->iovcnt); + + err = tapdisk_vbd_issue_request(vbd, vreq); ++ ++ pthread_mutex_lock(&vbd->mutex); + /* + * if this request failed, but was not completed, + * we'll back off for a while. +@@ -1662,20 +1727,22 @@ tapdisk_vbd_reissue_failed_requests(td_vbd_t *vbd) + if (err && !tapdisk_vbd_request_completed(vbd, vreq)) + break; + } ++ pthread_mutex_unlock(&vbd->mutex); + + return 0; + } + +-static void +-tapdisk_vbd_count_new_request(td_vbd_t *vbd, td_vbd_request_t *vreq) ++static td_sector_t ++tapdisk_vbd_count_new_request(td_vbd_request_t *vreq) + { + struct td_iovec *iov; +- int write; +- +- write = vreq->op == TD_OP_WRITE; ++ td_sector_t secs = 0; + +- for (iov = &vreq->iov[0]; iov < &vreq->iov[vreq->iovcnt]; iov++) +- td_sector_count_add(&vbd->secs, iov->secs, write); ++ ASSERT(vreq->iov); ++ for (iov = &vreq->iov[0]; iov < &vreq->iov[vreq->iovcnt]; iov++) { ++ secs += iov->secs; ++ } ++ return secs; + } + + static int +@@ -1684,17 +1751,29 @@ tapdisk_vbd_issue_new_requests(td_vbd_t *vbd) + int err; + td_vbd_request_t *vreq, *tmp; + ++ pthread_mutex_lock(&vbd->mutex); + tapdisk_vbd_for_each_request(vreq, tmp, &vbd->new_requests) { ++ td_sector_t secs = tapdisk_vbd_count_new_request(vreq); ++ bool write = vreq->op == TD_OP_WRITE; ++ ++ pthread_mutex_unlock(&vbd->mutex); + err = tapdisk_vbd_issue_request(vbd, vreq); ++ pthread_mutex_lock(&vbd->mutex); ++ + /* + * if this request failed, but was not completed, + * we'll back off for a while. + */ +- if (err && !tapdisk_vbd_request_completed(vbd, vreq)) ++ if (err && !tapdisk_vbd_request_completed(vbd, vreq)) { ++ pthread_mutex_unlock(&vbd->mutex); + return err; ++ } + +- tapdisk_vbd_count_new_request(vbd, vreq); ++ /* XXX: split counting and add; vreq must NOT be accessed after issuing the ++ request. */ ++ td_sector_count_add(&vbd->secs, secs, write); + } ++ pthread_mutex_unlock(&vbd->mutex); + + return 0; + } +@@ -1704,12 +1783,18 @@ tapdisk_vbd_recheck_state(td_vbd_t *vbd) + { + int err = 0; + +- if (list_empty(&vbd->new_requests)) ++ pthread_mutex_lock(&vbd->mutex); ++ if (list_empty(&vbd->new_requests)) { ++ pthread_mutex_unlock(&vbd->mutex); + return 0; ++ } + + if (td_flag_test(vbd->state, TD_VBD_QUIESCED) || +- td_flag_test(vbd->state, TD_VBD_QUIESCE_REQUESTED)) ++ td_flag_test(vbd->state, TD_VBD_QUIESCE_REQUESTED)) { ++ pthread_mutex_unlock(&vbd->mutex); + return 0; ++ } ++ pthread_mutex_unlock(&vbd->mutex); + + err = tapdisk_vbd_issue_requests(vbd); + +@@ -1722,6 +1807,7 @@ tapdisk_vbd_kill_requests(td_vbd_t *vbd) + { + td_vbd_request_t *vreq, *tmp; + ++ pthread_mutex_lock(&vbd->mutex); + tapdisk_vbd_for_each_request(vreq, tmp, &vbd->new_requests) { + vreq->error = -ESHUTDOWN; + tapdisk_vbd_move_request(vreq, &vbd->completed_requests); +@@ -1731,6 +1817,7 @@ tapdisk_vbd_kill_requests(td_vbd_t *vbd) + vreq->error = -ESHUTDOWN; + tapdisk_vbd_move_request(vreq, &vbd->completed_requests); + } ++ pthread_mutex_unlock(&vbd->mutex); + + return 0; + } +@@ -1740,17 +1827,24 @@ tapdisk_vbd_issue_requests(td_vbd_t *vbd) + { + int err; + +- if (td_flag_test(vbd->state, TD_VBD_DEAD)) ++ pthread_mutex_lock(&vbd->mutex); ++ if (td_flag_test(vbd->state, TD_VBD_DEAD)) { ++ pthread_mutex_unlock(&vbd->mutex); + return tapdisk_vbd_kill_requests(vbd); ++ } + + if (td_flag_test(vbd->state, TD_VBD_QUIESCED) || + td_flag_test(vbd->state, TD_VBD_QUIESCE_REQUESTED)) { + +- if (td_flag_test(vbd->state, TD_VBD_RESUME_FAILED)) ++ if (td_flag_test(vbd->state, TD_VBD_RESUME_FAILED)) { ++ pthread_mutex_unlock(&vbd->mutex); + return tapdisk_vbd_kill_requests(vbd); +- else ++ } else { ++ pthread_mutex_unlock(&vbd->mutex); + return -EAGAIN; ++ } + } ++ pthread_mutex_unlock(&vbd->mutex); + + err = tapdisk_vbd_reissue_failed_requests(vbd); + if (err) +@@ -1765,8 +1859,11 @@ tapdisk_vbd_queue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + gettimeofday(&vreq->ts, NULL); + vreq->vbd = vbd; + ++ pthread_mutex_lock(&vbd->mutex); + list_add_tail(&vreq->next, &vbd->new_requests); ++ vreq->list_head = &vbd->new_requests; + vbd->received++; ++ pthread_mutex_unlock(&vbd->mutex); + + return 0; + } +@@ -1774,11 +1871,13 @@ tapdisk_vbd_queue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + void + tapdisk_vbd_kick(td_vbd_t *vbd) + { +- const struct list_head *list = &vbd->completed_requests; ++ const struct list_head *list; + td_vbd_request_t *vreq, *prev, *next; + ++ pthread_mutex_lock(&vbd->mutex); + vbd->kicked++; + ++ list = &vbd->completed_requests; + while (!list_empty(list)) { + + /* +@@ -1808,6 +1907,7 @@ tapdisk_vbd_kick(td_vbd_t *vbd) + prev->cb(prev, prev->error, prev->token, 1); + vbd->returned++; + } ++ pthread_mutex_unlock(&vbd->mutex); + } + + int +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index 23ca7289..f3fa0398 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -32,6 +32,7 @@ + #define _TAPDISK_VBD_H_ + + #include ++#include + + #include "tapdisk.h" + #include "scheduler.h" +@@ -134,6 +135,7 @@ struct td_vbd_handle { + struct list_head pending_requests; + struct list_head failed_requests; + struct list_head completed_requests; ++ pthread_mutex_t mutex; + + struct list_head next; + +diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h +index 3163e190..d907d149 100644 +--- a/drivers/tapdisk.h ++++ b/drivers/tapdisk.h +@@ -243,7 +243,7 @@ struct td_sector_count { + }; + + static inline void +-td_sector_count_add(td_sector_count_t *s, td_sector_t v, int write) ++td_sector_count_add(td_sector_count_t *s, td_sector_t v, bool write) + { + if (write) + s->wr += v; diff --git a/SOURCES/0028-tapdisk-protect-td_blktap_t-structure.patch b/SOURCES/0028-tapdisk-protect-td_blktap_t-structure.patch new file mode 100644 index 0000000..ef81f53 --- /dev/null +++ b/SOURCES/0028-tapdisk-protect-td_blktap_t-structure.patch @@ -0,0 +1,153 @@ +From fbe5e855d2b3bc859653be445883f8c73b41b230 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:40 +0100 +Subject: [PATCH] tapdisk: protect td_blktap_t structure + +libqcow2 spawns a thread to handle IO requests to qcow2 layer. +Add a mutex to protect blktap structure from qcow2 concurrency. + +This is the second part of the tapdisk threading protection. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-blktap.c | 34 ++++++++++++++++++++++++---------- + drivers/tapdisk-blktap.h | 1 + + 2 files changed, 25 insertions(+), 10 deletions(-) + +diff --git a/drivers/tapdisk-blktap.c b/drivers/tapdisk-blktap.c +index cf72b095..9c2e4855 100644 +--- a/drivers/tapdisk-blktap.c ++++ b/drivers/tapdisk-blktap.c +@@ -87,19 +87,21 @@ struct td_blktap_req { + struct timeval ts; + }; + +-td_blktap_req_t * ++static td_blktap_req_t * + tapdisk_blktap_alloc_request(td_blktap_t *tap) + { + td_blktap_req_t *req = NULL; + ++ pthread_mutex_lock(&tap->mutex); + if (likely(tap->n_reqs_free)) + req = tap->reqs_free[--tap->n_reqs_free]; ++ pthread_mutex_unlock(&tap->mutex); + + return req; + } + +-void +-tapdisk_blktap_free_request(td_blktap_t *tap, td_blktap_req_t *req) ++static void ++tapdisk_blktap_free_request_locked(td_blktap_t *tap, td_blktap_req_t *req) + { + BUG_ON(tap->n_reqs_free >= tap->n_reqs); + tap->reqs_free[tap->n_reqs_free++] = req; +@@ -117,6 +119,8 @@ tapdisk_blktap_reqs_free(td_blktap_t *tap) + free(tap->reqs_free); + tap->reqs_free = NULL; + } ++ ++ pthread_mutex_destroy(&tap->mutex); + } + + static int +@@ -124,6 +128,8 @@ tapdisk_blktap_reqs_init(td_blktap_t *tap, int n_reqs) + { + int i, err; + ++ pthread_mutex_init(&tap->mutex, NULL); ++ + tap->reqs = malloc(n_reqs * sizeof(td_blktap_req_t)); + if (!tap->reqs) { + err = -errno; +@@ -139,8 +145,10 @@ tapdisk_blktap_reqs_init(td_blktap_t *tap, int n_reqs) + tap->n_reqs = n_reqs; + tap->n_reqs_free = 0; + ++ pthread_mutex_lock(&tap->mutex); + for (i = 0; i < n_reqs; i++) +- tapdisk_blktap_free_request(tap, &tap->reqs[i]); ++ tapdisk_blktap_free_request_locked(tap, &tap->reqs[i]); ++ pthread_mutex_unlock(&tap->mutex); + + return 0; + +@@ -180,7 +188,7 @@ tapdisk_blktap_error_status(td_blktap_t *tap, int error) + } + + static void +-__tapdisk_blktap_push_response(td_blktap_t *tap, int final) ++__tapdisk_blktap_push_response_locked(td_blktap_t *tap, int final) + { + tap->rsp_prod_pvt++; + +@@ -200,13 +208,15 @@ tapdisk_blktap_fail_request(td_blktap_t *tap, + + BUG_ON(!tap->vma); + ++ pthread_mutex_lock(&tap->mutex); + rsp = BLKTAP_GET_RESPONSE(tap, tap->rsp_prod_pvt); + + rsp->id = msg->id; + rsp->operation = msg->operation; + rsp->status = tapdisk_blktap_error_status(tap, error); + +- __tapdisk_blktap_push_response(tap, 1); ++ __tapdisk_blktap_push_response_locked(tap, 1); ++ pthread_mutex_unlock(&tap->mutex); + } + + static void +@@ -218,6 +228,7 @@ tapdisk_blktap_put_response(td_blktap_t *tap, + unsigned long long interval; + struct timeval now; + ++ pthread_mutex_lock(&tap->mutex); + BUG_ON(!tap->vma); + + rsp = BLKTAP_GET_RESPONSE(tap, tap->rsp_prod_pvt); +@@ -246,7 +257,10 @@ tapdisk_blktap_put_response(td_blktap_t *tap, + rsp->operation = op; + rsp->status = tapdisk_blktap_error_status(tap, error); + +- __tapdisk_blktap_push_response(tap, final); ++ tapdisk_blktap_free_request_locked(tap, req); ++ ++ __tapdisk_blktap_push_response_locked(tap, final); ++ pthread_mutex_unlock(&tap->mutex); + } + + static void +@@ -256,8 +270,6 @@ tapdisk_blktap_complete_request(td_blktap_t *tap, + { + if (likely(tap->vma)) + tapdisk_blktap_put_response(tap, req, error, final); +- +- tapdisk_blktap_free_request(tap, req); + } + + static void +@@ -390,7 +402,9 @@ tapdisk_blktap_get_requests(td_blktap_t *tap) + err = tapdisk_blktap_parse_request(tap, msg, req); + if (err) { + tapdisk_blktap_fail_request(tap, msg, err); +- tapdisk_blktap_free_request(tap, req); ++ pthread_mutex_lock(&tap->mutex); ++ tapdisk_blktap_free_request_locked(tap, req); ++ pthread_mutex_unlock(&tap->mutex); + goto fail_ring; + } + +diff --git a/drivers/tapdisk-blktap.h b/drivers/tapdisk-blktap.h +index ca576a77..5bd4aae8 100644 +--- a/drivers/tapdisk-blktap.h ++++ b/drivers/tapdisk-blktap.h +@@ -70,6 +70,7 @@ struct td_blktap { + td_blktap_req_t *reqs; + int n_reqs_free; + td_blktap_req_t **reqs_free; ++ pthread_mutex_t mutex; + + struct list_head entry; + diff --git a/SOURCES/0029-tapdisk-protect-td_xenblkif-structure.patch b/SOURCES/0029-tapdisk-protect-td_xenblkif-structure.patch new file mode 100644 index 0000000..fb1fd29 --- /dev/null +++ b/SOURCES/0029-tapdisk-protect-td_xenblkif-structure.patch @@ -0,0 +1,316 @@ +From 94394a12503d5ca4a30d40e13ca52c130358aeb8 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:47 +0100 +Subject: [PATCH] tapdisk: protect td_xenblkif structure + +libqcow2 spawns a thread to handle IO requests to qcow2 layer. +Add a mutex to protect blkif structure from qcow2 concurrency. + +This is the third part of the tapdisk threading protection. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-blkif.h | 5 ++++ + drivers/td-ctx.c | 12 ++++++-- + drivers/td-req.c | 70 +++++++++++++++++++++++++++++++++++----------- + drivers/td-req.h | 15 ---------- + 4 files changed, 68 insertions(+), 34 deletions(-) + +diff --git a/drivers/td-blkif.h b/drivers/td-blkif.h +index d96888bd..2eaf080c 100644 +--- a/drivers/td-blkif.h ++++ b/drivers/td-blkif.h +@@ -134,6 +134,11 @@ struct td_xenblkif { + */ + struct td_vbd_handle *vbd; + ++ /** ++ * Protect requests list ++ */ ++ pthread_mutex_t mutex; ++ + /** + * stats + */ +diff --git a/drivers/td-ctx.c b/drivers/td-ctx.c +index e344a62f..d54d8457 100644 +--- a/drivers/td-ctx.c ++++ b/drivers/td-ctx.c +@@ -295,10 +295,13 @@ tapdisk_xenio_ctx_process_ring(struct td_xenblkif *blkif, + blkif_request_t **reqs; + int limit; + ++ pthread_mutex_lock(&blkif->mutex); + start = blkif->n_reqs_free; + +- if (unlikely(blkif->barrier.msg)) +- return 0; ++ if (unlikely(blkif->barrier.msg)) { ++ pthread_mutex_unlock(&blkif->mutex); ++ return 0; ++ } + + /* + * In each iteration, copy as many request descriptors from the shared ring +@@ -340,7 +343,7 @@ tapdisk_xenio_ctx_process_ring(struct td_xenblkif *blkif, + + n_reqs = start - blkif->n_reqs_free; + +- if (!n_reqs) ++ if (!n_reqs) { + /* + * We got a notification but the ring is empty. This is because we had + * previously suspended the operation of the ring because of a +@@ -350,7 +353,9 @@ tapdisk_xenio_ctx_process_ring(struct td_xenblkif *blkif, + * notification. This notification is the one we should have consumed, + * and can be ignored. + */ ++ pthread_mutex_unlock(&blkif->mutex); + return 0; ++ } + + if (blkif->in_polling) + /* We found at least one request, so keep polling some more */ +@@ -364,6 +369,7 @@ tapdisk_xenio_ctx_process_ring(struct td_xenblkif *blkif, + reqs = alloca(sizeof(blkif_request_t*) * n_reqs); + memcpy(reqs, &blkif->reqs_free[blkif->ring_size - start], + sizeof(blkif_request_t*) * n_reqs); ++ pthread_mutex_unlock(&blkif->mutex); + + tapdisk_xenblkif_queue_requests(blkif, reqs, n_reqs); + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index 028e4bf6..4d901554 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -74,9 +74,11 @@ td_xenblkif_bufcache_event(event_id_t id, char mode, void *private) + { + struct td_xenblkif *blkif = private; + ++ pthread_mutex_lock(&blkif->mutex); + td_xenblkif_bufcache_free(blkif); + + td_xenblkif_bufcache_evt_unreg(blkif); ++ pthread_mutex_unlock(&blkif->mutex); + } + + /** +@@ -439,21 +441,26 @@ out: + * @req the request to complete + * @error completion status of the request + * @final controls whether the other end should be notified ++ * @lock must always be true except in this function to control recursion + */ +-void ++static bool + tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, +- struct td_xenblkif_req* req, int err, const int final) ++ struct td_xenblkif_req* req, int err, const int final, ++ bool lock) + { + int _err; + long long *max = NULL, *sum = NULL, *cnt = NULL; + static int depth = 0; + bool processing_barrier_message; ++ bool blkif_destroyed = false; + uint64_t *ticks = NULL; + + ASSERT(blkif); + ASSERT(req); + ASSERT(depth >= 0); + ++ if (lock) ++ pthread_mutex_lock(&blkif->mutex); + depth++; + + processing_barrier_message = +@@ -553,9 +560,16 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + /* + * If this is the last request, complete the barrier request. + */ +- if (tapdisk_xenblkif_barrier_should_complete(blkif)) +- tapdisk_xenblkif_complete_request(blkif, +- msg_to_tapreq(blkif->barrier.msg), 0, 1); ++ if (tapdisk_xenblkif_barrier_should_complete(blkif)) { ++ blkif_destroyed = tapdisk_xenblkif_complete_request(blkif, ++ msg_to_tapreq(blkif->barrier.msg), 0, 1, false); ++ /* ++ * We assert here on "blkif_destroyed == true" because as ++ * "depth > 1" in the recursive call above, the branch to ++ * destroy the ring shouldn't be taken. ++ */ ++ ASSERT(!blkif_destroyed); ++ } + } + + /* +@@ -566,11 +580,17 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + && !tapdisk_xenblkif_reqs_pending(blkif))) { + + RING_DEBUG(blkif, "destroying dead ring\n"); ++ pthread_mutex_unlock(&blkif->mutex); + tapdisk_xenblkif_destroy(blkif); ++ blkif_destroyed = true; ++ lock = false; /* blkif with its mutex were destroyed above so don't try to unlock it */ + } + + out: + depth--; ++ if (lock) ++ pthread_mutex_unlock(&blkif->mutex); ++ return blkif_destroyed; + } + + /** +@@ -595,18 +615,20 @@ __tapdisk_xenblkif_request_cb(struct td_vbd_request * const vreq, + req = container_of(vreq, struct td_xenblkif_req, vreq); + + if (error) { ++ pthread_mutex_lock(&blkif->mutex); + if (likely(!blkif->dead)) { + blkif->stats.errors.img++; + blkif->vbd_stats.stats->io_errors++; + } ++ pthread_mutex_unlock(&blkif->mutex); + } + +- tapdisk_xenblkif_complete_request(blkif, req, error, final); ++ tapdisk_xenblkif_complete_request(blkif, req, error, final, true); + } + + + static inline int +-tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, ++tapdisk_xenblkif_parse_request_locked(struct td_xenblkif * const blkif, + struct td_xenblkif_req * const req) + { + td_vbd_request_t *vreq; +@@ -625,6 +647,7 @@ tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, + req->vma = td_xenblkif_bufcache_get(blkif); + if (unlikely(!req->vma)) { + err = errno; ++ RING_ERR(blkif, "errno %d: invalid vma\n", err); + goto out; + } + +@@ -732,6 +755,7 @@ tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, + { + int err = 0; + td_vbd_request_t *vreq; ++ bool blkif_freed = false; + + ASSERT(req); + +@@ -779,8 +803,10 @@ tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, + goto out; + } + +- if (likely(req->msg.nr_segments)) +- err = tapdisk_xenblkif_parse_request(blkif, req); ++ if (likely(req->msg.nr_segments)) { ++ pthread_mutex_lock(&blkif->mutex); ++ err = tapdisk_xenblkif_parse_request_locked(blkif, req); ++ pthread_mutex_unlock(&blkif->mutex); + /* + * If we only got one request from the ring and that was a barrier one, + * check whether the barrier requests completion conditions are satisfied +@@ -789,10 +815,15 @@ tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, + * It could be that there are more requests in the ring after the barrier + * request, tapdisk_xenblkif_complete_request() will schedule a ring check. + */ +- else if (tapdisk_xenblkif_barrier_should_complete(blkif)) { +- tapdisk_xenblkif_complete_request(blkif, +- msg_to_tapreq(blkif->barrier.msg), 0, 1); +- err = 0; ++ } else { ++ pthread_mutex_lock(&blkif->mutex); ++ if (tapdisk_xenblkif_barrier_should_complete(blkif)) { ++ blkif_freed = tapdisk_xenblkif_complete_request(blkif, ++ msg_to_tapreq(blkif->barrier.msg), 0, 1, false); ++ err = 0; ++ } ++ if (!blkif_freed) ++ pthread_mutex_unlock(&blkif->mutex); + } + out: + return err; +@@ -857,12 +888,13 @@ tapdisk_xenblkif_queue_requests(struct td_xenblkif * const blkif, + int i; + int err; + int nr_errors = 0; ++ bool blkif_freed = false; + + ASSERT(blkif); + ASSERT(reqs); + ASSERT(nr_reqs >= 0); + +- for (i = 0; i < nr_reqs; i++) { /* for each request in the ring... */ ++ for (i = 0; i < nr_reqs && !blkif_freed; i++) { /* for each request in the ring... */ + blkif_request_t *msg = reqs[i]; + struct td_xenblkif_req *req; + +@@ -876,7 +908,7 @@ tapdisk_xenblkif_queue_requests(struct td_xenblkif * const blkif, + if (err) { + /* TODO log error */ + nr_errors++; +- tapdisk_xenblkif_complete_request(blkif, req, err, 1); ++ blkif_freed = tapdisk_xenblkif_complete_request(blkif, req, err, 1, true); + } + } + +@@ -884,8 +916,11 @@ tapdisk_xenblkif_queue_requests(struct td_xenblkif * const blkif, + dead and current request is the last one, hence adding + this check to avoid seg fault */ + +- if (nr_errors && blkif) ++ if (nr_errors && !blkif_freed) { ++ pthread_mutex_lock(&blkif->mutex); + xenio_blkif_put_response(blkif, NULL, 0, 1); ++ pthread_mutex_unlock(&blkif->mutex); ++ } + } + + void +@@ -905,6 +940,7 @@ tapdisk_xenblkif_reqs_free(struct td_xenblkif * const blkif) + free(blkif->reqs_free); + blkif->reqs_free = NULL; + ++ pthread_mutex_destroy(&blkif->mutex); + } + + int +@@ -916,6 +952,8 @@ tapdisk_xenblkif_reqs_init(struct td_xenblkif *td_blkif) + + ASSERT(td_blkif); + ++ pthread_mutex_init(&td_blkif->mutex, NULL); ++ + td_blkif->ring_size = td_blkif_ring_size(td_blkif); + ASSERT(td_blkif->ring_size > 0); + +diff --git a/drivers/td-req.h b/drivers/td-req.h +index 25eeb503..4d0cb24b 100644 +--- a/drivers/td-req.h ++++ b/drivers/td-req.h +@@ -118,21 +118,6 @@ tapdisk_xenblkif_reqs_init(struct td_xenblkif *td_blkif); + void + tapdisk_xenblkif_reqs_free(struct td_xenblkif * const blkif); + +-/** +- * Completes a request. If this is the last pending request of a dead block +- * interface, the block interface is destroyed, the caller must not access it +- * any more. +- * +- * @blkif the VBD the request belongs belongs to +- * @tapreq the request to complete +- * @error completion status of the request +- * @final controls whether the other end should be notified +- */ +- +-void +-tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, +- struct td_xenblkif_req* tapreq, int err, const int final); +- + #define msg_to_tapreq(_req) \ + container_of(_req, struct td_xenblkif_req, msg) + diff --git a/SOURCES/0030-tapdisk-protect-scheduler-structure.patch b/SOURCES/0030-tapdisk-protect-scheduler-structure.patch new file mode 100644 index 0000000..65c0a11 --- /dev/null +++ b/SOURCES/0030-tapdisk-protect-scheduler-structure.patch @@ -0,0 +1,244 @@ +From 206d4e826838aa86c3df6f611f75896585a68919 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 3 Apr 2025 16:06:24 +0200 +Subject: [PATCH] tapdisk: protect scheduler structure + +libqcow2 spawns a thread to handle IO requests to qcow2 layer. +Add a mutex to protect scheduler structure from qcow2 concurrency. + +This is the fourth part of the tapdisk threading protection. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/scheduler.c | 39 ++++++++++++++++++++++++----- + drivers/scheduler.h | 2 ++ + mockatests/drivers/test-scheduler.c | 4 +-- + 3 files changed, 37 insertions(+), 8 deletions(-) + +diff --git a/drivers/scheduler.c b/drivers/scheduler.c +index c18980eb..4be30570 100644 +--- a/drivers/scheduler.c ++++ b/drivers/scheduler.c +@@ -227,7 +227,7 @@ scheduler_check_events(scheduler_t *s, int nfds) + } + + static void +-scheduler_event_callback(event_t *event, char mode) ++scheduler_event_callback(scheduler_t *s, event_t *event, char mode) + { + if (event->mode & SCHEDULER_POLL_TIMEOUT + && !TV_IS_INF(event->timeout)) { +@@ -236,8 +236,11 @@ scheduler_event_callback(event_t *event, char mode) + TV_ADD(now, event->timeout, event->deadline); + } + +- if (!event->masked) ++ if (!event->masked) { ++ pthread_mutex_unlock(&s->mutex); + event->cb(event->id, mode, event->private); ++ pthread_mutex_lock(&s->mutex); ++ } + } + + static int +@@ -256,7 +259,7 @@ scheduler_run_events(scheduler_t *s) + if (pending) { + event->pending = 0; + /* NB. must clear before cb */ +- scheduler_event_callback(event, pending); ++ scheduler_event_callback(s, event, pending); + n_dispatched++; + } + } +@@ -312,6 +315,7 @@ scheduler_register_event(scheduler_t *s, char mode, int fd, + struct timeval timeout, event_cb_t cb, void *private) + { + event_t *event; ++ event_id_t id; + struct timeval now; + + if (!cb) +@@ -324,6 +328,9 @@ scheduler_register_event(scheduler_t *s, char mode, int fd, + if (!event) + return -ENOMEM; + ++ pthread_mutex_lock(&s->mutex); ++ id = scheduler_get_event_uuid(s); ++ + gettimeofday(&now, NULL); + + INIT_LIST_HEAD(&event->next); +@@ -338,12 +345,13 @@ scheduler_register_event(scheduler_t *s, char mode, int fd, + TV_ADD(now, timeout, event->deadline); + event->cb = cb; + event->private = private; +- event->id = scheduler_get_event_uuid(s); ++ event->id = id; + event->masked = 0; + + list_add_tail(&event->next, &s->events); ++ pthread_mutex_unlock(&s->mutex); + +- return event->id; ++ return id; + } + + void +@@ -354,11 +362,13 @@ scheduler_unregister_event(scheduler_t *s, event_id_t id) + if (!id) + return; + ++ pthread_mutex_lock(&s->mutex); + scheduler_for_each_event(s, event) + if (event->id == id) { + event->dead = 1; + break; + } ++ pthread_mutex_unlock(&s->mutex); + } + + void +@@ -369,11 +379,13 @@ scheduler_mask_event(scheduler_t *s, event_id_t id, int masked) + if (!id) + return; + ++ pthread_mutex_lock(&s->mutex); + scheduler_for_each_event(s, event) + if (event->id == id) { + event->masked = !!masked; + break; + } ++ pthread_mutex_unlock(&s->mutex); + } + + static void +@@ -391,8 +403,10 @@ scheduler_gc_events(scheduler_t *s) + void + scheduler_set_max_timeout(scheduler_t *s, struct timeval timeout) + { ++ pthread_mutex_lock(&s->mutex); + if (!TV_IS_INF(timeout)) + s->max_timeout = TV_MIN(s->max_timeout, timeout); ++ pthread_mutex_unlock(&s->mutex); + } + + int +@@ -401,6 +415,7 @@ scheduler_wait_for_events(scheduler_t *s) + int ret; + struct timeval tv; + ++ pthread_mutex_lock(&s->mutex); + s->depth++; + ret = 0; + +@@ -413,6 +428,7 @@ scheduler_wait_for_events(scheduler_t *s) + scheduler_prepare_events(s); + + tv = s->timeout; ++ pthread_mutex_unlock(&s->mutex); + + DBG("timeout: %ld.%ld, max_timeout: %ld.%ld\n", + s->timeout.tv_sec, s->timeout.tv_usec, s->max_timeout.tv_sec, s->max_timeout.tv_usec); +@@ -426,6 +442,7 @@ scheduler_wait_for_events(scheduler_t *s) + } + } while (ret == -EINTR); + ++ pthread_mutex_lock(&s->mutex); + if (ret < 0) { + EPRINTF("select failed: %s\n", strerror(-ret)); + goto out; +@@ -444,6 +461,7 @@ scheduler_wait_for_events(scheduler_t *s) + + out: + s->depth--; ++ pthread_mutex_unlock(&s->mutex); + + return ret; + } +@@ -453,6 +471,9 @@ scheduler_initialize(scheduler_t *s) + { + memset(s, 0, sizeof(scheduler_t)); + ++ pthread_mutex_init(&s->mutex, NULL); ++ ++ pthread_mutex_lock(&s->mutex); + s->uuid = 1; + s->depth = 0; + s->uuid_overflow = 0; +@@ -462,6 +483,7 @@ scheduler_initialize(scheduler_t *s) + FD_ZERO(&s->except_fds); + + INIT_LIST_HEAD(&s->events); ++ pthread_mutex_unlock(&s->mutex); + } + + int +@@ -474,10 +496,13 @@ scheduler_event_set_timeout(scheduler_t *sched, event_id_t event_id, struct time + if (!event_id) + return -EINVAL; + ++ pthread_mutex_lock(&sched->mutex); + scheduler_for_each_event(sched, event) { + if (event->id == event_id) { +- if (!(event->mode & SCHEDULER_POLL_TIMEOUT)) ++ if (!(event->mode & SCHEDULER_POLL_TIMEOUT)) { ++ pthread_mutex_unlock(&sched->mutex); + return -EINVAL; ++ } + event->timeout = timeo; + if (TV_IS_INF(event->timeout)) + event->deadline = TV_INF; +@@ -486,9 +511,11 @@ scheduler_event_set_timeout(scheduler_t *sched, event_id_t event_id, struct time + gettimeofday(&now, NULL); + TV_ADD(now, event->timeout, event->deadline); + } ++ pthread_mutex_unlock(&sched->mutex); + return 0; + } + } ++ pthread_mutex_unlock(&sched->mutex); + + return -ENOENT; + } +diff --git a/drivers/scheduler.h b/drivers/scheduler.h +index e2cae934..507b901a 100644 +--- a/drivers/scheduler.h ++++ b/drivers/scheduler.h +@@ -33,6 +33,7 @@ + + #include + #include ++#include + + #include "list.h" + +@@ -50,6 +51,7 @@ typedef struct scheduler { + fd_set except_fds; + + struct list_head events; ++ pthread_mutex_t mutex; + + event_id_t uuid; + int uuid_overflow; +diff --git a/mockatests/drivers/test-scheduler.c b/mockatests/drivers/test-scheduler.c +index 428dd732..8ede9693 100644 +--- a/mockatests/drivers/test-scheduler.c ++++ b/mockatests/drivers/test-scheduler.c +@@ -743,7 +743,7 @@ test_scheduler_callback(void **state) + // Update current time to time_now2 + fake_gettimeofday = (struct timeval){ .tv_sec = time_now2 }; + +- scheduler_event_callback(event1, test_mode); ++ scheduler_event_callback(&s, event1, test_mode, false); + + // Check callback has been called + assert_int_equal(event_cb_spy.was_called, 1); +@@ -776,7 +776,7 @@ test_scheduler_callback_ignores_masked_events(void **state) + event1->masked = true; + + const int test_mode = 1; +- scheduler_event_callback(event1, test_mode); ++ scheduler_event_callback(&s, event1, test_mode, false); + + // Check callback has not been called + assert_int_equal(event_cb_spy.was_called, 0); diff --git a/SOURCES/0031-mocka-fix-scheduler-tests-according-mutex-protection.patch b/SOURCES/0031-mocka-fix-scheduler-tests-according-mutex-protection.patch new file mode 100644 index 0000000..329868c --- /dev/null +++ b/SOURCES/0031-mocka-fix-scheduler-tests-according-mutex-protection.patch @@ -0,0 +1,775 @@ +From a509127f18e7290830efdc59b78ef5ccda06aad0 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 21 Jan 2026 17:25:29 +0100 +Subject: [PATCH] mocka: fix scheduler tests according mutex protection + +Signed-off-by: Anthoine Bourgeois +--- + drivers/scheduler.c | 2 +- + drivers/scheduler.h | 1 - + mockatests/drivers/test-scheduler.c | 152 +++++++++++++++++++++++++++- + 3 files changed, 151 insertions(+), 4 deletions(-) + +diff --git a/drivers/scheduler.c b/drivers/scheduler.c +index 4be30570..58b6cc7a 100644 +--- a/drivers/scheduler.c ++++ b/drivers/scheduler.c +@@ -267,7 +267,7 @@ scheduler_run_events(scheduler_t *s) + return n_dispatched; + } + +-event_id_t ++static event_id_t + scheduler_get_event_uuid(scheduler_t *s) { + + bool uuid_found = false; +diff --git a/drivers/scheduler.h b/drivers/scheduler.h +index 507b901a..143065ba 100644 +--- a/drivers/scheduler.h ++++ b/drivers/scheduler.h +@@ -73,7 +73,6 @@ event_id_t scheduler_register_event(scheduler_t *, char mode, + int fd, struct timeval timeout, + event_cb_t cb, void *private); + +-event_id_t scheduler_get_event_uuid(scheduler_t *); + void scheduler_unregister_event(scheduler_t *, event_id_t); + void scheduler_mask_event(scheduler_t *, event_id_t, int masked); + void scheduler_set_max_timeout(scheduler_t *, struct timeval); +diff --git a/mockatests/drivers/test-scheduler.c b/mockatests/drivers/test-scheduler.c +index 8ede9693..234affac 100644 +--- a/mockatests/drivers/test-scheduler.c ++++ b/mockatests/drivers/test-scheduler.c +@@ -118,6 +118,7 @@ void + test_scheduler_set_max_timeout(void **state) + { + scheduler_t s; ++ scheduler_initialize(&s); + s.max_timeout.tv_sec = 42; + struct timeval timeout = {.tv_sec = 0 }; + scheduler_set_max_timeout(&s, timeout); +@@ -129,6 +130,7 @@ test_scheduler_set_max_timeout_lower(void **state) + { + // Setting a new lower value will stick + scheduler_t s; ++ scheduler_initialize(&s); + s.max_timeout.tv_sec = 430; + struct timeval timeout = {.tv_sec = 360 }; + scheduler_set_max_timeout(&s, timeout); +@@ -140,6 +142,7 @@ test_scheduler_set_max_timeout_higher(void **state) + { + // Setting a new higher value will be ignored + scheduler_t s; ++ scheduler_initialize(&s); + s.max_timeout.tv_sec = 430; + struct timeval timeout = {.tv_sec = 458 }; + scheduler_set_max_timeout(&s, timeout); +@@ -151,6 +154,7 @@ test_scheduler_set_max_timeout_negative(void **state) + { + // Setting a new negative value will be ignored + scheduler_t s; ++ scheduler_initialize(&s); + s.max_timeout.tv_sec = 458; + struct timeval timeout = {.tv_sec = -2 }; + scheduler_set_max_timeout(&s, timeout); +@@ -162,6 +166,7 @@ test_scheduler_set_max_timeout_inf(void **state) + { + // Setting TV_INF will be ignored + scheduler_t s; ++ scheduler_initialize(&s); + s.max_timeout.tv_sec = 458; + struct timeval timeout = TV_INF; + scheduler_set_max_timeout(&s, timeout); +@@ -184,7 +189,9 @@ test_scheduler_register_event_null_callback(void **state) + assert_int_equal(r, -EINVAL); + + scheduler_unregister_event(&s, r); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -203,7 +210,9 @@ test_scheduler_register_event_bad_mode(void **state) + assert_int_equal(r, -EINVAL); + + scheduler_unregister_event(&s, r); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -234,7 +243,9 @@ test_scheduler_register_multiple_events(void **state) + + scheduler_unregister_event(&s, event_id1); + scheduler_unregister_event(&s, event_id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -268,7 +279,9 @@ test_scheduler_register_event_populates_event(void **state) + assert_int_equal(e->deadline.tv_usec, fake_gettimeofday.tv_usec + timeout.tv_usec); + + scheduler_unregister_event(&s, event_id1); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -290,7 +303,9 @@ test_scheduler_set_timeout_inf(void **state) + assert_true(TV_IS_INF(e->timeout)); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -298,6 +313,7 @@ test_scheduler_set_timeout_invalid_event(void **state) + { + // Invalid event ID will return EINVAL + scheduler_t sched; ++ scheduler_initialize(&sched); + event_id_t event_id = 0; + struct timeval timeo; + const int r = scheduler_event_set_timeout(&sched, event_id, timeo); +@@ -323,7 +339,9 @@ test_scheduler_set_timeout_on_non_polled_event(void **state) + + close(fd); + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -344,7 +362,9 @@ test_scheduler_set_timeout_missing_event(void **state) + assert_int_equal(r, -ENOENT); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -391,7 +411,9 @@ test_scheduler_set_timeout(void **state) + scheduler_unregister_event(&s, id1); + scheduler_unregister_event(&s, id2); + scheduler_unregister_event(&s, id3); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -420,7 +442,9 @@ test_scheduler_set_timeout_inf_and_deadline(void **state) + assert_true(TV_IS_INF(e->deadline)); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -443,7 +467,9 @@ test_scheduler_unregister_event_will_set_dead_field(void **state) + assert_int_equal(e->dead, 1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -466,7 +492,9 @@ test_scheduler_unregister_event_will_ignore_invalid_event(void **state) + assert_int_not_equal(e->dead, 1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -492,7 +520,9 @@ test_scheduler_mask_event_will_set_masked_field(void **state) + assert_int_not_equal(e->masked, 1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -515,7 +545,9 @@ test_scheduler_mask_event_will_accept_non_zero_value(void **state) + assert_int_equal(e->masked, 1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -538,7 +570,9 @@ test_scheduler_mask_event_will_ignore_invalid_event_id(void **state) + assert_int_not_equal(e->masked, 1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -547,10 +581,12 @@ test_scheduler_get_uuid(void **state) + scheduler_t s; + scheduler_initialize(&s); + ++ pthread_mutex_lock(&s.mutex); + s.uuid = 1; + const event_id_t new_uuid = scheduler_get_event_uuid(&s); + assert_int_equal(new_uuid, 1); + assert_int_equal(s.uuid, 2); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -560,6 +596,7 @@ test_scheduler_get_uuid_overflow(void **state) + scheduler_t s; + scheduler_initialize(&s); + ++ pthread_mutex_lock(&s.mutex); + s.uuid = INT_MAX; + const event_id_t new_uuid = scheduler_get_event_uuid(&s); + assert_int_equal(new_uuid, INT_MAX); +@@ -568,6 +605,7 @@ test_scheduler_get_uuid_overflow(void **state) + const event_id_t new_uuid2 = scheduler_get_event_uuid(&s); + assert_int_equal(new_uuid2, 1); + assert_int_equal(s.uuid, 2); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -592,6 +630,7 @@ test_scheduler_get_uuid_overflow_fragmented(void **state) + // After an overflow the next UUID should be 2 + // because that is the next free event id + s.uuid = INT_MIN; ++ pthread_mutex_lock(&s.mutex); + const event_id_t new_uuid1 = scheduler_get_event_uuid(&s); + assert_int_equal(new_uuid1, 2); + // +---+---+---+--- +@@ -600,6 +639,7 @@ test_scheduler_get_uuid_overflow_fragmented(void **state) + + // The next UUID after that will be 4 because 3 is already used. + const event_id_t new_uuid2 = scheduler_get_event_uuid(&s); ++ pthread_mutex_unlock(&s.mutex); + assert_int_equal(new_uuid2, 4); + // +---+---+---+---+--- + // | 1 | 3 | 2 | 4 |... +@@ -607,7 +647,9 @@ test_scheduler_get_uuid_overflow_fragmented(void **state) + + scheduler_unregister_event(&s, id1); + scheduler_unregister_event(&s, id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -645,7 +687,9 @@ test_scheduler_gc_will_remove_dead_events_from_list(void **state) + // +---+ +---+ + // | 1 |->| 3 | + // +---+ +---+ ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + assert_int_equal(event_queue_length(&s), 2); + + // event 1 is now linked to event 3 +@@ -654,7 +698,9 @@ test_scheduler_gc_will_remove_dead_events_from_list(void **state) + scheduler_unregister_event(&s, id1); + scheduler_unregister_event(&s, id2); + scheduler_unregister_event(&s, id3); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -693,7 +739,9 @@ test_scheduler_check_timeouts(void **state) + e5->deadline.tv_sec = 3; // 5: skip because timeout not reached + e6->deadline.tv_sec = 1; // 6: mark because timeout has passed + ++ pthread_mutex_lock(&s.mutex); + scheduler_check_timeouts(&s); ++ pthread_mutex_unlock(&s.mutex); + assert_int_not_equal(e1->pending, SCHEDULER_POLL_TIMEOUT); // unchanged + assert_int_equal(e2->pending, SCHEDULER_POLL_TIMEOUT); // unchanged + assert_int_not_equal(e3->pending, SCHEDULER_POLL_TIMEOUT); // unchanged +@@ -707,7 +755,9 @@ test_scheduler_check_timeouts(void **state) + scheduler_unregister_event(&s, id4); + scheduler_unregister_event(&s, id5); + scheduler_unregister_event(&s, id6); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -743,7 +793,9 @@ test_scheduler_callback(void **state) + // Update current time to time_now2 + fake_gettimeofday = (struct timeval){ .tv_sec = time_now2 }; + +- scheduler_event_callback(&s, event1, test_mode, false); ++ pthread_mutex_lock(&s.mutex); ++ scheduler_event_callback(&s, event1, test_mode); ++ pthread_mutex_unlock(&s.mutex); + + // Check callback has been called + assert_int_equal(event_cb_spy.was_called, 1); +@@ -754,7 +806,9 @@ test_scheduler_callback(void **state) + assert_int_equal(event1->deadline.tv_sec, to.tv_sec + time_now2); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -776,7 +830,9 @@ test_scheduler_callback_ignores_masked_events(void **state) + event1->masked = true; + + const int test_mode = 1; +- scheduler_event_callback(&s, event1, test_mode, false); ++ pthread_mutex_lock(&s.mutex); ++ scheduler_event_callback(&s, event1, test_mode); ++ pthread_mutex_unlock(&s.mutex); + + // Check callback has not been called + assert_int_equal(event_cb_spy.was_called, 0); +@@ -784,7 +840,9 @@ test_scheduler_callback_ignores_masked_events(void **state) + assert_int_not_equal(event_cb_spy.id, event1->id); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -805,13 +863,17 @@ test_scheduler_run_events_run_callback_if_pending(void **state) + // Set event to pending + event->pending = SCHEDULER_POLL_TIMEOUT; + ++ pthread_mutex_lock(&s.mutex); + const int n_dispatched = scheduler_run_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(n_dispatched, 1); + assert_int_equal(event_cb_spy.was_called, 1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -831,13 +893,17 @@ test_scheduler_run_events_no_callback_if_not_pending(void **state) + + event->pending = 0; + ++ pthread_mutex_lock(&s.mutex); + const int n_dispatched = scheduler_run_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(n_dispatched, 0); + assert_int_equal(event_cb_spy.was_called, 0); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -858,7 +924,9 @@ test_scheduler_run_events_pending_mode_is_reset(void **state) + // Set event to pending + event->pending = SCHEDULER_POLL_TIMEOUT; + ++ pthread_mutex_lock(&s.mutex); + (void)scheduler_run_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + // The callback gets the original pending value + assert_int_equal(event_cb_spy.mode, SCHEDULER_POLL_TIMEOUT); +@@ -867,7 +935,9 @@ test_scheduler_run_events_pending_mode_is_reset(void **state) + assert_int_not_equal(event->pending, SCHEDULER_POLL_TIMEOUT); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -890,13 +960,17 @@ test_scheduler_run_events_ignore_event_if_dead(void **state) + + event->dead = true; + ++ pthread_mutex_lock(&s.mutex); + const int n_dispatched = scheduler_run_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(n_dispatched, 0); + assert_int_equal(event_cb_spy.was_called, 0); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -906,7 +980,9 @@ test_scheduler_run_events_no_events(void **state) + scheduler_t s; + scheduler_initialize(&s); + ++ pthread_mutex_lock(&s.mutex); + const int n_dispatched = scheduler_run_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(n_dispatched, 0); + } +@@ -917,7 +993,9 @@ test_scheduler_prepare_events_no_events(void **state) + // scheduler_prepare_events no events no problem + scheduler_t s; + scheduler_initialize(&s); ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + assert_int_equal(s.max_fd, -1); + } + +@@ -937,12 +1015,16 @@ test_scheduler_prepare_events_masked_event_ignored(void **state) + // Mask the event here + event->masked = true; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, -1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -961,12 +1043,16 @@ test_scheduler_prepare_events_dead_event_ignored(void **state) + // Unalive event here + event->dead = true; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, -1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -981,12 +1067,16 @@ test_scheduler_add_read_event(void **state) + const struct timeval to = {}; + const int id = scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL); + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, test_fd); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1005,13 +1095,17 @@ test_scheduler_read_event_with_invalid_fd(void **state) + // Invalid event + event->fd = -2; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, -1); + + close(fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1026,12 +1120,16 @@ test_scheduler_add_write_event(void **state) + const struct timeval to = {}; + const int event_id = scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL); + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, test_fd); + + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1050,13 +1148,17 @@ test_scheduler_write_event_with_invalid_fd(void **state) + // Invalid event + event->fd = -2; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, -1); + + close(fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1071,12 +1173,16 @@ test_scheduler_add_except_event(void **state) + const struct timeval to = {}; + const int event_id = scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL); + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, test_fd); + + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1095,12 +1201,16 @@ test_scheduler_except_event_with_invalid_fd(void **state) + // Invalid event + event->fd = -2; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, -1); + close(fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1116,14 +1226,18 @@ test_scheduler_no_timeout_events_then_timeout_is_max(void **state) + const struct timeval to = {}; + const int event_id = scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL); + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + const struct timeval expected_tv = TV_SECS(600); + assert_int_equal(s.timeout.tv_sec, expected_tv.tv_sec); + + close(test_fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1143,13 +1257,17 @@ test_scheduler_add_timeout_event(void **state) + const struct timeval time_now = { .tv_sec = 2, .tv_usec = 0}; + fake_gettimeofday = time_now; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + // New timeout value is time to the event deadline + assert_int_equal(s.timeout.tv_sec, to.tv_sec - time_now.tv_sec); + + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1171,14 +1289,18 @@ test_scheduler_multiple_timeout_events_use_lowest_timeout(void **state) + const struct timeval time_now = { .tv_sec = 2, .tv_usec = 0}; + fake_gettimeofday = time_now; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + // New timeout is based on the smaller event timeout value (to2). + assert_int_equal(s.timeout.tv_sec, to2.tv_sec - time_now.tv_sec); + + scheduler_unregister_event(&s, id1); + scheduler_unregister_event(&s, id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1198,13 +1320,17 @@ test_scheduler_timeout_event_is_instant_if_deadline_is_now(void **state) + // Set the time now to the event timeout + fake_gettimeofday = to; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + // New timeout is zero because deadline has already been reached. + assert_int_equal(s.timeout.tv_sec, 0); + + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1226,14 +1352,18 @@ test_scheduler_multiple_timeout_events_dont_interfere(void **state) + // Set the time now to the first event timeout + fake_gettimeofday = to1; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + // Event though event2 still has 10 seconds left event1 is 0 therefor timeout is 0 + assert_int_equal(s.timeout.tv_sec, 0); + + scheduler_unregister_event(&s, id1); + scheduler_unregister_event(&s, id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1250,11 +1380,15 @@ test_scheduler_timeout_event_ignored_if_no_timeout(void **state) + fake_gettimeofday = (struct timeval){ .tv_sec = 0, .tv_usec = 0}; + const int id = scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL); + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, -1); + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1302,7 +1436,9 @@ test_scheduler_run_single_read_fd(void **state) + + close(fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + /* +@@ -1355,7 +1491,9 @@ test_scheduler_run_single_write_fd(void **state) + + close(fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + #if 0 +@@ -1428,7 +1566,9 @@ test_scheduler_run_single_dead_event(void **state) + + close(fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + /* Create two events with the same fd but different callbacks. +@@ -1484,7 +1624,9 @@ test_scheduler_run_duplicate_fds_are_handled_once(void **state) + close(fd); + scheduler_unregister_event(&s, event_id1); + scheduler_unregister_event(&s, event_id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + /* Register two events with different fds but the same callback. +@@ -1525,7 +1667,9 @@ test_scheduler_run_with_duplicate_callbacks(void **state) + close(fd2); + scheduler_unregister_event(&s, event_id1); + scheduler_unregister_event(&s, event_id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1587,7 +1731,9 @@ test_scheduler_run_read_and_write_fd(void **state) + close(fd); + scheduler_unregister_event(&s, event_id1); + scheduler_unregister_event(&s, event_id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1635,5 +1781,7 @@ test_scheduler_run_deleted_duplicate_event(void **state) + + scheduler_unregister_event(&s, event_id1); + scheduler_unregister_event(&s, event_id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } diff --git a/SOURCES/0032-tapdisk-protect-td_nbdserver-structures.patch b/SOURCES/0032-tapdisk-protect-td_nbdserver-structures.patch new file mode 100644 index 0000000..3c77a60 --- /dev/null +++ b/SOURCES/0032-tapdisk-protect-td_nbdserver-structures.patch @@ -0,0 +1,277 @@ +From 3ed6dd2c64cf0f1dfe45845e9279dd9e3b2a78b5 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Fri, 17 Oct 2025 15:14:44 +0200 +Subject: [PATCH] tapdisk: protect td_nbdserver structures + +libqcow2 spawns a thread to handle IO requests to qcow2 layer. +Add a mutex to protect NBD server structures from qcow2 concurrency. + +This is the fifth part of tapdisk threading protection. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-nbdserver.c | 55 +++++++++++++++++++++++++++++++++---- + drivers/tapdisk-nbdserver.h | 2 ++ + 2 files changed, 52 insertions(+), 5 deletions(-) + +diff --git a/drivers/tapdisk-nbdserver.c b/drivers/tapdisk-nbdserver.c +index 23f6e241..66657c35 100644 +--- a/drivers/tapdisk-nbdserver.c ++++ b/drivers/tapdisk-nbdserver.c +@@ -235,10 +235,13 @@ tapdisk_nbdserver_alloc_request(td_nbdserver_client_t *client) + + ASSERT(client); + ++ pthread_mutex_lock(&client->mutex); + if (likely(client->n_reqs_free)) + req = client->reqs_free[--client->n_reqs_free]; ++ pthread_mutex_unlock(&client->mutex); + + pending = tapdisk_nbdserver_reqs_pending(client); ++ pthread_mutex_lock(&client->mutex); + if (pending > client->max_used_reqs) + client->max_used_reqs = pending; + +@@ -246,7 +249,7 @@ tapdisk_nbdserver_alloc_request(td_nbdserver_client_t *client) + /* last free request, mask the events */ + tapdisk_server_mask_event(client->client_event_id, 1); + } +- ++ pthread_mutex_unlock(&client->mutex); + + return req; + } +@@ -318,17 +321,23 @@ void + tapdisk_nbdserver_free_request(td_nbdserver_client_t *client, + td_nbdserver_req_t *req, bool free_client_if_dead) + { ++ pthread_mutex_lock(&client->mutex); + tapdisk_nbdserver_set_free_request(client, req); + + if (unlikely(client->n_reqs_free == (client->n_reqs / 4))) { + /* free requests, unmask the events */ + tapdisk_server_mask_event(client->client_event_id, 0); + } ++ pthread_mutex_unlock(&client->mutex); + + if (unlikely(free_client_if_dead && + client->dead && +- !tapdisk_nbdserver_reqs_pending(client))) ++ !tapdisk_nbdserver_reqs_pending(client))) { ++ td_nbdserver_t *server = client->server; ++ pthread_mutex_lock(&server->mutex); + tapdisk_nbdserver_free_client(client); ++ pthread_mutex_unlock(&server->mutex); ++ } + } + + static void +@@ -800,6 +809,7 @@ tapdisk_nbdserver_alloc_client(td_nbdserver_t *server) + strerror(errno)); + goto fail; + } ++ pthread_mutex_init(&client->mutex, NULL); + + err = tapdisk_nbdserver_reqs_init(client, NBD_SERVER_NUM_REQS); + if (err < 0) { +@@ -810,8 +820,10 @@ tapdisk_nbdserver_alloc_client(td_nbdserver_t *server) + client->client_fd = -1; + client->client_event_id = -1; + client->server = server; ++ pthread_mutex_lock(&server->mutex); + INIT_LIST_HEAD(&client->clientlist); + list_add(&client->clientlist, &server->clients); ++ pthread_mutex_unlock(&server->mutex); + + client->paused = 0; + client->dead = false; +@@ -820,8 +832,10 @@ tapdisk_nbdserver_alloc_client(td_nbdserver_t *server) + return client; + + fail: +- if (client) ++ if (client) { ++ pthread_mutex_destroy(&client->mutex); + free(client); ++ } + + return NULL; + } +@@ -841,6 +855,7 @@ tapdisk_nbdserver_free_client(td_nbdserver_client_t *client) + if (likely(!tapdisk_nbdserver_reqs_pending(client))) { + list_del(&client->clientlist); + tapdisk_nbdserver_reqs_free(client); ++ pthread_mutex_destroy(&client->mutex); + free(client); + } else + client->dead = true; +@@ -1054,7 +1069,9 @@ tapdisk_nbdserver_handshake_cb(event_id_t id, char mode, void *data) + if (tapdisk_nbdserver_enable_client(client) < 0) { + ERR("Error enabling client"); + tmp_fd = client->client_fd; ++ pthread_mutex_lock(&server->mutex); + tapdisk_nbdserver_free_client(client); ++ pthread_mutex_unlock(&server->mutex); + close(tmp_fd); + } + +@@ -1136,7 +1153,9 @@ tapdisk_nbdserver_newclient_fd_old(td_nbdserver_t *server, int new_fd) + INFO("About to enable client on fd %d", client->client_fd); + if (tapdisk_nbdserver_enable_client(client) < 0) { + ERR("Error enabling client"); ++ pthread_mutex_lock(&server->mutex); + tapdisk_nbdserver_free_client(client); ++ pthread_mutex_unlock(&server->mutex); + close(new_fd); + } + } +@@ -1164,7 +1183,9 @@ tapdisk_nbdserver_newclient_fd_new_fixed(td_nbdserver_t *server, int new_fd) + + if(tapdisk_nbdserver_new_protocol_handshake(client, new_fd) != 0) { + ERR("Error handshaking new client connection"); ++ pthread_mutex_lock(&server->mutex); + tapdisk_nbdserver_free_client(client); ++ pthread_mutex_unlock(&server->mutex); + close(new_fd); + return; + } +@@ -1290,7 +1311,9 @@ tapdisk_nbdserver_clientcb(event_id_t id, char mode, void *data) + break; + case TAPDISK_NBD_CMD_DISC: + INFO("Received close message. Sending reconnect header"); ++ pthread_mutex_lock(&server->mutex); + tapdisk_nbdserver_free_client(client); ++ pthread_mutex_unlock(&server->mutex); + INFO("About to send initial connection message"); + tapdisk_nbdserver_newclient_fd(server, fd); + INFO("Sent initial connection message"); +@@ -1338,7 +1361,9 @@ fail: + if (vreq) + tapdisk_nbd_server_free_vreq(client, vreq, false); + close(client->client_fd); ++ pthread_mutex_lock(&server->mutex); + tapdisk_nbdserver_free_client(client); ++ pthread_mutex_unlock(&server->mutex); + return; + } + +@@ -1431,7 +1456,10 @@ tapdisk_nbdserver_alloc(td_vbd_t *vbd, td_disk_info_t info, nbd_protocol_style_t + server->unix_listening_fd = -1; + server->unix_listening_event_id = -1; + server->style = style; ++ pthread_mutex_init(&server->mutex, NULL); ++ pthread_mutex_lock(&server->mutex); + INIT_LIST_HEAD(&server->clients); ++ pthread_mutex_unlock(&server->mutex); + + switch (style) { + case TAPDISK_NBD_PROTOCOL_OLD: +@@ -1479,6 +1507,7 @@ fail: + if (server) { + if (server->fdreceiver) + td_fdreceiver_stop(server->fdreceiver); ++ pthread_mutex_destroy(&server->mutex); + free(server); + } + +@@ -1494,12 +1523,14 @@ tapdisk_nbdserver_pause(td_nbdserver_t *server, bool log) + INFO("NBD server pause(%p)", server); + } + ++ pthread_mutex_lock(&server->mutex); + list_for_each_entry_safe(pos, q, &server->clients, clientlist){ + if (pos->paused != 1 && pos->client_event_id >= 0) { + tapdisk_nbdserver_disable_client(pos); + pos->paused = 1; + } + } ++ pthread_mutex_unlock(&server->mutex); + + if (server->fdrecv_listening_event_id >= 0) { + tapdisk_server_unregister_event(server->fdrecv_listening_event_id); +@@ -1732,15 +1763,18 @@ tapdisk_nbdserver_unpause(td_nbdserver_t *server) + "unix_listening_fd=%d", server, server->fdrecv_listening_fd, + server->unix_listening_fd); + ++ pthread_mutex_lock(&server->mutex); + list_for_each_entry_safe(pos, q, &server->clients, clientlist){ + if (pos->paused == 1) { + if((err = tapdisk_nbdserver_enable_client(pos)) < 0) { ++ pthread_mutex_unlock(&server->mutex); + ERR("Failed to enable nbd client after pause"); + return err; + } + pos->paused = 0; + } + } ++ pthread_mutex_unlock(&server->mutex); + + err = tapdisk_nbdserver_unpause_fdrecv(server); + if (err) +@@ -1761,8 +1795,10 @@ tapdisk_nbdserver_free(td_nbdserver_t *server) + + INFO("NBD server free(%p)", server); + ++ pthread_mutex_lock(&server->mutex); + list_for_each_entry_safe(pos, q, &server->clients, clientlist) + tapdisk_nbdserver_free_client(pos); ++ pthread_mutex_unlock(&server->mutex); + + if (server->fdrecv_listening_event_id >= 0) { + tapdisk_server_unregister_event(server->fdrecv_listening_event_id); +@@ -1796,15 +1832,20 @@ tapdisk_nbdserver_free(td_nbdserver_t *server) + if (err) + ERR("failed to delete NBD metrics: %s\n", strerror(errno)); + ++ pthread_mutex_destroy(&server->mutex); + free(server); + } + + int + tapdisk_nbdserver_reqs_pending(td_nbdserver_client_t *client) + { ++ int pending; + ASSERT(client); + +- return client->n_reqs - client->n_reqs_free; ++ pthread_mutex_lock(&client->mutex); ++ pending = client->n_reqs - client->n_reqs_free; ++ pthread_mutex_unlock(&client->mutex); ++ return pending; + } + + bool +@@ -1815,8 +1856,12 @@ tapdisk_nbdserver_contains_client(td_nbdserver_t *server, + + ASSERT(server); + ++ pthread_mutex_lock(&server->mutex); + list_for_each_entry(_client, &server->clients, clientlist) +- if (client == _client) ++ if (client == _client) { ++ pthread_mutex_unlock(&server->mutex); + return true; ++ } ++ pthread_mutex_unlock(&server->mutex); + return false; + } +diff --git a/drivers/tapdisk-nbdserver.h b/drivers/tapdisk-nbdserver.h +index 098dcb32..e92039f6 100644 +--- a/drivers/tapdisk-nbdserver.h ++++ b/drivers/tapdisk-nbdserver.h +@@ -125,6 +125,7 @@ struct td_nbdserver { + char sockpath[TAPDISK_NBDSERVER_MAX_PATH_LEN]; + + struct list_head clients; ++ pthread_mutex_t mutex; + + stats_t nbd_stats; + +@@ -137,6 +138,7 @@ struct td_nbdserver_client { + struct td_iovec *iovecs; + int n_reqs_free; + td_nbdserver_req_t **reqs_free; ++ pthread_mutex_t mutex; + + int client_fd; + int client_event_id; diff --git a/SOURCES/0033-libqcow2-prepare-proper-cleanup-of-libqcow2-on-close.patch b/SOURCES/0033-libqcow2-prepare-proper-cleanup-of-libqcow2-on-close.patch new file mode 100644 index 0000000..30461fb --- /dev/null +++ b/SOURCES/0033-libqcow2-prepare-proper-cleanup-of-libqcow2-on-close.patch @@ -0,0 +1,134 @@ +From 625aab02193c7af95edc4ba99dfd12d0b0b6b8c1 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 12 Mar 2025 13:46:25 +0100 +Subject: [PATCH] libqcow2: prepare proper cleanup of libqcow2 on close + +The close operation will stop the libqcow2 thread and we'll need to +clean-up resources to prepare the reopen or unpause. + +Signed-off-by: Anthoine Bourgeois +--- + include/qemu/main-loop.h | 2 ++ + qcow2/lib/system/cpus.c | 5 ++++ + qcow2/lib/util/main-loop.c | 49 +++++++++++++++++++++++++++++++++++++- + 3 files changed, 55 insertions(+), 1 deletion(-) + +diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h +index feab3b96..c2983f59 100644 +--- a/include/qemu/main-loop.h ++++ b/include/qemu/main-loop.h +@@ -42,6 +42,7 @@ typedef struct MainLoop MainLoop; + #endif + + void qemu_init_cpu_loop(void); ++void qemu_deinit_cpu_loop(void); + + /** + * qemu_init_main_loop: Set up the process so that it can run the main loop. +@@ -57,6 +58,7 @@ void qemu_init_cpu_loop(void); + * In the case of QEMU tools, this will also start/initialize timers. + */ + int qemu_init_main_loop(Error **errp); ++int qemu_deinit_main_loop(void); + + /** + * main_loop_wait: Run one iteration of the main loop. +diff --git a/qcow2/lib/system/cpus.c b/qcow2/lib/system/cpus.c +index ed2b9ff5..dbf0b3dc 100644 +--- a/qcow2/lib/system/cpus.c ++++ b/qcow2/lib/system/cpus.c +@@ -429,6 +429,11 @@ void qemu_init_cpu_loop(void) + //qemu_thread_get_self(&io_thread); + } + ++void qemu_deinit_cpu_loop(void) ++{ ++ qemu_mutex_destroy(&bql); ++} ++ + #if 0 + void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data) + { +diff --git a/qcow2/lib/util/main-loop.c b/qcow2/lib/util/main-loop.c +index 817e65a6..8bde8add 100644 +--- a/qcow2/lib/util/main-loop.c ++++ b/qcow2/lib/util/main-loop.c +@@ -39,8 +39,12 @@ + #include + #endif + ++static void iohandler_deinit(void); ++ + #ifndef _WIN32 + ++static int sigfd = -1; ++ + /* If we have signalfd, we mask out the signals we want to handle and then + * use signalfd to listen for them. We rely on whatever the current signal + * handler is to dispatch the signals when we receive them. +@@ -86,7 +90,6 @@ static void sigfd_handler(void *opaque) + + static int qemu_signal_init(Error **errp) + { +- int sigfd; + sigset_t set; + + /* +@@ -191,6 +194,39 @@ int qemu_init_main_loop(Error **errp) + return 0; + } + ++int qemu_deinit_main_loop(void) ++{ ++ GSource *src; ++ ++ src = iohandler_get_g_source(); ++ g_source_unref(src); ++ g_source_destroy(src); ++ g_source_unref(src); ++ ++ src = aio_get_g_source(qemu_aio_context); ++ g_source_unref(src); ++ g_source_destroy(src); ++ g_source_unref(src); ++ ++ g_array_free(gpollfds, TRUE); ++ ++ qemu_bh_delete(qemu_notify_bh); ++ qemu_notify_bh = NULL; ++ ++ if (sigfd != -1) { ++ aio_set_fd_handler(iohandler_get_aio_context(), sigfd, NULL, NULL, NULL, NULL, NULL); ++ close(sigfd); ++ sigfd = -1; ++ } ++ ++ iohandler_deinit(); ++ qemu_aio_context = NULL; ++ ++ timerlistgroup_deinit(&main_loop_tlg); ++ ++ return 0; ++} ++ + #if 0 + static void main_loop_update_params(EventLoopBase *base, Error **errp) + { +@@ -633,6 +669,17 @@ static void iohandler_init(void) + } + } + ++static void iohandler_deinit(void) ++{ ++ if (iohandler_ctx) { ++ /* ++ * iohandler_ctx will be freed by the callback aio_ctx_finalize, ++ * but we need to set it to null to reallocate a new one in the future. ++ */ ++ iohandler_ctx = NULL; ++ } ++} ++ + AioContext *iohandler_get_aio_context(void) + { + iohandler_init(); diff --git a/SOURCES/0034-libqcow2-mask-signals-used-by-tapdisk.patch b/SOURCES/0034-libqcow2-mask-signals-used-by-tapdisk.patch new file mode 100644 index 0000000..7de7254 --- /dev/null +++ b/SOURCES/0034-libqcow2-mask-signals-used-by-tapdisk.patch @@ -0,0 +1,41 @@ +From cc2b5528d52cc78d5cba25fbd5ee9a07149fc155 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 15 Oct 2025 14:35:21 +0200 +Subject: [PATCH] libqcow2: mask signals used by tapdisk + +To avoid conflict and signals stolen by the libqcow2 this patch masks the +signals used by tapdisk so they are exclusive to it. + +Signed-off-by: Anthoine Bourgeois +--- + qcow2/lib/util/main-loop.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/qcow2/lib/util/main-loop.c b/qcow2/lib/util/main-loop.c +index 8bde8add..227caf58 100644 +--- a/qcow2/lib/util/main-loop.c ++++ b/qcow2/lib/util/main-loop.c +@@ -102,6 +102,11 @@ static int qemu_signal_init(Error **errp) + sigaddset(&set, SIGIO); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGBUS); ++ sigaddset(&set, SIGINT); ++ sigaddset(&set, SIGXFSZ); ++ //sigaddset(&set, SIGUSR1); // SIG_IPI == SIGUSR1 ++ sigaddset(&set, SIGUSR2); ++ sigaddset(&set, SIGHUP); + /* SIGINT cannot be handled via signalfd, so that ^C can be used + * to interrupt QEMU when it is being run under gdb. SIGHUP and + * SIGTERM are also handled asynchronously, even though it is not +@@ -110,6 +115,11 @@ static int qemu_signal_init(Error **errp) + pthread_sigmask(SIG_BLOCK, &set, NULL); + + sigdelset(&set, SIG_IPI); ++ sigdelset(&set, SIGBUS); ++ sigdelset(&set, SIGINT); ++ sigdelset(&set, SIGXFSZ); ++ sigdelset(&set, SIGUSR2); ++ sigdelset(&set, SIGHUP); + sigfd = qemu_signalfd(&set); + if (sigfd == -1) { + error_setg_errno(errp, errno, "failed to create signalfd"); diff --git a/SOURCES/0035-tapdisk-replace-signals-handling-by-signalfd.patch b/SOURCES/0035-tapdisk-replace-signals-handling-by-signalfd.patch new file mode 100644 index 0000000..c4df7d9 --- /dev/null +++ b/SOURCES/0035-tapdisk-replace-signals-handling-by-signalfd.patch @@ -0,0 +1,138 @@ +From 2d8405db2f9aec38c0c4db856e2dbdc601a56eee Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Fri, 17 Oct 2025 18:10:41 +0200 +Subject: [PATCH] tapdisk: replace signals handling by signalfd + +With libqcow2, each of its threads create an signalfd to handle signals +at the thread level. Implement the same signalfd handler for tapdisk +thread to direct signals. + +No functional change are expected, but to achieve this, we would have +had to remove SIGINT from the managed signals in order for GDB to work. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-server.c | 63 ++++++++++++++++++++++++++++++++++------ + 1 file changed, 54 insertions(+), 9 deletions(-) + +diff --git a/drivers/tapdisk-server.c b/drivers/tapdisk-server.c +index a165d2f2..bf426735 100644 +--- a/drivers/tapdisk-server.c ++++ b/drivers/tapdisk-server.c +@@ -39,7 +39,8 @@ + #include + #include + #include +-#include ++#include ++#include + #ifdef HAVE_EVENTFD + #include + #else +@@ -100,6 +101,8 @@ typedef struct tapdisk_server { + } cpumond_state; + + event_id_t tlog_reopen_evid; ++ event_id_t signal_handler_evid; ++ int sigfd; + } tapdisk_server_t; + + static tapdisk_server_t server; +@@ -387,6 +390,12 @@ tapdisk_server_close(void) + if (likely(server.tlog_reopen_evid >= 0)) + tapdisk_server_unregister_event(server.tlog_reopen_evid); + ++ if (likely(server.signal_handler_evid >= 0)) ++ tapdisk_server_unregister_event(server.signal_handler_evid); ++ ++ if (likely(server.sigfd > 0)) ++ close(server.sigfd); ++ + tapdisk_server_close_tlog(); + tapdisk_server_close_aio(); + } +@@ -421,15 +430,25 @@ __tapdisk_server_run(void) + } + + static void +-tapdisk_server_signal_handler(int signal) ++tapdisk_server_signal_handler(event_id_t id, char mode __attribute__((unused)), void *private) + { ++ int signal; ++ ssize_t size; ++ struct signalfd_siginfo fdsi; + td_vbd_t *vbd, *tmp; + struct td_xenblkif *blkif; + static int xfsz_error_sent = 0; + ++ size = read(server.sigfd, &fdsi, sizeof(fdsi)); ++ if (size != sizeof(fdsi)) { ++ ERR(EFBIG, "failed to read signals"); ++ return; ++ } ++ ++ signal = fdsi.ssi_signo; ++ + switch (signal) { + case SIGBUS: +- case SIGINT: + tapdisk_server_for_each_vbd(vbd, tmp) + tapdisk_vbd_close(vbd); + break; +@@ -777,6 +796,7 @@ tapdisk_server_init(void) + + out: + server.tlog_reopen_evid = -1; ++ server.signal_handler_evid = -1; + + return 0; + } +@@ -828,17 +848,42 @@ int + tapdisk_server_run() + { + int err; ++ sigset_t set; + + err = tapdisk_set_resource_limits(); + if (err) + return err; + +- signal(SIGBUS, tapdisk_server_signal_handler); +- signal(SIGINT, tapdisk_server_signal_handler); +- signal(SIGUSR1, tapdisk_server_signal_handler); +- signal(SIGUSR2, tapdisk_server_signal_handler); +- signal(SIGHUP, tapdisk_server_signal_handler); +- signal(SIGXFSZ, tapdisk_server_signal_handler); ++ sigemptyset(&set); ++ sigaddset(&set, SIGBUS); ++ sigaddset(&set, SIGUSR1); ++ sigaddset(&set, SIGUSR2); ++ sigaddset(&set, SIGHUP); ++ sigaddset(&set, SIGXFSZ); ++ server.sigfd = signalfd(-1, &set, 0); ++ if (server.sigfd == -1) { ++ err = errno; ++ EPRINTF("failed to create a new signalfd: %s\n", ++ strerror(-err)); ++ goto out; ++ } ++ ++ if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) { ++ err = errno; ++ EPRINTF("failed to block signals we'd like to handle with signalfd: %s\n", ++ strerror(-err)); ++ goto out; ++ } ++ ++ err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD, ++ server.sigfd, TV_ZERO, ++ tapdisk_server_signal_handler, &server); ++ if (unlikely(err < 0)) { ++ EPRINTF("failed to register signal handler event: %s\n", strerror(-err)); ++ goto out; ++ } ++ ++ server.signal_handler_evid = err; + + err = tapdisk_server_register_event(SCHEDULER_POLL_TIMEOUT, -1, TV_INF, + tlog_reopen_cb, NULL); diff --git a/SOURCES/0036-qcow2-driver-support-qcow2-images-in-tapdisk.patch b/SOURCES/0036-qcow2-driver-support-qcow2-images-in-tapdisk.patch new file mode 100644 index 0000000..22aebce --- /dev/null +++ b/SOURCES/0036-qcow2-driver-support-qcow2-images-in-tapdisk.patch @@ -0,0 +1,964 @@ +From 7cce89a9e5a1456ebaa45586c982f22c01570032 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:50 +0100 +Subject: [PATCH] qcow2: driver: support qcow2 images in tapdisk + +This commit creates the tapdisk driver for qcow2 format. +It create a thread on open/unpause and destroy it on close/pause. +The libqcow2 handles parents of the VDI on it side, no need for tapdisk +to open parents. Read/Write requests are forward to libqcow2. + +Signed-off-by: Anthoine Bourgeois +Signed-off-by: Emmanuel Varagnat +--- + drivers/Makefile.am | 4 + + drivers/block-qcow2.c | 869 +++++++++++++++++++++++++++++++++++++ + drivers/tapdisk-disktype.c | 8 +- + 3 files changed, 875 insertions(+), 6 deletions(-) + create mode 100644 drivers/block-qcow2.c + +diff --git a/drivers/Makefile.am b/drivers/Makefile.am +index c03ef7ed..70bfd2d5 100644 +--- a/drivers/Makefile.am ++++ b/drivers/Makefile.am +@@ -6,6 +6,7 @@ AM_CFLAGS += $(if $(GCOV),-fprofile-dir=/tmp/coverage/blktap/drivers -fprofile-a + + AM_CPPFLAGS = -D_GNU_SOURCE + AM_CPPFLAGS += -I$(top_srcdir)/include ++AM_CPPFLAGS += $(GLIB_CFLAGS) + + libexec_PROGRAMS = tapdisk + +@@ -90,6 +91,7 @@ libtapdisk_la_SOURCES += block-lcache.c + libtapdisk_la_SOURCES += block-llcache.c + libtapdisk_la_SOURCES += block-nbd.c + libtapdisk_la_SOURCES += block-log.c ++libtapdisk_la_SOURCES += block-qcow2.c + + # shared ring + libtapdisk_la_SOURCES += td-blkif.c +@@ -102,12 +104,14 @@ libtapdisk_la_SOURCES += td-stats.c + libtapdisk_la_SOURCES += td-stats.h + + libtapdisk_la_LIBADD = ../vhd/lib/libvhd.la ++libtapdisk_la_LIBADD += ../qcow2/lib/libqcow2.la + libtapdisk_la_LIBADD += -laio + libtapdisk_la_LIBADD += -lxenevtchn + libtapdisk_la_LIBADD += -lxengnttab + libtapdisk_la_LIBADD += -lz + libtapdisk_la_LIBADD += -lrt + libtapdisk_la_LIBADD += -ldl ++libtapdisk_la_LIBADD += -lm + + # encryption support + lib_LTLIBRARIES = libblockcrypto.la +diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c +new file mode 100644 +index 00000000..99c0e7e2 +--- /dev/null ++++ b/drivers/block-qcow2.c +@@ -0,0 +1,869 @@ ++/* ++ * Copyright (c) 2024, Vates ++ * ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the copyright holder nor the names of its ++ * contributors may be used to endorse or promote products derived from ++ * this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER ++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++/* ++ * block-qcow2.c: asynchronous qcow2 implementation. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "qemu/osdep.h" ++#include "qcow2.h" ++#include "qemu/main-loop.h" ++#include "hw/block/block.h" ++#include "qemu/error-report.h" ++#include "qapi/error.h" ++#include "sysemu/block-backend.h" ++#include "qapi/qmp/qdict.h" ++#include "tapdisk.h" ++#include "tapdisk-driver.h" ++#include "tapdisk-interface.h" ++#include "tapdisk-disktype.h" ++#include "tapdisk-storage.h" ++//#include "block-crypto.h" ++ ++#define DEBUGGING 2 ++ ++#define __TRACE(s) \ ++ do { \ ++ DBG(TLOG_DBG, "%s: QUEUED: %" PRIu64 ", COMPLETED: %" \ ++ PRIu64", RETURNED: %" PRIu64 ", DATA_ALLOCATED: " \ ++ "%u\n", \ ++ blk_name(s->conf.blk), s->queued, s->completed, s->returned, \ ++ QCOW2_REQS - s->vreq_free_count); \ ++ } while(0) ++ ++#if (DEBUGGING == 1) ++ #define DBG(level, _f, _a...) DPRINTF(_f, ##_a) ++ #define ERR(_s, err, _f, _a...) DPRINTF("ERROR: %d: " _f, err, ##_a) ++ #define TRACE(s) ((void)0) ++#elif (DEBUGGING == 2) ++ #define DBG(level, _f, _a...) tlog_write(level, _f, ##_a) ++ #define ERR(_s, _err, _f, _a...) tlog_drv_error((_s)->driver, _err, _f, ##_a) ++ #define TRACE(s) __TRACE(s) ++#else ++ #define DBG(level, _f, _a...) ((void)0) ++ #define ERR(_s, err, _f, _a...) ((void)0) ++ #define TRACE(s) ((void)0) ++#endif ++ ++enum qcow2_ops { ++ QCOW2_OP_READ, ++ QCOW2_OP_WRITE, ++}; ++ ++struct qcow2_state; ++struct qcow2_request; ++ ++#define QCOW2_REQS TAPDISK_DATA_REQUESTS ++ ++struct qcow2_request { ++ int error; ++ enum qcow2_ops op; ++ td_request_t treq; ++ struct qcow2_state *state; ++ BlockAIOCB *aiocb; ++ QSIMPLEQ_ENTRY(qcow2_request) list; ++ int aio_inflight; ++ QEMUIOVector qiov; ++#if DEBUGGING != 0 ++ int id; ++ struct timeval allocate_tv; ++ struct timeval submit_tv; ++ struct timeval complete_tv; ++#endif ++}; ++ ++#if DEBUGGING != 0 ++struct io_stat { ++ uint64_t max_val; ++ uint64_t min_val; ++ uint64_t samples; ++ ++ double mean; ++ double S; ++}; ++#endif ++ ++struct qcow2_state { ++ td_driver_t *driver; ++ const char *name; ++ struct td_vbd_encryption *encryption; ++ td_flag_t flags; ++ ++ BlockConf conf; ++ int blk_shift; ++ ++ int vreq_free_count; ++ struct qcow2_request *vreq_free[QCOW2_REQS]; ++ struct qcow2_request vreq_list[QCOW2_REQS]; ++ QSIMPLEQ_HEAD(inflight_head, qcow2_request) inflight; ++ ++ QEMUBH *bh; ++ AioContext *ctx; ++ ++ /* Open thread */ ++ QemuThread thread; ++ pthread_mutex_t lock; ++ pthread_cond_t cond; ++ bool driver_opened; ++ int open_status; ++ MemReentrancyGuard mem_reentrancy_guard; ++ ++ /* Stats */ ++ uint64_t queued; ++ uint64_t completed; ++ uint64_t returned; ++ uint64_t reads; ++ uint64_t read_size; ++ uint64_t writes; ++ uint64_t write_size; ++ uint64_t kick; ++ uint64_t schedule; ++#if DEBUGGING != 0 ++ struct io_stat read_slat; ++ struct io_stat read_clat; ++ struct io_stat read_lat; ++ struct io_stat write_slat; ++ struct io_stat write_clat; ++ struct io_stat write_lat; ++#endif ++}; ++ ++#define test_qcow2_flag(word, flag) ((word) & (flag)) ++#define set_qcow2_flag(word, flag) ((word) |= (flag)) ++#define clear_qcow2_flag(word, flag) ((word) &= ~(flag)) ++ ++static void qcow2_complete(void *, int); ++static inline void do_aio_read(struct qcow2_state *s, struct qcow2_request *req); ++static inline void do_aio_write(struct qcow2_state *s, struct qcow2_request *req); ++ ++static int ++qcow2_initialize(struct qcow2_state *s, Error **perr) ++{ ++ int err; ++ ++#if DEBUGGING != 0 ++ s->read_slat.min_val = ULONG_MAX; ++ s->read_clat.min_val = ULONG_MAX; ++ s->read_lat.min_val = ULONG_MAX; ++ s->write_slat.min_val = ULONG_MAX; ++ s->write_clat.min_val = ULONG_MAX; ++ s->write_lat.min_val = ULONG_MAX; ++#endif ++ ++ qemu_init_cpu_loop(); ++ bql_lock(); ++ ++ bdrv_init(); ++ ++ err = qemu_init_main_loop(perr); ++ if (err != 0) { ++ EPRINTF("failed to initialize main loop %d\n", err); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static void ++qcow2_free(struct qcow2_state *s) ++{ ++ qemu_deinit_main_loop(); ++ ++ bql_unlock(); ++ qemu_deinit_cpu_loop(); ++} ++ ++static void qcow2_handle_requests(struct qcow2_state *s) ++{ ++ struct qcow2_request *req; ++ ++ pthread_mutex_lock(&s->lock); ++ while ((req = QSIMPLEQ_FIRST(&s->inflight))) { ++ QSIMPLEQ_REMOVE_HEAD(&s->inflight, list); ++ pthread_mutex_unlock(&s->lock); ++ ++ switch (req->op) { ++ case QCOW2_OP_READ: ++ do_aio_read(s, req); ++ break; ++ case QCOW2_OP_WRITE: ++ do_aio_write(s, req); ++ break; ++ } ++ pthread_mutex_lock(&s->lock); ++ } ++ pthread_mutex_unlock(&s->lock); ++} ++ ++static void block_bh(void *opaque) ++{ ++ struct qcow2_state *s = opaque; ++ ++ qcow2_handle_requests(s); ++} ++ ++static void * ++qcow2_open(void *opaque) ++{ ++ int o_flags, err, i; ++ struct qcow2_state *s; ++ td_driver_t *driver; ++ td_flag_t flags; ++ const char *name; ++ QDict *options; ++ QDict *file_layer; ++ Error *local_err = NULL; ++ BlockConf *conf = NULL; ++ bool writethrough; ++ bool has_aio_native = true; ++ const char *cache = "none"; ++ bool has_discard = false; ++ const char *discard = "off"; ++ ++ s = opaque; ++ conf = &s->conf; ++ driver = s->driver; ++ name = s->name; ++ flags = s->flags; ++ ++ err = qcow2_initialize(s, &local_err); ++ if (err) { ++ error_report("Initialization error: %s", error_get_pretty(local_err)); ++ goto fail1; ++ } ++ ++ o_flags = 0; ++ ++ if (test_qcow2_flag(flags, TD_OPEN_RDONLY)) ++ clear_qcow2_flag(o_flags, BDRV_O_RDWR); ++ else ++ set_qcow2_flag(o_flags, BDRV_O_RDWR); ++ ++ if ((test_qcow2_flag(flags, TD_OPEN_RDONLY) || ++ test_qcow2_flag(flags, TD_OPEN_LOCAL_CACHE)) && ++ test_qcow2_flag(flags, TD_OPEN_NO_O_DIRECT)) { ++ clear_qcow2_flag(o_flags, BDRV_O_NOCACHE); ++ cache = "writeback"; ++ has_aio_native = false; ++ } ++ ++ if (test_qcow2_flag(flags, TD_OPEN_QUERY)) { ++ clear_qcow2_flag(o_flags, BDRV_O_RDWR); ++ set_qcow2_flag(o_flags, BDRV_O_NO_IO); ++ set_qcow2_flag(o_flags, BDRV_O_NOCACHE); ++ } ++ if (test_qcow2_flag(o_flags, TD_OPEN_LOCAL_CACHE)) { ++ cache = "writeback"; ++ has_aio_native = false; ++ } ++ ++ options = qdict_new(); ++ file_layer = qdict_new(); ++ ++ qdict_put_str(file_layer, "filename", name); ++ ++ qdict_put_str(options, "driver", "qcow2"); ++ ++ if (has_aio_native) { ++ has_discard = true; ++ discard = "unmap"; ++ } ++ ++ if (has_discard) { ++ qdict_put_str(options, "discard", discard); ++ qdict_put_str(file_layer, "discard", discard); ++ qdict_put_str(options, "detect-zeroes", discard); ++ qdict_put_str(file_layer, "detect-zeroes", discard); ++ } ++ ++ if (has_aio_native) { ++ qdict_put_str(file_layer, "aio", "native"); ++ o_flags |= BDRV_O_NATIVE_AIO; ++ } ++ ++ qdict_put(options, "file", file_layer); ++ ++ err = bdrv_parse_cache_mode(cache, &o_flags, &writethrough); ++ if (err < 0) { ++ error_setg(&local_err, "Invalid source cache option: %s", cache); ++ goto fail1; ++ } ++ ++ if (has_discard) { ++ err = bdrv_parse_discard_flags(discard, &o_flags); ++ if (err < 0) { ++ error_setg(&local_err, "Invalid discard option: %s", cache); ++ goto fail1; ++ } ++ } ++ ++ DPRINTF("discard %d (%s), aio_native %d (cache %s), flags %x\n", has_discard, discard, has_aio_native, cache, o_flags); ++ ++ conf->blk = blk_new_open(name, NULL, options, o_flags, &local_err); ++ if (!conf->blk) { ++ goto fail1; ++ } ++ blk_set_enable_write_cache(conf->blk, !writethrough); ++ ++ ++ if (!blk_is_inserted(conf->blk)) { ++ error_setg(&local_err, "device needs media, but drive is empty"); ++ goto fail; ++ } ++ ++ if (!blkconf_apply_backend_options(conf, test_qcow2_flag(flags, TD_OPEN_RDONLY), true, &local_err)) { ++ goto fail; ++ } ++ ++ if (!blkconf_geometry(conf, NULL, 65535, 255, 255, &local_err)) { ++ goto fail; ++ } ++ ++ if (!blkconf_blocksizes(conf, &local_err)) { ++ goto fail; ++ } ++ ++ if (conf->discard_granularity == -1) { ++ conf->discard_granularity = conf->physical_block_size; ++ } ++ ++ s->vreq_free_count = QCOW2_REQS; ++ for (i = 0; i < QCOW2_REQS; i++) { ++ s->vreq_free[i] = s->vreq_list + i; ++ qemu_iovec_init(&s->vreq_free[i]->qiov, 1); ++ } ++ ++ s->blk_shift = 31 - clz32(conf->logical_block_size); ++ driver->info.size = blk_getlength(conf->blk) >> s->blk_shift; ++ driver->info.sector_size = conf->logical_block_size; ++ driver->info.info = 0; ++ ++ QSIMPLEQ_INIT(&s->inflight); ++ s->ctx = qemu_get_aio_context(); ++ s->bh = aio_bh_new_guarded(s->ctx, block_bh, ++ s, ++ &s->mem_reentrancy_guard); ++ ++ DBG(TLOG_INFO, "qcow2_open: ctx %p bh %p\n", s->ctx, s->bh); ++ ++ DBG(TLOG_INFO, "qcow2_open: done (sz:%"PRIu64", sct:%lu, inf:%u)\n", ++ driver->info.size, driver->info.sector_size, driver->info.info); ++ ++ pthread_mutex_lock(&s->lock); ++ s->open_status = 0; ++ pthread_cond_signal(&s->cond); ++ ++ while (s->driver_opened) { ++ pthread_mutex_unlock(&s->lock); ++ main_loop_wait(false); ++ pthread_mutex_lock(&s->lock); ++ } ++ pthread_mutex_unlock(&s->lock); ++ ++ blk_set_aio_context(conf->blk, qemu_get_aio_context(), &error_abort); ++ ++ blk_drain_all(); ++ ++ qemu_bh_delete(s->bh); ++ ++ s->vreq_free_count = QCOW2_REQS; ++ for (i = 0; i < QCOW2_REQS; i++) { ++ qemu_iovec_destroy(&s->vreq_list[i].qiov); ++ } ++ ++ blk_unref(conf->blk); ++ ++ drain_call_rcu(); ++ ++ qcow2_free(s); ++ ++ return NULL; ++ fail: ++ blk_unref(conf->blk); ++ fail1: ++ EPRINTF("open error: %s\n", error_get_pretty(local_err)); ++ error_free(local_err); ++ ++ pthread_mutex_lock(&s->lock); ++ s->open_status = -EINVAL; ++ pthread_cond_signal(&s->cond); ++ pthread_mutex_unlock(&s->lock); ++ ++ qcow2_free(s); ++ return NULL; ++} ++ ++static int ++_qcow2_open(td_driver_t *driver, const char *name, ++ struct td_vbd_encryption *encryption, td_flag_t flags) ++{ ++ int err; ++ struct qcow2_state *s; ++ ++ /* pre-allocate for all but NFS and LVM storage */ ++ driver->storage = tapdisk_storage_type(name); ++ ++ DPRINTF("%s: name %s, flags %x\n", __func__, name, flags); ++ ++ s = (struct qcow2_state *)driver->data; ++ memset(s, 0, sizeof(struct qcow2_state)); ++ ++ s->driver = driver; ++ s->name = name; ++ s->encryption = encryption; ++ s->flags = flags; ++ ++ err = pthread_cond_init(&s->cond, NULL); ++ if (err) { ++ EPRINTF("failed to init thread condition %d\n", err); ++ return err; ++ } ++ pthread_mutex_init(&s->lock, NULL); ++ ++ pthread_mutex_lock(&s->lock); ++ s->driver_opened = true; ++ qemu_thread_create(&s->thread, "td-qcow2", qcow2_open, s, ++ QEMU_THREAD_JOINABLE); ++ ++ pthread_cond_wait(&s->cond, &s->lock); ++ err = s->open_status; ++ s->open_status = 0; ++ pthread_mutex_unlock(&s->lock); ++ ++ return err; ++} ++ ++static int ++_qcow2_close(td_driver_t *driver) ++{ ++ int err; ++ struct qcow2_state *s = (struct qcow2_state *)driver->data; ++ ++ DBG(TLOG_WARN, "qcow2_close\n"); ++ ++ pthread_mutex_lock(&s->lock); ++ s->driver_opened = false; ++ pthread_mutex_unlock(&s->lock); ++ ++ qemu_bh_schedule(s->bh); ++ ++ // Ignore return, qcow2_open() always return NULL; or will abort ++ qemu_thread_join(&s->thread); ++ ++ err = pthread_cond_destroy(&s->cond); ++ if (err) { ++ EPRINTF("failed to destroy thread condition %d\n", err); ++ return err; ++ } ++ err = pthread_mutex_destroy(&s->lock); ++ if (err) { ++ EPRINTF("failed to destroy mutex %d\n", err); ++ return err; ++ } ++ ++ memset(s, 0, sizeof(struct qcow2_state)); ++ ++ return 0; ++} ++ ++int ++qcow2_validate_parent(td_driver_t *child_driver, ++ td_driver_t *parent_driver, td_flag_t flags) ++{ ++ DPRINTF("qcow2_validate_parent. ptype %d, ctype %d", ++ parent_driver->type, child_driver->type); ++ if (parent_driver->type != DISK_TYPE_QCOW) ++ { ++ if (child_driver->type != DISK_TYPE_QCOW) ++ return -EINVAL; ++ return 0; ++ } ++ ++ return 0; ++} ++ ++int ++qcow2_get_parent_id(td_driver_t *driver, td_disk_id_t *id) ++{ ++ DBG(TLOG_DBG, "\n"); ++ memset(id, 0, sizeof(td_disk_id_t)); ++ ++ return TD_NO_PARENT; ++} ++ ++static inline void ++init_qcow2_request(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ req->state = s; ++#if DEBUGGING != 0 ++ req->id = req - s->vreq_list; ++ gettimeofday(&req->allocate_tv, NULL); ++#endif ++} ++ ++static inline struct qcow2_request * ++alloc_qcow2_request(struct qcow2_state *s) ++{ ++ struct qcow2_request *req = NULL; ++ ++ pthread_mutex_lock(&s->lock); ++ if (s->vreq_free_count > 0) { ++ req = s->vreq_free[--s->vreq_free_count]; ++ pthread_mutex_unlock(&s->lock); ++ ASSERT(req->treq.secs == 0); ++ init_qcow2_request(s, req); ++ return req; ++ } ++ ++ pthread_mutex_unlock(&s->lock); ++ return NULL; ++} ++ ++static inline void ++free_qcow2_request(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ memset(&req->treq, 0, sizeof(req->treq)); ++ ++ qemu_iovec_reset(&req->qiov); ++ ++ pthread_mutex_lock(&s->lock); ++ s->vreq_free[s->vreq_free_count++] = req; ++ pthread_mutex_unlock(&s->lock); ++} ++ ++static inline void ++signal_completion(struct qcow2_request *r) ++{ ++ struct qcow2_state *s = r->state; ++ td_vbd_t *vbd = r->treq.vreq->vbd; ++ ++ td_complete_request(r->treq, r->error); ++ DBG(TLOG_DBG, "lsec: 0x%08"PRIx64", blk: 0x%04x, " ++ "err: %d\n", r->treq.sec, r->treq.secs, r->error); ++ if (r->error == 0) { ++ tapdisk_vbd_kick(vbd); ++ s->kick++; ++ } ++ free_qcow2_request(s, r); ++ ++ s->returned++; ++ TRACE(s); ++} ++ ++#if DEBUGGING != 0 ++static void sum_stat(struct io_stat *dst, struct io_stat *src, bool first) ++{ ++ double mean, S; ++ ++ dst->min_val = MIN(dst->min_val, src->min_val); ++ dst->max_val = MAX(dst->max_val, src->max_val); ++ ++ if (first) { ++ mean = src->mean; ++ S = src->S; ++ } else { ++ double delta = src->mean - dst->mean; ++ ++ mean = ((src->mean * src->samples) + ++ (dst->mean * dst->samples)) / ++ (dst->samples + src->samples); ++ ++ S = src->S + dst->S + pow(delta, 2.0) * ++ (dst->samples * src->samples) / ++ (dst->samples + src->samples); ++ } ++ ++ dst->samples += src->samples; ++ dst->mean = mean; ++ dst->S = S; ++} ++ ++bool calc_lat(struct io_stat *is, uint64_t *min, ++ uint64_t *max, double *mean, double *dev) ++{ ++ double n = (double) is->samples; ++ ++ if (n == 0) ++ return false; ++ ++ *min = is->min_val; ++ *max = is->max_val; ++ *mean = is->mean; ++ ++ if (n > 1.0) ++ *dev = sqrt(is->S / (n - 1.0)); ++ else ++ *dev = 0; ++ ++ return true; ++} ++ ++#define update_latency(dst, latency) \ ++{ \ ++ struct io_stat ios; \ ++ ios.samples = 1; \ ++ ios.S = 0; \ ++ ios.min_val = ios.max_val = ios.mean = (latency); \ ++ sum_stat(&(dst), &ios, dst.samples == 0); \ ++} ++ ++#define print_latency(s, name, lat) \ ++{ \ ++ if (calc_lat(&(s)->name ## _ ## lat, &min, &max, &mean, &dev)) { \ ++ DBG(TLOG_WARN, #name ": " #lat " min %luus, max %luus, mean %fus, S %f, N %lu\n", \ ++ min, max, mean, dev, (s)->name ## _ ## lat.samples); \ ++ } else { \ ++ DBG(TLOG_WARN, #name ": " #lat " min 0us, max 0us, mean 0us, S 0.0, N 0\n"); \ ++ } \ ++} ++ ++#define print_latencies(s) \ ++{ \ ++ uint64_t min, max; \ ++ double mean, dev; \ ++ print_latency((s), read, slat); \ ++ print_latency((s), read, clat); \ ++ print_latency((s), read, lat); \ ++ print_latency((s), write, slat); \ ++ print_latency((s), write, clat); \ ++ print_latency((s), write, lat); \ ++} ++ ++#endif ++ ++static void qcow2_complete(void *opaque, int ret) ++{ ++ struct qcow2_request *req = (struct qcow2_request *)opaque; ++ struct qcow2_state *s = req->state; ++#if DEBUGGING != 0 ++ unsigned long long latency, submit_latency, complete_latency; ++ struct timeval now; ++#endif ++ ++ if (!s) { ++ DBG(TLOG_DBG, "s is null\n"); ++ return; ++ } ++ ++ req->error = ret; ++ ++ if (req->error) ++ ERR(s, req->error, "%s: op: %u, lsec: 0x%08"PRIx64", secs: 0x%04x", ++ req->treq.image->name, req->op, req->treq.sec, req->treq.secs); ++ ++ DBG(TLOG_DBG, "%d: inflight %d\n", req->id, req->aio_inflight); ++ ++#if DEBUGGING != 0 ++ gettimeofday(&now, NULL); ++ latency = timeval_to_us(&now) - timeval_to_us(&req->allocate_tv); ++ submit_latency = timeval_to_us(&req->submit_tv) - timeval_to_us(&req->allocate_tv); ++ complete_latency = timeval_to_us(&now) - timeval_to_us(&req->submit_tv); ++#endif ++ ++ s->completed++; ++ TRACE(s); ++ ++ switch (req->op) { ++ case QCOW2_OP_READ: ++ signal_completion(req); ++#if DEBUGGING != 0 ++ update_latency(s->read_lat, latency); ++ update_latency(s->read_slat, submit_latency); ++ update_latency(s->read_clat, complete_latency); ++#endif ++ break; ++ ++ case QCOW2_OP_WRITE: ++ DBG(TLOG_DBG, "%s: op: %u, lsec: 0x%08"PRIx64", secs: 0x%04x", ++ req->treq.image->name, req->op, req->treq.sec, req->treq.secs); ++ signal_completion(req); ++#if DEBUGGING != 0 ++ update_latency(s->write_lat, latency); ++ update_latency(s->write_slat, submit_latency); ++ update_latency(s->write_clat, complete_latency); ++#endif ++ break; ++ ++ default: ++ ERR(s, req->error, "%s: unknown op: %u", ++ req->treq.image->name, req->op); ++ ASSERT(0 && req->op); ++ break; ++ } ++} ++ ++static inline void ++do_aio_read(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ BlockBackend *blk = s->conf.blk; ++ ++ qemu_iovec_add(&req->qiov, req->treq.buf, req->treq.secs << s->blk_shift); ++ ++ req->aio_inflight++; ++#if DEBUGGING != 0 ++ gettimeofday(&req->submit_tv, NULL); ++#endif ++ req->aiocb = blk_aio_preadv(blk, req->treq.sec << s->blk_shift, &req->qiov, 0, qcow2_complete, req); ++ ++ s->queued++; ++ s->reads++; ++ s->read_size += req->treq.secs; ++ TRACE(s); ++} ++ ++static inline void ++do_aio_write(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ BlockBackend *blk = s->conf.blk; ++ ++ qemu_iovec_add(&req->qiov, req->treq.buf, req->treq.secs << s->blk_shift); ++ ++ req->aio_inflight++; ++#if DEBUGGING != 0 ++ gettimeofday(&req->submit_tv, NULL); ++#endif ++ req->aiocb = blk_aio_pwritev(blk, req->treq.sec << s->blk_shift, &req->qiov, 0, qcow2_complete, req); ++ ++ s->queued++; ++ s->writes++; ++ s->write_size += req->treq.secs; ++ TRACE(s); ++} ++ ++static int ++schedule_request(struct qcow2_state *s, td_request_t *treq, enum qcow2_ops op) ++{ ++ struct qcow2_request *req = NULL; ++ ++ req = alloc_qcow2_request(s); ++ if (!req) ++ return -EBUSY; ++ ++ req->treq = *treq; ++ req->op = op; ++ ++ pthread_mutex_lock(&s->lock); ++ QSIMPLEQ_INSERT_TAIL(&s->inflight, req, list); ++ pthread_mutex_unlock(&s->lock); ++ ++ qemu_bh_schedule(s->bh); ++ s->schedule++; ++ ++ DBG(TLOG_DBG, "%s: lsec: 0x%08"PRIx64", " ++ "nr_secs: 0x%08x, buf: %p, id %d\n", ++ treq->image->name, treq->sec, treq->secs, ++ treq->buf, req->id); ++ ++ return 0; ++} ++ ++static void ++qcow2_queue(td_driver_t *driver, td_request_t *treq, int op) ++{ ++ struct qcow2_state *s = (struct qcow2_state *)driver->data; ++ int err; ++ ++ DBG(TLOG_DBG, "%s: lsec: 0x%08"PRIx64", secs: 0x%04x (seg: %d)\n", ++ treq->image->name, treq->sec, treq->secs, treq->sidx); ++ ++ err = schedule_request(s, treq, op); ++ if (err) ++ goto fail; ++ ++ return; ++fail: ++ DBG(TLOG_DBG, "request failed\n"); ++ td_complete_request(*treq, err); ++} ++ ++static void ++qcow2_queue_read(td_driver_t *driver, td_request_t treq) ++{ ++ qcow2_queue(driver, &treq, QCOW2_OP_READ); ++} ++ ++static void ++qcow2_queue_write(td_driver_t *driver, td_request_t treq) ++{ ++ qcow2_queue(driver, &treq, QCOW2_OP_WRITE); ++} ++ ++void ++qcow2_debug(td_driver_t *driver) ++{ ++ struct qcow2_state *s = (struct qcow2_state *)driver->data; ++ ++ DBG(TLOG_WARN, "Qcow2: %s: queued %lu, completed %lu, returned %lu, " ++ "reads %lu, read sz avg %f, " ++ "writes %lu, write sz avg %f, schedule %lu, kick %lu\n", ++ blk_name(s->conf.blk), ++ s->queued, s->completed, s->returned, ++ s->reads, (s->reads ? ((float)s->read_size / s->reads) : 0.0), ++ s->writes, (s->writes ? ((float)s->write_size / s->writes) : 0.0), ++ s->schedule, s->kick); ++ ++#if DEBUGGING != 0 ++ print_latencies(s); ++#endif ++} ++ ++struct tap_disk tapdisk_qcow = { ++ .disk_type = "tapdisk_qcow2", ++ .flags = 0, ++ .private_data_size = sizeof(struct qcow2_state), ++ .td_open = _qcow2_open, ++ .td_close = _qcow2_close, ++ .td_queue_read = qcow2_queue_read, ++ .td_queue_block_status = NULL, ++ .td_queue_write = qcow2_queue_write, ++ .td_get_parent_id = qcow2_get_parent_id, ++ .td_validate_parent = qcow2_validate_parent, ++ .td_debug = qcow2_debug, ++}; +diff --git a/drivers/tapdisk-disktype.c b/drivers/tapdisk-disktype.c +index 8ab96c4f..71678045 100644 +--- a/drivers/tapdisk-disktype.c ++++ b/drivers/tapdisk-disktype.c +@@ -77,8 +77,8 @@ static const disk_info_t ram_disk = { + }; + + static const disk_info_t qcow_disk = { +- "qcow", +- "qcow disk (qcow)", ++ "qcow2", ++ "QEMU copy-on-write disk (qcow2)", + 0, + }; + +@@ -164,9 +164,7 @@ extern struct tap_disk tapdisk_vhdsync; + #endif + extern struct tap_disk tapdisk_vhd; + extern struct tap_disk tapdisk_ram; +-#if 0 + extern struct tap_disk tapdisk_qcow; +-#endif + extern struct tap_disk tapdisk_block_cache; + extern struct tap_disk tapdisk_vhd_index; + extern struct tap_disk tapdisk_log; +@@ -185,9 +183,7 @@ const struct tap_disk *tapdisk_disk_drivers[] = { + #endif + [DISK_TYPE_VHD] = &tapdisk_vhd, + [DISK_TYPE_RAM] = &tapdisk_ram, +-#if 0 + [DISK_TYPE_QCOW] = &tapdisk_qcow, +-#endif + [DISK_TYPE_BLOCK_CACHE] = &tapdisk_block_cache, + [DISK_TYPE_VINDEX] = &tapdisk_vhd_index, + [DISK_TYPE_LOG] = &tapdisk_log, diff --git a/SOURCES/0037-vbd-wake-up-scheduler-to-force-check-ring.patch b/SOURCES/0037-vbd-wake-up-scheduler-to-force-check-ring.patch new file mode 100644 index 0000000..96d589f --- /dev/null +++ b/SOURCES/0037-vbd-wake-up-scheduler-to-force-check-ring.patch @@ -0,0 +1,230 @@ +From 638fa45ef2e12170997f50899ce1ebe42662d5f7 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 8 Oct 2025 14:05:35 +0200 +Subject: [PATCH] vbd: wake-up scheduler to force check ring + +In the case of a write barrier, the ring is only explore until the +barrier lefting pending requests hereafter. The completion of a barrier +will trigger a ring check but if the select is already sleeping, because +libqcow2 completion is in another thread, it will not wake it up so it +will have to wait the next wake up or timeout. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/block-qcow2.c | 4 +-- + drivers/tapdisk-server.c | 2 +- + drivers/tapdisk-vbd.c | 71 +++++++++++++++++++++++++++++++++++++++- + drivers/tapdisk-vbd.h | 6 +++- + drivers/tapdisk.h | 2 ++ + 5 files changed, 80 insertions(+), 5 deletions(-) + +diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c +index 99c0e7e2..454c3bf5 100644 +--- a/drivers/block-qcow2.c ++++ b/drivers/block-qcow2.c +@@ -588,7 +588,7 @@ signal_completion(struct qcow2_request *r) + DBG(TLOG_DBG, "lsec: 0x%08"PRIx64", blk: 0x%04x, " + "err: %d\n", r->treq.sec, r->treq.secs, r->error); + if (r->error == 0) { +- tapdisk_vbd_kick(vbd); ++ tapdisk_vbd_kick(vbd, true); + s->kick++; + } + free_qcow2_request(s, r); +@@ -856,7 +856,7 @@ qcow2_debug(td_driver_t *driver) + + struct tap_disk tapdisk_qcow = { + .disk_type = "tapdisk_qcow2", +- .flags = 0, ++ .flags = TD_DRIVER_THREADED, + .private_data_size = sizeof(struct qcow2_state), + .td_open = _qcow2_open, + .td_close = _qcow2_close, +diff --git a/drivers/tapdisk-server.c b/drivers/tapdisk-server.c +index bf426735..812ecd90 100644 +--- a/drivers/tapdisk-server.c ++++ b/drivers/tapdisk-server.c +@@ -283,7 +283,7 @@ tapdisk_server_kick_responses(void) + td_vbd_t *vbd, *tmp; + + tapdisk_server_for_each_vbd(vbd, tmp) +- tapdisk_vbd_kick(vbd); ++ tapdisk_vbd_kick(vbd, false); + } + + static void +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 6e1f95d3..a71207a7 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -39,10 +39,12 @@ + #include + #include + #include ++#include + #include + #include + #include + #include ++#include + + #include "debug.h" + #include "libvhd.h" +@@ -59,6 +61,7 @@ + #include "tapdisk-nbdserver.h" + #include "td-stats.h" + #include "tapdisk-utils.h" ++#include "timeout-math.h" + + #define DBG(_level, _f, _a...) tlog_write(_level, _f, ##_a) + #define ERR(_err, _f, _a...) tlog_error(_err, _f, ##_a) +@@ -283,6 +286,13 @@ tapdisk_vbd_close_vdi(td_vbd_t *vbd) + + pthread_mutex_lock(&vbd->mutex); + td_flag_set(vbd->state, TD_VBD_CLOSED); ++ ++ if (td_flag_test(vbd->driver_flags, TD_DRIVER_THREADED)) { ++ tapdisk_server_unregister_event(vbd->event); ++ vbd->event = -1; ++ close(vbd->efd); ++ vbd->efd = -1; ++ } + pthread_mutex_unlock(&vbd->mutex); + } + +@@ -576,6 +586,24 @@ fail: + return err; + } + ++void ++tapdisk_vbd_event_cb(event_id_t id __attribute__((unused)), ++ char mode __attribute__((unused)), void *private) ++{ ++ td_vbd_t *vbd = private; ++ uint64_t u; ++ ssize_t s; ++ ++ pthread_mutex_lock(&vbd->mutex); ++ if (vbd->efd < 0) ++ goto unlock; ++ ++ s = read(vbd->efd, &u, sizeof(uint64_t)); ++ ASSERT(s == sizeof(uint64_t)); ++unlock: ++ pthread_mutex_unlock(&vbd->mutex); ++} ++ + int + tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_devnum) + { +@@ -604,6 +632,26 @@ tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_d + if (err) + goto fail; + ++ if (td_flag_test(tapdisk_vbd_first_image(vbd)->driver->ops->flags, TD_DRIVER_THREADED)) { ++ vbd->driver_flags = tapdisk_vbd_first_image(vbd)->driver->ops->flags; ++ ++ vbd->efd = eventfd(0, 0); ++ if (vbd->efd == -1) { ++ err = errno; ++ ERROR("Failed to create eventfd: %s\n", strerror(-err)); ++ goto fail; ++ } ++ ++ vbd->event = tapdisk_server_register_event( ++ SCHEDULER_POLL_READ_FD, vbd->efd, TV_INF, ++ tapdisk_vbd_event_cb, vbd); ++ if (unlikely(vbd->event < 0)) { ++ err = vbd->event; ++ ERROR("Failed to register eventfd: %s\n", strerror(-err)); ++ goto fail; ++ } ++ } ++ + td_flag_clear(vbd->state, TD_VBD_CLOSED); + vbd->flags = flags; + +@@ -660,6 +708,14 @@ fail: + free(vbd->name); + vbd->name = tmp; + } ++ if (vbd->event > 0) { ++ tapdisk_server_unregister_event(vbd->event); ++ vbd->event = -1; ++ } ++ if (vbd->efd > 0) { ++ close(vbd->efd); ++ vbd->efd = -1; ++ } + + if (!list_empty(&vbd->images)) + tapdisk_image_close_chain(&vbd->images); +@@ -1869,10 +1925,11 @@ tapdisk_vbd_queue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + } + + void +-tapdisk_vbd_kick(td_vbd_t *vbd) ++tapdisk_vbd_kick(td_vbd_t *vbd, bool scheduler_kick) + { + const struct list_head *list; + td_vbd_request_t *vreq, *prev, *next; ++ ssize_t s; + + pthread_mutex_lock(&vbd->mutex); + vbd->kicked++; +@@ -1907,6 +1964,18 @@ tapdisk_vbd_kick(td_vbd_t *vbd) + prev->cb(prev, prev->error, prev->token, 1); + vbd->returned++; + } ++ ++ if (scheduler_kick && td_flag_test(vbd->driver_flags, TD_DRIVER_THREADED)) { ++ static uint64_t token = 1; ++ ++ if (vbd->efd < 0) { ++ pthread_mutex_unlock(&vbd->mutex); ++ return; ++ } ++ ++ s = write(vbd->efd, &token, sizeof(uint64_t)); ++ ASSERT(s == sizeof(uint64_t)); ++ } + pthread_mutex_unlock(&vbd->mutex); + } + +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index f3fa0398..eb2b1425 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -167,6 +167,10 @@ struct td_vbd_handle { + struct td_vbd_encryption encryption; + + bool watchdog_warned; ++ ++ td_flag_t driver_flags; ++ int efd; ++ event_id_t event; + }; + + #define tapdisk_vbd_for_each_request(vreq, tmp, list) \ +@@ -223,7 +227,7 @@ int tapdisk_vbd_kill_queue(td_vbd_t *); + int tapdisk_vbd_pause(td_vbd_t *); + void tapdisk_vbd_squash_pause_logging(bool squash); + int tapdisk_vbd_resume(td_vbd_t *, const char *); +-void tapdisk_vbd_kick(td_vbd_t *); ++void tapdisk_vbd_kick(td_vbd_t *, bool); + void tapdisk_vbd_check_state(td_vbd_t *); + void tapdisk_vbd_free(td_vbd_t *); + +diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h +index d907d149..7e96b5d2 100644 +--- a/drivers/tapdisk.h ++++ b/drivers/tapdisk.h +@@ -115,6 +115,8 @@ enum TD_OPS{ + #define TD_CREATE_SPARSE 0x00001 + #define TD_CREATE_MULTITYPE 0x00002 + ++#define TD_DRIVER_THREADED 0x00001 ++ + #define td_flag_set(word, flag) ((word) |= (flag)) + #define td_flag_clear(word, flag) ((word) &= ~(flag)) + #define td_flag_test(word, flag) ((word) & (flag)) diff --git a/SOURCES/0038-tapdisk-support-new-commit-command.patch b/SOURCES/0038-tapdisk-support-new-commit-command.patch new file mode 100644 index 0000000..081ffa1 --- /dev/null +++ b/SOURCES/0038-tapdisk-support-new-commit-command.patch @@ -0,0 +1,378 @@ +From a6242df1cb58d476b2cd376365ec1d4a7e74062c Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Mon, 10 Feb 2025 16:45:05 +0100 +Subject: [PATCH] tapdisk: support new commit command + +This command will coalesce the disk in parameter with its parent. +tap-ctl commit -m 0 -p 12345 -a /path/to/disk.qcow2 + +Signed-off-by: Anthoine Bourgeois +--- + control/Makefile.am | 1 + + control/tap-ctl-commit.c | 76 +++++++++++++++++++++++++++++++++++++ + control/tap-ctl.c | 47 +++++++++++++++++++++++ + drivers/tapdisk-control.c | 36 ++++++++++++++++++ + drivers/tapdisk-interface.c | 25 ++++++++++++ + drivers/tapdisk-interface.h | 1 + + drivers/tapdisk-vbd.c | 16 ++++++++ + drivers/tapdisk-vbd.h | 1 + + drivers/tapdisk.h | 1 + + include/tap-ctl.h | 5 +++ + include/tapdisk-message.h | 8 ++++ + 11 files changed, 217 insertions(+) + create mode 100644 control/tap-ctl-commit.c + +diff --git a/control/Makefile.am b/control/Makefile.am +index 6ca88ab7..5f954c92 100644 +--- a/control/Makefile.am ++++ b/control/Makefile.am +@@ -33,6 +33,7 @@ libblktapctl_la_SOURCES += tap-ctl-check.c + libblktapctl_la_SOURCES += tap-ctl-stats.c + libblktapctl_la_SOURCES += tap-ctl-xen.c + libblktapctl_la_SOURCES += tap-ctl-info.c ++libblktapctl_la_SOURCES += tap-ctl-commit.c + + libblktapctl_la_LDFLAGS = -version-info 1:1:1 + +diff --git a/control/tap-ctl-commit.c b/control/tap-ctl-commit.c +new file mode 100644 +index 00000000..e3b30853 +--- /dev/null ++++ b/control/tap-ctl-commit.c +@@ -0,0 +1,76 @@ ++/* ++ * Copyright (c) 2025, Vates ++ * ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the copyright holder nor the names of its ++ * contributors may be used to endorse or promote products derived from ++ * this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER ++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tap-ctl.h" ++#include "util.h" ++ ++int ++tap_ctl_commit(const int id, const int minor, const char *params) ++{ ++ int err; ++ tapdisk_message_t message; ++ ++ memset(&message, 0, sizeof(message)); ++ message.type = TAPDISK_MESSAGE_COMMIT; ++ message.cookie = minor; ++ ++ if (params) ++ safe_strncpy(message.u.params.path, params, ++ sizeof(message.u.params.path)); ++ ++ err = tap_ctl_connect_send_and_receive(id, &message, NULL); ++ if (err) ++ return err; ++ ++ if (message.type == TAPDISK_MESSAGE_COMMIT_RSP ++ || message.type == TAPDISK_MESSAGE_ERROR) ++ err = -message.u.response.error; ++ else { ++ EPRINTF("got unexpected result '%s' from %d\n", ++ tapdisk_message_name(message.type), id); ++ err = -EINVAL; ++ } ++ ++ if (err) ++ EPRINTF("commit failed: %s\n", strerror(-err)); ++ ++ return err; ++} +diff --git a/control/tap-ctl.c b/control/tap-ctl.c +index 64d96d0d..25d2248d 100644 +--- a/control/tap-ctl.c ++++ b/control/tap-ctl.c +@@ -943,6 +943,52 @@ usage: + return EINVAL; + } + ++static void ++tap_cli_commit_usage(FILE *stream) ++{ ++ fprintf(stream, "usage: commit <-p pid> <-m minor> <-a /path/to/file>\n"); ++} ++ ++int ++tap_cli_commit(int argc, char **argv) ++{ ++ const char *args; ++ int c, pid, minor; ++ ++ pid = -1; ++ minor = -1; ++ args = NULL; ++ ++ optind = 0; ++ while ((c = getopt(argc, argv, "p:m:a:h")) != -1) { ++ switch (c) { ++ case 'p': ++ pid = atoi(optarg); ++ break; ++ case 'm': ++ minor = atoi(optarg); ++ break; ++ case 'a': ++ args = optarg; ++ break; ++ case '?': ++ goto usage; ++ case 'h': ++ tap_cli_commit_usage(stdout); ++ return 0; ++ } ++ } ++ ++ if (pid == -1 || minor == -1 || args == NULL) ++ goto usage; ++ ++ return tap_ctl_commit(pid, minor, args); ++ ++usage: ++ tap_cli_commit_usage(stderr); ++ return EINVAL; ++} ++ + struct command commands[] = { + { .name = "list", .func = tap_cli_list }, + { .name = "allocate", .func = tap_cli_allocate }, +@@ -959,6 +1005,7 @@ struct command commands[] = { + { .name = "stats", .func = tap_cli_stats }, + { .name = "major", .func = tap_cli_major }, + { .name = "check", .func = tap_cli_check }, ++ { .name = "commit", .func = tap_cli_commit }, + }; + + #define print_commands() \ +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index 39e7ab0e..7466f02c 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -1307,6 +1307,38 @@ out: + return err; + } + ++static int ++tapdisk_control_commit(struct tapdisk_ctl_conn *conn, ++ tapdisk_message_t *request, tapdisk_message_t * const response) ++{ ++ int err; ++ td_vbd_t *vbd; ++ const char *desc = NULL; ++ ++ ASSERT(conn); ++ ASSERT(request); ++ ASSERT(response); ++ ++ INFO("commit %d\n", request->cookie); ++ ++ vbd = tapdisk_server_get_vbd(request->cookie); ++ if (!vbd) { ++ /* TODO log error */ ++ err = -ENODEV; ++ goto out; ++ } ++ ++ if (request->u.params.path[0]) ++ desc = request->u.params.path; ++ ++ err = tapdisk_vbd_commit(vbd, desc); ++out: ++ response->cookie = request->cookie; ++ if (!err) ++ response->type = TAPDISK_MESSAGE_COMMIT_RSP; ++ return err; ++} ++ + + struct tapdisk_control_info message_infos[] = { + [TAPDISK_MESSAGE_PID] = { +@@ -1364,6 +1396,10 @@ struct tapdisk_control_info message_infos[] = { + [TAPDISK_MESSAGE_EXIT] = { + .handler = NULL, + .flags = 0 ++ }, ++ [TAPDISK_MESSAGE_COMMIT] = { ++ .handler = tapdisk_control_commit, ++ .flags = TAPDISK_MSG_VERBOSE, + } + }; + +diff --git a/drivers/tapdisk-interface.c b/drivers/tapdisk-interface.c +index 2cdd1286..5f562707 100644 +--- a/drivers/tapdisk-interface.c ++++ b/drivers/tapdisk-interface.c +@@ -270,6 +270,31 @@ fail: + td_complete_request(*treq, err); + } + ++int ++td_commit(td_image_t *image, const char *name) ++{ ++ td_driver_t *driver; ++ ++ if (!image) { ++ return -ENODEV; ++ } ++ ++ driver = image->driver; ++ if (!driver) { ++ return -ENODEV; ++ } ++ ++ if (!td_flag_test(driver->state, TD_DRIVER_OPEN)) { ++ return -EBADF; ++ } ++ ++ if (!driver->ops->td_commit) { ++ return -EOPNOTSUPP; ++ } ++ ++ return driver->ops->td_commit(driver, name); ++} ++ + void + td_forward_request(td_request_t treq) + { +diff --git a/drivers/tapdisk-interface.h b/drivers/tapdisk-interface.h +index 437feb58..076528ea 100644 +--- a/drivers/tapdisk-interface.h ++++ b/drivers/tapdisk-interface.h +@@ -42,6 +42,7 @@ int td_load(td_image_t *); + int td_close(td_image_t *); + int td_get_parent_id(td_image_t *, td_disk_id_t *); + int td_validate_parent(td_image_t *, td_image_t *); ++int td_commit(td_image_t *, const char *); + + void td_queue_write(td_image_t *, td_request_t); + void td_queue_read(td_image_t *, td_request_t); +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index a71207a7..98a24db8 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -1133,6 +1133,22 @@ resume_failed: + return 0; + } + ++int ++tapdisk_vbd_commit(td_vbd_t *vbd, const char *name) ++{ ++ int err; ++ ++ if (log) { ++ INFO("commit %s\n", name); ++ } ++ ++ err = td_commit(tapdisk_vbd_first_image(vbd), name); ++ ++ INFO("commit started (%d)\n", err); ++ ++ return err; ++} ++ + static int + tapdisk_vbd_request_ttl(td_vbd_request_t *vreq, + const struct timeval *now) +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index eb2b1425..909c2549 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -230,6 +230,7 @@ int tapdisk_vbd_resume(td_vbd_t *, const char *); + void tapdisk_vbd_kick(td_vbd_t *, bool); + void tapdisk_vbd_check_state(td_vbd_t *); + void tapdisk_vbd_free(td_vbd_t *); ++int tapdisk_vbd_commit(td_vbd_t *, const char *); + + void tapdisk_vbd_complete_td_request(td_request_t, int); + int add_extent(tapdisk_extents_t *, td_request_t *); +diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h +index 7e96b5d2..482c323e 100644 +--- a/drivers/tapdisk.h ++++ b/drivers/tapdisk.h +@@ -228,6 +228,7 @@ struct tap_disk { + void (*td_queue_write) (td_driver_t *, td_request_t); + void (*td_debug) (td_driver_t *); + void (*td_stats) (td_driver_t *, td_stats_t *); ++ int (*td_commit) (td_driver_t *, const char *); + + /** + * Callback to produce RRD output. +diff --git a/include/tap-ctl.h b/include/tap-ctl.h +index f2db49fe..03bb0434 100644 +--- a/include/tap-ctl.h ++++ b/include/tap-ctl.h +@@ -143,6 +143,11 @@ int tap_ctl_stats_fwrite(pid_t pid, int minor, FILE *out); + + int tap_ctl_blk_major(void); + ++/** ++ * Commit a VBD. ++ */ ++int tap_ctl_commit(const int id, const int minor, const char *params); ++ + /** + * Instructs a tapdisk to connect to the shared ring. + * +diff --git a/include/tapdisk-message.h b/include/tapdisk-message.h +index 7c54fb3b..13295123 100644 +--- a/include/tapdisk-message.h ++++ b/include/tapdisk-message.h +@@ -235,6 +235,8 @@ enum tapdisk_message_id { + TAPDISK_MESSAGE_DISK_INFO, + TAPDISK_MESSAGE_DISK_INFO_RSP, + TAPDISK_MESSAGE_EXIT, ++ TAPDISK_MESSAGE_COMMIT, ++ TAPDISK_MESSAGE_COMMIT_RSP, + TAPDISK_MESSAGE_MAX /* This value must be the last. */ + }; + +@@ -329,6 +331,12 @@ tapdisk_message_name(enum tapdisk_message_id id) + case TAPDISK_MESSAGE_EXIT: + return "exit"; + ++ case TAPDISK_MESSAGE_COMMIT: ++ return "commit"; ++ ++ case TAPDISK_MESSAGE_COMMIT_RSP: ++ return "commit response"; ++ + default: + return "unknown"; + } diff --git a/SOURCES/0039-qcow2-support-commit-command.patch b/SOURCES/0039-qcow2-support-commit-command.patch new file mode 100644 index 0000000..c06bf02 --- /dev/null +++ b/SOURCES/0039-qcow2-support-commit-command.patch @@ -0,0 +1,206 @@ +From 2c453575368fef852682fbd90047e6a5390cfb47 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Mon, 10 Feb 2025 18:54:25 +0100 +Subject: [PATCH] qcow2: support commit command + +This command will coalesce the disk in parameter with its parent. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/block-qcow2.c | 121 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 120 insertions(+), 1 deletion(-) + +diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c +index 454c3bf5..4e242237 100644 +--- a/drivers/block-qcow2.c ++++ b/drivers/block-qcow2.c +@@ -60,6 +60,8 @@ + #include "qapi/error.h" + #include "sysemu/block-backend.h" + #include "qapi/qmp/qdict.h" ++#include "qapi/qapi-commands-block-core.h" ++ + #include "tapdisk.h" + #include "tapdisk-driver.h" + #include "tapdisk-interface.h" +@@ -95,6 +97,7 @@ + enum qcow2_ops { + QCOW2_OP_READ, + QCOW2_OP_WRITE, ++ QCOW2_OP_COMMIT, + }; + + struct qcow2_state; +@@ -105,7 +108,12 @@ struct qcow2_request; + struct qcow2_request { + int error; + enum qcow2_ops op; +- td_request_t treq; ++ union { ++ /* OP_READ, OP_WRITE */ ++ td_request_t treq; ++ /* OP_COMMIT */ ++ char * top; ++ }; + struct qcow2_state *state; + BlockAIOCB *aiocb; + QSIMPLEQ_ENTRY(qcow2_request) list; +@@ -155,6 +163,12 @@ struct qcow2_state { + int open_status; + MemReentrancyGuard mem_reentrancy_guard; + ++ /* commit/query synchronization */ ++#define COMMIT_JOB_ID "JIDCOMMIT0" ++ pthread_mutex_t commit_lock; ++ pthread_cond_t commit_cond; ++ JobInfo job_info; ++ + /* Stats */ + uint64_t queued; + uint64_t completed; +@@ -182,6 +196,7 @@ struct qcow2_state { + static void qcow2_complete(void *, int); + static inline void do_aio_read(struct qcow2_state *s, struct qcow2_request *req); + static inline void do_aio_write(struct qcow2_state *s, struct qcow2_request *req); ++static inline void do_commit(struct qcow2_state *s, struct qcow2_request *req); + + static int + qcow2_initialize(struct qcow2_state *s, Error **perr) +@@ -236,6 +251,9 @@ static void qcow2_handle_requests(struct qcow2_state *s) + case QCOW2_OP_WRITE: + do_aio_write(s, req); + break; ++ case QCOW2_OP_COMMIT: ++ do_commit(s, req); ++ break; + } + pthread_mutex_lock(&s->lock); + } +@@ -466,6 +484,12 @@ _qcow2_open(td_driver_t *driver, const char *name, + return err; + } + pthread_mutex_init(&s->lock, NULL); ++ err = pthread_cond_init(&s->commit_cond, NULL); ++ if (err) { ++ EPRINTF("failed to init thread condition %d\n", err); ++ return err; ++ } ++ pthread_mutex_init(&s->commit_lock, NULL); + + pthread_mutex_lock(&s->lock); + s->driver_opened = true; +@@ -497,6 +521,17 @@ _qcow2_close(td_driver_t *driver) + // Ignore return, qcow2_open() always return NULL; or will abort + qemu_thread_join(&s->thread); + ++ err = pthread_cond_destroy(&s->commit_cond); ++ if (err) { ++ EPRINTF("failed to destroy thread condition %d\n", err); ++ return err; ++ } ++ err = pthread_mutex_destroy(&s->commit_lock); ++ if (err) { ++ EPRINTF("failed to destroy mutex %d\n", err); ++ return err; ++ } ++ + err = pthread_cond_destroy(&s->cond); + if (err) { + EPRINTF("failed to destroy thread condition %d\n", err); +@@ -835,6 +870,89 @@ qcow2_queue_write(td_driver_t *driver, td_request_t treq) + qcow2_queue(driver, &treq, QCOW2_OP_WRITE); + } + ++int ++qcow2_commit(td_driver_t *driver, const char *name) ++{ ++ struct qcow2_state *s = (struct qcow2_state *)driver->data; ++ struct qcow2_request *req; ++ int err; ++ ++ if (name == NULL) ++ return -EINVAL; ++ ++ DBG(TLOG_WARN, "Qcow2: commit %s.\n", name); ++ ++ req = alloc_qcow2_request(s); ++ if (!req) ++ return -EBUSY; ++ ++ req->top = strdup(name); ++ req->op = QCOW2_OP_COMMIT; ++ ++ pthread_mutex_lock(&s->lock); ++ QSIMPLEQ_INSERT_TAIL(&s->inflight, req, list); ++ pthread_mutex_unlock(&s->lock); ++ ++ pthread_mutex_lock(&s->commit_lock); ++ qemu_bh_schedule(s->bh); ++ ++ pthread_cond_wait(&s->commit_cond, &s->commit_lock); ++ err = req->error; ++ pthread_mutex_unlock(&s->commit_lock); ++ ++ free(req->top); ++ free_qcow2_request(s, req); ++ ++ return err; ++} ++ ++static inline void ++do_commit(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ Error *local_err = NULL; ++ char *node, *top_node, *base_node; ++ BlockDriverState *bs, *top_bs, *base_bs; ++ int err = 0; ++ ++ bs = blk_bs(s->conf.blk); ++ node = bs->node_name; ++ if (strcmp(bs->filename, req->top) == 0) { ++ top_bs = bs; ++ } else { ++ top_bs = bdrv_find_backing_image(bs, req->top); ++ } ++ if (top_bs == NULL) { ++ DPRINTF("qcow2_commit: top '%s' doesn't exist\n", req->top); ++ goto signal_commit; ++ } ++ top_node = top_bs->node_name; ++ base_bs = bdrv_backing_chain_next(top_bs); ++ if (base_bs == NULL) { ++ DPRINTF("qcow2_commit: no base to commit in\n"); ++ goto signal_commit; ++ } ++ base_node = base_bs->node_name; ++ ++ DBG(TLOG_DBG, "Qcow2: block commit %s (node-name: '%s').\n", req->top, node); ++ DBG(TLOG_DBG, "Qcow2: block commit: top-node: '%s' on '%s' base-node).\n", top_node, base_node); ++ ++ qmp_block_commit(COMMIT_JOB_ID, node, base_node, NULL, top_node, NULL, NULL, ++ false, false, false, 0, false, BLOCKDEV_ON_ERROR_REPORT, ++ NULL, true, true, true, false, &local_err); ++ ++ if (local_err) { ++ DPRINTF("qcow2_commit: error: %s\n", error_get_pretty(local_err)); ++ error_free(local_err); ++ err = -EINVAL; ++ } ++ ++signal_commit: ++ pthread_mutex_lock(&s->commit_lock); ++ req->error = err; ++ pthread_cond_signal(&s->commit_cond); ++ pthread_mutex_unlock(&s->commit_lock); ++} ++ + void + qcow2_debug(td_driver_t *driver) + { +@@ -865,5 +983,6 @@ struct tap_disk tapdisk_qcow = { + .td_queue_write = qcow2_queue_write, + .td_get_parent_id = qcow2_get_parent_id, + .td_validate_parent = qcow2_validate_parent, ++ .td_commit = qcow2_commit, + .td_debug = qcow2_debug, + }; diff --git a/SOURCES/0040-tapdisk-support-new-query-command.patch b/SOURCES/0040-tapdisk-support-new-query-command.patch new file mode 100644 index 0000000..c807378 --- /dev/null +++ b/SOURCES/0040-tapdisk-support-new-query-command.patch @@ -0,0 +1,370 @@ +From ccbf8e59540ec8a477b309b5f0a10c3bf60c1a7a Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 13 Feb 2025 08:54:48 +0100 +Subject: [PATCH] tapdisk: support new query command + +This command will print the status of the coalesce in background. +$ tap-ctl query -m 0 -p 12345 +Commit status 'running' (2817589248/4558422016) + +Signed-off-by: Anthoine Bourgeois +--- + control/tap-ctl-commit.c | 31 +++++++++++++++++++++++++++ + control/tap-ctl.c | 42 +++++++++++++++++++++++++++++++++++++ + drivers/tapdisk-control.c | 41 ++++++++++++++++++++++++++++++++++++ + drivers/tapdisk-interface.c | 25 ++++++++++++++++++++++ + drivers/tapdisk-interface.h | 1 + + drivers/tapdisk-vbd.c | 16 ++++++++++++++ + drivers/tapdisk-vbd.h | 1 + + drivers/tapdisk.h | 8 +++++++ + include/tap-ctl.h | 1 + + include/tapdisk-message.h | 18 +++++++++++++++- + 10 files changed, 183 insertions(+), 1 deletion(-) + +diff --git a/control/tap-ctl-commit.c b/control/tap-ctl-commit.c +index e3b30853..04985f69 100644 +--- a/control/tap-ctl-commit.c ++++ b/control/tap-ctl-commit.c +@@ -74,3 +74,34 @@ tap_ctl_commit(const int id, const int minor, const char *params) + + return err; + } ++ ++int ++tap_ctl_query_commit_job(const int id, const int minor) ++{ ++ int err; ++ tapdisk_message_t message; ++ ++ memset(&message, 0, sizeof(message)); ++ message.type = TAPDISK_MESSAGE_QUERY_COMMIT_JOB; ++ message.cookie = minor; ++ ++ err = tap_ctl_connect_send_and_receive(id, &message, NULL); ++ if (err) ++ return err; ++ ++ if (message.type == TAPDISK_MESSAGE_QUERY_COMMIT_JOB_RSP) { ++ printf("Commit status '%s' (%lu/%lu)\n", message.u.query.status, message.u.query.current_progress, message.u.query.total_progress); ++ err = 0; ++ } else if (message.type == TAPDISK_MESSAGE_ERROR) { ++ err = -message.u.response.error; ++ } else { ++ EPRINTF("got unexpected result '%s' from %d\n", ++ tapdisk_message_name(message.type), id); ++ err = -EINVAL; ++ } ++ ++ if (err) ++ EPRINTF("query commit job failed: %s\n", strerror(-err)); ++ ++ return err; ++} +diff --git a/control/tap-ctl.c b/control/tap-ctl.c +index 25d2248d..1c55248a 100644 +--- a/control/tap-ctl.c ++++ b/control/tap-ctl.c +@@ -989,6 +989,47 @@ usage: + return EINVAL; + } + ++static void ++tap_cli_query_commit_job_usage(FILE *stream) ++{ ++ fprintf(stream, "usage: query <-p pid> <-m minor>\n"); ++} ++ ++int ++tap_cli_query_commit_job(int argc, char **argv) ++{ ++ int c, pid, minor; ++ ++ pid = -1; ++ minor = -1; ++ ++ optind = 0; ++ while ((c = getopt(argc, argv, "p:m:h")) != -1) { ++ switch (c) { ++ case 'p': ++ pid = atoi(optarg); ++ break; ++ case 'm': ++ minor = atoi(optarg); ++ break; ++ case '?': ++ goto usage; ++ case 'h': ++ tap_cli_query_commit_job_usage(stdout); ++ return 0; ++ } ++ } ++ ++ if (pid == -1 || minor == -1) ++ goto usage; ++ ++ return tap_ctl_query_commit_job(pid, minor); ++ ++usage: ++ tap_cli_query_commit_job_usage(stderr); ++ return EINVAL; ++} ++ + struct command commands[] = { + { .name = "list", .func = tap_cli_list }, + { .name = "allocate", .func = tap_cli_allocate }, +@@ -1006,6 +1047,7 @@ struct command commands[] = { + { .name = "major", .func = tap_cli_major }, + { .name = "check", .func = tap_cli_check }, + { .name = "commit", .func = tap_cli_commit }, ++ { .name = "query", .func = tap_cli_query_commit_job }, + }; + + #define print_commands() \ +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index 7466f02c..ac21f4dc 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -1339,6 +1339,43 @@ out: + return err; + } + ++static int ++tapdisk_control_query_commit_job(struct tapdisk_ctl_conn *conn, ++ tapdisk_message_t *request, tapdisk_message_t * const response) ++{ ++ int err; ++ td_vbd_t *vbd; ++ td_query_t query; ++ ++ ASSERT(conn); ++ ASSERT(request); ++ ASSERT(response); ++ ++ INFO("query commit job %d\n", request->cookie); ++ ++ vbd = tapdisk_server_get_vbd(request->cookie); ++ if (!vbd) { ++ /* TODO log error */ ++ err = -ENODEV; ++ goto out; ++ } ++ ++ err = tapdisk_vbd_query_commit_job(vbd, &query); ++out: ++ response->cookie = request->cookie; ++ ++ if (!err) { ++ response->type = TAPDISK_MESSAGE_QUERY_COMMIT_JOB_RSP; ++ ++ if (query.status) ++ safe_strncpy(response->u.query.status, query.status, ++ sizeof(response->u.query.status)); ++ response->u.query.current_progress = query.current_progress; ++ response->u.query.total_progress = query.total_progress; ++ } ++ return err; ++} ++ + + struct tapdisk_control_info message_infos[] = { + [TAPDISK_MESSAGE_PID] = { +@@ -1400,6 +1437,10 @@ struct tapdisk_control_info message_infos[] = { + [TAPDISK_MESSAGE_COMMIT] = { + .handler = tapdisk_control_commit, + .flags = TAPDISK_MSG_VERBOSE, ++ }, ++ [TAPDISK_MESSAGE_QUERY_COMMIT_JOB] = { ++ .handler = tapdisk_control_query_commit_job, ++ .flags = TAPDISK_MSG_VERBOSE, + } + }; + +diff --git a/drivers/tapdisk-interface.c b/drivers/tapdisk-interface.c +index 5f562707..428ee98f 100644 +--- a/drivers/tapdisk-interface.c ++++ b/drivers/tapdisk-interface.c +@@ -295,6 +295,31 @@ td_commit(td_image_t *image, const char *name) + return driver->ops->td_commit(driver, name); + } + ++int ++td_query_commit_job(td_image_t *image, td_query_t *query) ++{ ++ td_driver_t *driver; ++ ++ if (!image) { ++ return -ENODEV; ++ } ++ ++ driver = image->driver; ++ if (!driver) { ++ return -ENODEV; ++ } ++ ++ if (!td_flag_test(driver->state, TD_DRIVER_OPEN)) { ++ return -EBADF; ++ } ++ ++ if (!driver->ops->td_query_commit_job) { ++ return -EOPNOTSUPP; ++ } ++ ++ return driver->ops->td_query_commit_job(driver, query); ++} ++ + void + td_forward_request(td_request_t treq) + { +diff --git a/drivers/tapdisk-interface.h b/drivers/tapdisk-interface.h +index 076528ea..b3ff6e13 100644 +--- a/drivers/tapdisk-interface.h ++++ b/drivers/tapdisk-interface.h +@@ -43,6 +43,7 @@ int td_close(td_image_t *); + int td_get_parent_id(td_image_t *, td_disk_id_t *); + int td_validate_parent(td_image_t *, td_image_t *); + int td_commit(td_image_t *, const char *); ++int td_query_commit_job(td_image_t *, td_query_t *); + + void td_queue_write(td_image_t *, td_request_t); + void td_queue_read(td_image_t *, td_request_t); +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 98a24db8..9b6d4d84 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -1149,6 +1149,22 @@ tapdisk_vbd_commit(td_vbd_t *vbd, const char *name) + return err; + } + ++int ++tapdisk_vbd_query_commit_job(td_vbd_t *vbd, td_query_t *query) ++{ ++ int err; ++ ++ if (log) { ++ INFO("query commit job.\n"); ++ } ++ ++ err = td_query_commit_job(tapdisk_vbd_first_image(vbd), query); ++ ++ INFO("query commit job (%d)\n", err); ++ ++ return err; ++} ++ + static int + tapdisk_vbd_request_ttl(td_vbd_request_t *vreq, + const struct timeval *now) +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index 909c2549..af333736 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -231,6 +231,7 @@ void tapdisk_vbd_kick(td_vbd_t *, bool); + void tapdisk_vbd_check_state(td_vbd_t *); + void tapdisk_vbd_free(td_vbd_t *); + int tapdisk_vbd_commit(td_vbd_t *, const char *); ++int tapdisk_vbd_query_commit_job(td_vbd_t *, td_query_t *); + + void tapdisk_vbd_complete_td_request(td_request_t, int); + int add_extent(tapdisk_extents_t *, td_request_t *); +diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h +index 482c323e..cf1adabc 100644 +--- a/drivers/tapdisk.h ++++ b/drivers/tapdisk.h +@@ -136,6 +136,7 @@ typedef struct td_image_handle td_image_t; + typedef struct td_sector_count td_sector_count_t; + typedef struct td_vbd_request td_vbd_request_t; + typedef struct td_vbd_handle td_vbd_t; ++typedef struct td_query td_query_t; + + /* + * Prototype of the callback to activate as requests complete. +@@ -211,6 +212,12 @@ struct td_vbd_encryption + uint8_t *encryption_key; + }; + ++struct td_query { ++ uint64_t current_progress; ++ uint64_t total_progress; ++ const char *status; ++}; ++ + /* + * Structure describing the interface to a virtual disk implementation. + * See note at the top of this file describing this interface. +@@ -229,6 +236,7 @@ struct tap_disk { + void (*td_debug) (td_driver_t *); + void (*td_stats) (td_driver_t *, td_stats_t *); + int (*td_commit) (td_driver_t *, const char *); ++ int (*td_query_commit_job) (td_driver_t *, td_query_t *); + + /** + * Callback to produce RRD output. +diff --git a/include/tap-ctl.h b/include/tap-ctl.h +index 03bb0434..8749b28c 100644 +--- a/include/tap-ctl.h ++++ b/include/tap-ctl.h +@@ -147,6 +147,7 @@ int tap_ctl_blk_major(void); + * Commit a VBD. + */ + int tap_ctl_commit(const int id, const int minor, const char *params); ++int tap_ctl_query_commit_job(const int id, const int minor); + + /** + * Instructs a tapdisk to connect to the shared ring. +diff --git a/include/tapdisk-message.h b/include/tapdisk-message.h +index 13295123..7645553a 100644 +--- a/include/tapdisk-message.h ++++ b/include/tapdisk-message.h +@@ -68,6 +68,7 @@ typedef struct tapdisk_message_response tapdisk_message_response_t; + typedef struct tapdisk_message_minors tapdisk_message_minors_t; + typedef struct tapdisk_message_list tapdisk_message_list_t; + typedef struct tapdisk_message_stat tapdisk_message_stat_t; ++typedef struct tapdisk_message_query tapdisk_message_query_t; + + struct tapdisk_message_params { + tapdisk_message_flag_t flags; +@@ -113,6 +114,12 @@ struct tapdisk_message_stat { + size_t length; + }; + ++struct tapdisk_message_query { ++ uint64_t current_progress; ++ uint64_t total_progress; ++ char status[TAPDISK_MESSAGE_STRING_LENGTH]; ++}; ++ + /** + * Tapdisk message containing all the necessary information required for the + * tapdisk to connect to a guest's blkfront. +@@ -200,7 +207,8 @@ struct tapdisk_message { + tapdisk_message_list_t list; + tapdisk_message_stat_t info; + tapdisk_message_blkif_t blkif; +- tapdisk_message_resume_t resume; ++ tapdisk_message_resume_t resume; ++ tapdisk_message_query_t query; + } u; + }; + +@@ -237,6 +245,8 @@ enum tapdisk_message_id { + TAPDISK_MESSAGE_EXIT, + TAPDISK_MESSAGE_COMMIT, + TAPDISK_MESSAGE_COMMIT_RSP, ++ TAPDISK_MESSAGE_QUERY_COMMIT_JOB, ++ TAPDISK_MESSAGE_QUERY_COMMIT_JOB_RSP, + TAPDISK_MESSAGE_MAX /* This value must be the last. */ + }; + +@@ -337,6 +347,12 @@ tapdisk_message_name(enum tapdisk_message_id id) + case TAPDISK_MESSAGE_COMMIT_RSP: + return "commit response"; + ++ case TAPDISK_MESSAGE_QUERY_COMMIT_JOB: ++ return "query commit job"; ++ ++ case TAPDISK_MESSAGE_QUERY_COMMIT_JOB_RSP: ++ return "query commit job response"; ++ + default: + return "unknown"; + } diff --git a/SOURCES/0041-qcow2-support-query-command.patch b/SOURCES/0041-qcow2-support-query-command.patch new file mode 100644 index 0000000..70e70c1 --- /dev/null +++ b/SOURCES/0041-qcow2-support-query-command.patch @@ -0,0 +1,158 @@ +From b2f29e6773ec00179fdfb5a2bb020b41fcdffeab Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 13 Feb 2025 08:56:22 +0100 +Subject: [PATCH] qcow2: support query command + +This command will print the status of the coalesce in background. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/block-qcow2.c | 101 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 101 insertions(+) + +diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c +index 4e242237..5c57233e 100644 +--- a/drivers/block-qcow2.c ++++ b/drivers/block-qcow2.c +@@ -61,6 +61,7 @@ + #include "sysemu/block-backend.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qapi-commands-block-core.h" ++#include "qapi/qapi-commands-job.h" + + #include "tapdisk.h" + #include "tapdisk-driver.h" +@@ -98,6 +99,7 @@ enum qcow2_ops { + QCOW2_OP_READ, + QCOW2_OP_WRITE, + QCOW2_OP_COMMIT, ++ QCOW2_OP_QUERY, + }; + + struct qcow2_state; +@@ -197,6 +199,7 @@ static void qcow2_complete(void *, int); + static inline void do_aio_read(struct qcow2_state *s, struct qcow2_request *req); + static inline void do_aio_write(struct qcow2_state *s, struct qcow2_request *req); + static inline void do_commit(struct qcow2_state *s, struct qcow2_request *req); ++static inline void do_query_commit_job(struct qcow2_state *s, struct qcow2_request *req); + + static int + qcow2_initialize(struct qcow2_state *s, Error **perr) +@@ -254,6 +257,9 @@ static void qcow2_handle_requests(struct qcow2_state *s) + case QCOW2_OP_COMMIT: + do_commit(s, req); + break; ++ case QCOW2_OP_QUERY: ++ do_query_commit_job(s, req); ++ break; + } + pthread_mutex_lock(&s->lock); + } +@@ -953,6 +959,100 @@ signal_commit: + pthread_mutex_unlock(&s->commit_lock); + } + ++int ++qcow2_query_commit_job(td_driver_t *driver, td_query_t *query) ++{ ++ struct qcow2_state *s = (struct qcow2_state *)driver->data; ++ struct qcow2_request *req; ++ int err; ++ ++ DBG(TLOG_DBG, "Qcow2: query commit job.\n"); ++ ++ req = alloc_qcow2_request(s); ++ if (!req) ++ return -EBUSY; ++ ++ req->op = QCOW2_OP_QUERY; ++ ++ pthread_mutex_lock(&s->lock); ++ QSIMPLEQ_INSERT_TAIL(&s->inflight, req, list); ++ pthread_mutex_unlock(&s->lock); ++ ++ pthread_mutex_lock(&s->commit_lock); ++ qemu_bh_schedule(s->bh); ++ ++ pthread_cond_wait(&s->commit_cond, &s->commit_lock); ++ ++ if (query) { ++ query->status = JobStatus_str(s->job_info.status); ++ query->current_progress = s->job_info.current_progress; ++ query->total_progress = s->job_info.total_progress; ++ } ++ ++ err = req->error; ++ pthread_mutex_unlock(&s->commit_lock); ++ ++ DBG(TLOG_WARN, "Qcow2: query commit job done (%d).\n", err); ++ ++ free_qcow2_request(s, req); ++ ++ return err; ++} ++ ++static inline void ++do_query_commit_job(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ Error *local_err = NULL; ++ int err = 0; ++ BlockJob *bjob; ++ JobStatus status = JOB_STATUS_UNDEFINED; ++ uint64_t current = 0, total = 0; ++ ++ job_lock(); ++ bjob = block_job_get_locked(COMMIT_JOB_ID); ++ if (!bjob) { ++ job_unlock(); ++ DPRINTF("Qcow2: no job running.\n"); ++ goto signal; ++ } ++ ++ status = bjob->job.status; ++ current = bjob->job.progress.current; ++ total = bjob->job.progress.total; ++ ++ if (status == JOB_STATUS_READY) { ++ Job *job = &bjob->job; ++ job_complete_locked(job, &local_err); ++ if (local_err) { ++ DPRINTF("Qcow2: job complete error: %s\n", error_get_pretty(local_err)); ++ error_free(local_err); ++ err = -EINVAL; ++ } ++ } ++ if (status == JOB_STATUS_CONCLUDED) { ++ Job *job = &bjob->job; ++ job_dismiss_locked(&job, &local_err); ++ if (local_err) { ++ DPRINTF("Qcow2: job dismiss error: %s\n", error_get_pretty(local_err)); ++ error_free(local_err); ++ err = -EINVAL; ++ } ++ } ++ job_unlock(); ++ ++ DPRINTF("Qcow2: commit job '%s'.\n", JobStatus_str(status)); ++ ++signal: ++ pthread_mutex_lock(&s->commit_lock); ++ s->job_info.status = status; ++ s->job_info.current_progress = current; ++ s->job_info.total_progress = total; ++ ++ req->error = err; ++ pthread_cond_signal(&s->commit_cond); ++ pthread_mutex_unlock(&s->commit_lock); ++} ++ + void + qcow2_debug(td_driver_t *driver) + { +@@ -984,5 +1084,6 @@ struct tap_disk tapdisk_qcow = { + .td_get_parent_id = qcow2_get_parent_id, + .td_validate_parent = qcow2_validate_parent, + .td_commit = qcow2_commit, ++ .td_query_commit_job = qcow2_query_commit_job, + .td_debug = qcow2_debug, + }; diff --git a/SOURCES/0042-tapdisk-support-new-cancel-command.patch b/SOURCES/0042-tapdisk-support-new-cancel-command.patch new file mode 100644 index 0000000..51f73bb --- /dev/null +++ b/SOURCES/0042-tapdisk-support-new-cancel-command.patch @@ -0,0 +1,312 @@ +From ae49a1d06ca20863062f21e3dd1cf2729e4a2432 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 5 Mar 2025 18:09:16 +0100 +Subject: [PATCH] tapdisk: support new cancel command + +This command will cancel the coalesce job in background. +$ tap-ctl cancel -m 0 -p 12345 + +Signed-off-by: Anthoine Bourgeois +--- + control/tap-ctl-commit.c | 30 +++++++++++++++++++++++ + control/tap-ctl.c | 47 +++++++++++++++++++++++++++++++++++++ + drivers/tapdisk-control.c | 32 +++++++++++++++++++++++++ + drivers/tapdisk-interface.c | 25 ++++++++++++++++++++ + drivers/tapdisk-interface.h | 1 + + drivers/tapdisk-vbd.c | 16 +++++++++++++ + drivers/tapdisk-vbd.h | 1 + + drivers/tapdisk.h | 1 + + include/tap-ctl.h | 1 + + include/tapdisk-message.h | 8 +++++++ + 10 files changed, 162 insertions(+) + +diff --git a/control/tap-ctl-commit.c b/control/tap-ctl-commit.c +index 04985f69..56549deb 100644 +--- a/control/tap-ctl-commit.c ++++ b/control/tap-ctl-commit.c +@@ -105,3 +105,33 @@ tap_ctl_query_commit_job(const int id, const int minor) + + return err; + } ++ ++int ++tap_ctl_cancel_commit_job(const int id, const int minor, bool wait) ++{ ++ int err; ++ tapdisk_message_t message; ++ ++ memset(&message, 0, sizeof(message)); ++ message.type = TAPDISK_MESSAGE_CANCEL_COMMIT_JOB; ++ message.cookie = minor; ++ message.u.params.flags = wait; ++ ++ err = tap_ctl_connect_send_and_receive(id, &message, NULL); ++ if (err) ++ return err; ++ ++ if (message.type == TAPDISK_MESSAGE_CANCEL_COMMIT_JOB_RSP ++ || message.type == TAPDISK_MESSAGE_ERROR) { ++ err = -message.u.response.error; ++ } else { ++ EPRINTF("got unexpected result '%s' from %d\n", ++ tapdisk_message_name(message.type), id); ++ err = -EINVAL; ++ } ++ ++ if (err) ++ EPRINTF("cancel commit job failed: %s\n", strerror(-err)); ++ ++ return err; ++} +diff --git a/control/tap-ctl.c b/control/tap-ctl.c +index 1c55248a..c5597c94 100644 +--- a/control/tap-ctl.c ++++ b/control/tap-ctl.c +@@ -1030,6 +1030,52 @@ usage: + return EINVAL; + } + ++static void ++tap_cli_cancel_commit_job_usage(FILE *stream) ++{ ++ fprintf(stream, "usage: cancel <-p pid> <-m minor> [-w]\n"); ++} ++ ++int ++tap_cli_cancel_commit_job(int argc, char **argv) ++{ ++ int c, pid, minor; ++ bool wait; ++ ++ pid = -1; ++ minor = -1; ++ wait = false; ++ ++ optind = 0; ++ while ((c = getopt(argc, argv, "p:m:wh")) != -1) { ++ switch (c) { ++ case 'p': ++ pid = atoi(optarg); ++ break; ++ case 'm': ++ minor = atoi(optarg); ++ break; ++ case 'w': ++ wait = true; ++ break; ++ case '?': ++ goto usage; ++ case 'h': ++ tap_cli_cancel_commit_job_usage(stdout); ++ return 0; ++ } ++ } ++ ++ if (pid == -1 || minor == -1) ++ goto usage; ++ ++ return tap_ctl_cancel_commit_job(pid, minor, wait); ++ ++usage: ++ tap_cli_cancel_commit_job_usage(stderr); ++ return EINVAL; ++} ++ + struct command commands[] = { + { .name = "list", .func = tap_cli_list }, + { .name = "allocate", .func = tap_cli_allocate }, +@@ -1048,6 +1094,7 @@ struct command commands[] = { + { .name = "check", .func = tap_cli_check }, + { .name = "commit", .func = tap_cli_commit }, + { .name = "query", .func = tap_cli_query_commit_job }, ++ { .name = "cancel", .func = tap_cli_cancel_commit_job }, + }; + + #define print_commands() \ +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index ac21f4dc..4b7aab20 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -1376,6 +1376,34 @@ out: + return err; + } + ++static int ++tapdisk_control_cancel_commit_job(struct tapdisk_ctl_conn *conn, ++ tapdisk_message_t *request, tapdisk_message_t * const response) ++{ ++ int err; ++ td_vbd_t *vbd; ++ ++ ASSERT(conn); ++ ASSERT(request); ++ ASSERT(response); ++ ++ INFO("cancel commit job %d\n", request->cookie); ++ ++ vbd = tapdisk_server_get_vbd(request->cookie); ++ if (!vbd) { ++ err = -ENODEV; ++ ERR(err, "cancel commit job '%d' do not find an associated vbd, abort.\n", request->cookie); ++ goto out; ++ } ++ ++ err = tapdisk_vbd_cancel_commit_job(vbd, request->u.params.flags); ++out: ++ response->cookie = request->cookie; ++ if (!err) ++ response->type = TAPDISK_MESSAGE_CANCEL_COMMIT_JOB_RSP; ++ return err; ++} ++ + + struct tapdisk_control_info message_infos[] = { + [TAPDISK_MESSAGE_PID] = { +@@ -1441,6 +1469,10 @@ struct tapdisk_control_info message_infos[] = { + [TAPDISK_MESSAGE_QUERY_COMMIT_JOB] = { + .handler = tapdisk_control_query_commit_job, + .flags = TAPDISK_MSG_VERBOSE, ++ }, ++ [TAPDISK_MESSAGE_CANCEL_COMMIT_JOB] = { ++ .handler = tapdisk_control_cancel_commit_job, ++ .flags = TAPDISK_MSG_VERBOSE, + } + }; + +diff --git a/drivers/tapdisk-interface.c b/drivers/tapdisk-interface.c +index 428ee98f..4f3c79d3 100644 +--- a/drivers/tapdisk-interface.c ++++ b/drivers/tapdisk-interface.c +@@ -320,6 +320,31 @@ td_query_commit_job(td_image_t *image, td_query_t *query) + return driver->ops->td_query_commit_job(driver, query); + } + ++int ++td_cancel_commit_job(td_image_t *image, bool wait) ++{ ++ td_driver_t *driver; ++ ++ if (!image) { ++ return -ENODEV; ++ } ++ ++ driver = image->driver; ++ if (!driver) { ++ return -ENODEV; ++ } ++ ++ if (!td_flag_test(driver->state, TD_DRIVER_OPEN)) { ++ return -EBADF; ++ } ++ ++ if (!driver->ops->td_cancel_commit_job) { ++ return -EOPNOTSUPP; ++ } ++ ++ return driver->ops->td_cancel_commit_job(driver, wait); ++} ++ + void + td_forward_request(td_request_t treq) + { +diff --git a/drivers/tapdisk-interface.h b/drivers/tapdisk-interface.h +index b3ff6e13..d4ddf4d6 100644 +--- a/drivers/tapdisk-interface.h ++++ b/drivers/tapdisk-interface.h +@@ -44,6 +44,7 @@ int td_get_parent_id(td_image_t *, td_disk_id_t *); + int td_validate_parent(td_image_t *, td_image_t *); + int td_commit(td_image_t *, const char *); + int td_query_commit_job(td_image_t *, td_query_t *); ++int td_cancel_commit_job(td_image_t *, bool); + + void td_queue_write(td_image_t *, td_request_t); + void td_queue_read(td_image_t *, td_request_t); +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 9b6d4d84..345fba36 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -1165,6 +1165,22 @@ tapdisk_vbd_query_commit_job(td_vbd_t *vbd, td_query_t *query) + return err; + } + ++int ++tapdisk_vbd_cancel_commit_job(td_vbd_t *vbd, bool wait) ++{ ++ int err; ++ ++ if (log) { ++ INFO("cancel commit job.\n"); ++ } ++ ++ err = td_cancel_commit_job(tapdisk_vbd_first_image(vbd), wait); ++ ++ INFO("cancel commit job (%d)\n", err); ++ ++ return err; ++} ++ + static int + tapdisk_vbd_request_ttl(td_vbd_request_t *vreq, + const struct timeval *now) +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index af333736..bfac0c66 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -232,6 +232,7 @@ void tapdisk_vbd_check_state(td_vbd_t *); + void tapdisk_vbd_free(td_vbd_t *); + int tapdisk_vbd_commit(td_vbd_t *, const char *); + int tapdisk_vbd_query_commit_job(td_vbd_t *, td_query_t *); ++int tapdisk_vbd_cancel_commit_job(td_vbd_t *, bool); + + void tapdisk_vbd_complete_td_request(td_request_t, int); + int add_extent(tapdisk_extents_t *, td_request_t *); +diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h +index cf1adabc..2f74c957 100644 +--- a/drivers/tapdisk.h ++++ b/drivers/tapdisk.h +@@ -237,6 +237,7 @@ struct tap_disk { + void (*td_stats) (td_driver_t *, td_stats_t *); + int (*td_commit) (td_driver_t *, const char *); + int (*td_query_commit_job) (td_driver_t *, td_query_t *); ++ int (*td_cancel_commit_job) (td_driver_t *, bool); + + /** + * Callback to produce RRD output. +diff --git a/include/tap-ctl.h b/include/tap-ctl.h +index 8749b28c..f093912d 100644 +--- a/include/tap-ctl.h ++++ b/include/tap-ctl.h +@@ -148,6 +148,7 @@ int tap_ctl_blk_major(void); + */ + int tap_ctl_commit(const int id, const int minor, const char *params); + int tap_ctl_query_commit_job(const int id, const int minor); ++int tap_ctl_cancel_commit_job(const int id, const int minor, bool wait); + + /** + * Instructs a tapdisk to connect to the shared ring. +diff --git a/include/tapdisk-message.h b/include/tapdisk-message.h +index 7645553a..22756eec 100644 +--- a/include/tapdisk-message.h ++++ b/include/tapdisk-message.h +@@ -247,6 +247,8 @@ enum tapdisk_message_id { + TAPDISK_MESSAGE_COMMIT_RSP, + TAPDISK_MESSAGE_QUERY_COMMIT_JOB, + TAPDISK_MESSAGE_QUERY_COMMIT_JOB_RSP, ++ TAPDISK_MESSAGE_CANCEL_COMMIT_JOB, ++ TAPDISK_MESSAGE_CANCEL_COMMIT_JOB_RSP, + TAPDISK_MESSAGE_MAX /* This value must be the last. */ + }; + +@@ -353,6 +355,12 @@ tapdisk_message_name(enum tapdisk_message_id id) + case TAPDISK_MESSAGE_QUERY_COMMIT_JOB_RSP: + return "query commit job response"; + ++ case TAPDISK_MESSAGE_CANCEL_COMMIT_JOB: ++ return "cancel commit job"; ++ ++ case TAPDISK_MESSAGE_CANCEL_COMMIT_JOB_RSP: ++ return "cancel commit job response"; ++ + default: + return "unknown"; + } diff --git a/SOURCES/0043-qcow2-support-cancel-command.patch b/SOURCES/0043-qcow2-support-cancel-command.patch new file mode 100644 index 0000000..58ad396 --- /dev/null +++ b/SOURCES/0043-qcow2-support-cancel-command.patch @@ -0,0 +1,178 @@ +From 0a8a3097f67baa716a76ff773b3988ebf6d62586 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 5 Mar 2025 18:22:56 +0100 +Subject: [PATCH] qcow2: support cancel command + +This command will cancel the coalesce job in background. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/block-qcow2.c | 97 ++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 96 insertions(+), 1 deletion(-) + +diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c +index 5c57233e..1dca89dd 100644 +--- a/drivers/block-qcow2.c ++++ b/drivers/block-qcow2.c +@@ -100,6 +100,7 @@ enum qcow2_ops { + QCOW2_OP_WRITE, + QCOW2_OP_COMMIT, + QCOW2_OP_QUERY, ++ QCOW2_OP_CANCEL_COMMIT, + }; + + struct qcow2_state; +@@ -115,6 +116,8 @@ struct qcow2_request { + td_request_t treq; + /* OP_COMMIT */ + char * top; ++ /* OP_CANCEL_COMMIT */ ++ bool sync; + }; + struct qcow2_state *state; + BlockAIOCB *aiocb; +@@ -200,6 +203,8 @@ static inline void do_aio_read(struct qcow2_state *s, struct qcow2_request *req) + static inline void do_aio_write(struct qcow2_state *s, struct qcow2_request *req); + static inline void do_commit(struct qcow2_state *s, struct qcow2_request *req); + static inline void do_query_commit_job(struct qcow2_state *s, struct qcow2_request *req); ++static inline void do_cancel_commit_job(struct qcow2_state *s, struct qcow2_request *req); ++static int qcow2_cancel_commit_job(td_driver_t *driver, bool wait); + + static int + qcow2_initialize(struct qcow2_state *s, Error **perr) +@@ -260,6 +265,9 @@ static void qcow2_handle_requests(struct qcow2_state *s) + case QCOW2_OP_QUERY: + do_query_commit_job(s, req); + break; ++ case QCOW2_OP_CANCEL_COMMIT: ++ do_cancel_commit_job(s, req); ++ break; + } + pthread_mutex_lock(&s->lock); + } +@@ -431,6 +439,18 @@ qcow2_open(void *opaque) + } + pthread_mutex_unlock(&s->lock); + ++ job_lock(); ++ BlockJob *bjob = block_job_get_locked(COMMIT_JOB_ID); ++ if (bjob) { ++ Job *job = &bjob->job; ++ job_dismiss_locked(&job, &local_err); ++ if (local_err) { ++ DPRINTF("Qcow2: job dismiss error: %s\n", error_get_pretty(local_err)); ++ error_free(local_err); ++ } ++ } ++ job_unlock(); ++ + blk_set_aio_context(conf->blk, qemu_get_aio_context(), &error_abort); + + blk_drain_all(); +@@ -516,6 +536,8 @@ _qcow2_close(td_driver_t *driver) + int err; + struct qcow2_state *s = (struct qcow2_state *)driver->data; + ++ qcow2_cancel_commit_job(driver, true); ++ + DBG(TLOG_WARN, "qcow2_close\n"); + + pthread_mutex_lock(&s->lock); +@@ -1053,9 +1075,82 @@ signal: + pthread_mutex_unlock(&s->commit_lock); + } + ++ ++int ++qcow2_cancel_commit_job(td_driver_t *driver, bool wait) ++{ ++ struct qcow2_state *s = (struct qcow2_state *)driver->data; ++ struct qcow2_request *req; ++ int err; ++ ++ DBG(TLOG_DBG, "Qcow2: cancel commit.\n"); ++ ++ req = alloc_qcow2_request(s); ++ if (!req) ++ return -EBUSY; ++ ++ req->op = QCOW2_OP_CANCEL_COMMIT; ++ req->sync = wait; ++ ++ pthread_mutex_lock(&s->lock); ++ QSIMPLEQ_INSERT_TAIL(&s->inflight, req, list); ++ pthread_mutex_unlock(&s->lock); ++ ++ pthread_mutex_lock(&s->commit_lock); ++ qemu_bh_schedule(s->bh); ++ ++ pthread_cond_wait(&s->commit_cond, &s->commit_lock); ++ err = req->error; ++ pthread_mutex_unlock(&s->commit_lock); ++ ++ DBG(TLOG_WARN, "Qcow2: cancel commit done (%d).\n", err); ++ ++ free_qcow2_request(s, req); ++ ++ /* Already canceled, so this is considered a success */ ++ if (err == -ECANCELED) ++ err = 0; ++ ++ return err; ++} ++ ++static inline void ++do_cancel_commit_job(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ int err = 0; ++ BlockJob *bjob; ++ ++ job_lock(); ++ bjob = block_job_get_locked(COMMIT_JOB_ID); ++ if (!bjob) { ++ job_unlock(); ++ DPRINTF("Qcow2: no job to cancel.\n"); ++ goto signal; ++ } ++ ++ if (bjob->job.status == JOB_STATUS_RUNNING || ++ bjob->job.status == JOB_STATUS_READY) { ++ if (req->sync == false) { ++ job_cancel_locked(&bjob->job, false); ++ } else { ++ err = job_cancel_sync_locked(&bjob->job, false); ++ } ++ } ++ job_unlock(); ++ ++ DBG(TLOG_WARN, "Qcow2: cancel %s (%d).\n", req->sync ? "sync" : "async", err); ++ ++signal: ++ pthread_mutex_lock(&s->commit_lock); ++ req->error = err; ++ pthread_cond_signal(&s->commit_cond); ++ pthread_mutex_unlock(&s->commit_lock); ++} ++ + void + qcow2_debug(td_driver_t *driver) + { ++#if DEBUGGING != 0 + struct qcow2_state *s = (struct qcow2_state *)driver->data; + + DBG(TLOG_WARN, "Qcow2: %s: queued %lu, completed %lu, returned %lu, " +@@ -1067,7 +1162,6 @@ qcow2_debug(td_driver_t *driver) + s->writes, (s->writes ? ((float)s->write_size / s->writes) : 0.0), + s->schedule, s->kick); + +-#if DEBUGGING != 0 + print_latencies(s); + #endif + } +@@ -1085,5 +1179,6 @@ struct tap_disk tapdisk_qcow = { + .td_validate_parent = qcow2_validate_parent, + .td_commit = qcow2_commit, + .td_query_commit_job = qcow2_query_commit_job, ++ .td_cancel_commit_job = qcow2_cancel_commit_job, + .td_debug = qcow2_debug, + }; diff --git a/SOURCES/0044-libqcow2-fix-abort-commit-without-crash.patch b/SOURCES/0044-libqcow2-fix-abort-commit-without-crash.patch new file mode 100644 index 0000000..17a519a --- /dev/null +++ b/SOURCES/0044-libqcow2-fix-abort-commit-without-crash.patch @@ -0,0 +1,25 @@ +From 09a1a2609d0e4388bdd2e71948401459d502a8b7 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 12 Mar 2025 10:28:42 +0100 +Subject: [PATCH] libqcow2: fix abort commit without crash + +Signed-off-by: Anthoine Bourgeois +--- + qcow2/lib/block/commit.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/qcow2/lib/block/commit.c b/qcow2/lib/block/commit.c +index 7c3fdcb0..e7a201f8 100644 +--- a/qcow2/lib/block/commit.c ++++ b/qcow2/lib/block/commit.c +@@ -98,6 +98,10 @@ static void commit_abort(Job *job) + * after the failed/cancelled commit job is gone? If we already wrote + * something to base, the intermediate images aren't valid any more. */ + bdrv_graph_rdlock_main_loop(); ++ if (!s->commit_top_bs->backing) { ++ bdrv_graph_rdunlock_main_loop(); ++ return; ++ } + commit_top_backing_bs = s->commit_top_bs->backing->bs; + bdrv_graph_rdunlock_main_loop(); + diff --git a/SOURCES/0045-feat-pass-error-details-up-to-tap-ctl.patch b/SOURCES/0045-feat-pass-error-details-up-to-tap-ctl.patch new file mode 100644 index 0000000..3f462d4 --- /dev/null +++ b/SOURCES/0045-feat-pass-error-details-up-to-tap-ctl.patch @@ -0,0 +1,1943 @@ +From d3bc87696afd92396a39de63a7302d2dcd9fe08f Mon Sep 17 00:00:00 2001 +From: Mathieu Labourier +Date: Thu, 30 Apr 2026 11:20:20 +0200 +Subject: [PATCH] feat: pass error details up to tap-ctl + +Passing an error structure all the way through the +functions and calls invoked by tap-ctl allows to have detailed errors + +This also paves the way for structured logging +when calling tap-ctl using other program or scripts (e.g. SMAPI) + +(cherry picked from commit b6aba882df7ae943c874e5ca850b36186abc177a) + +Signed-off-by: Mathieu Labourier +--- + control/tap-ctl-unpause.c | 14 ++- + control/tap-ctl.c | 255 ++++++++++++++++++++++++-------------- + drivers/tapdisk-control.c | 116 ++++++++++------- + drivers/tapdisk-image.c | 73 +++++++---- + drivers/tapdisk-image.h | 3 +- + drivers/tapdisk-stream.c | 3 +- + drivers/tapdisk-vbd.c | 26 ++-- + drivers/tapdisk-vbd.h | 4 +- + include/Makefile.am | 1 + + include/tap-ctl.h | 3 +- + include/tapdisk-err.h | 182 +++++++++++++++++++++++++++ + 11 files changed, 501 insertions(+), 179 deletions(-) + create mode 100644 include/tapdisk-err.h + +diff --git a/control/tap-ctl-unpause.c b/control/tap-ctl-unpause.c +index 277ef88..a0d5868 100644 +--- a/control/tap-ctl-unpause.c ++++ b/control/tap-ctl-unpause.c +@@ -44,7 +44,7 @@ + + int + tap_ctl_unpause(const int id, const int minor, const char *params, int flags, +- char *secondary, const char *logpath) ++ char *secondary, const char *logpath, td_err *error) + { + int err; + tapdisk_message_t message; +@@ -54,6 +54,8 @@ tap_ctl_unpause(const int id, const int minor, const char *params, int flags, + message.cookie = minor; + message.u.params.flags = flags; + ++ td_err_init_errno(error); ++ + if (params) + safe_strncpy(message.u.params.path, params, + sizeof(message.u.params.path)); +@@ -63,7 +65,7 @@ tap_ctl_unpause(const int id, const int minor, const char *params, int flags, + secondary); + if (err >= sizeof(message.u.params.secondary)) { + EPRINTF("secondary image name too long\n"); +- return -ENAMETOOLONG; ++ return td_err_set_errno(error, -ENAMETOOLONG); + } + } + if (logpath) { +@@ -73,8 +75,10 @@ tap_ctl_unpause(const int id, const int minor, const char *params, int flags, + err = tap_ctl_connect_send_and_receive(id, &message, NULL); + } + ++ if (message.u.response.message[0]) ++ td_err_set_reason(error, message.u.response.message); + if (err) +- return err; ++ return td_err_set_errno(error, err); + + if (message.type == TAPDISK_MESSAGE_RESUME_RSP + || message.type == TAPDISK_MESSAGE_ERROR) +@@ -87,6 +91,8 @@ tap_ctl_unpause(const int id, const int minor, const char *params, int flags, + + if (err) + EPRINTF("unpause failed: %s\n", strerror(-err)); ++ else ++ return td_err_set_success(error); + +- return err; ++ return td_err_set_errno(error, err); + } +diff --git a/control/tap-ctl.c b/control/tap-ctl.c +index c5597c9..79bcbc6 100644 +--- a/control/tap-ctl.c ++++ b/control/tap-ctl.c +@@ -44,13 +44,27 @@ + + #define MAX_AES_XTS_PLAIN_KEYSIZE 1024 + +-typedef int (*tap_ctl_func_t) (int, char **); ++typedef int (*tap_ctl_func_t) (int, char **, td_err *); + + struct command { + char *name; + tap_ctl_func_t func; + }; + ++static void ++td_err_print_error(const td_err *error) ++{ ++ if (!error) return; ++ ++ fprintf(stderr, "%s [%d - %s]: %s. Reason: %s\n", ++ error->code == TD_SUCCESS ? "SUCCESS" : "ERROR", ++ error->code == TD_USE_ERRNO ? error->saved_errno : (int)error->code, ++ error->cmd[0] ? error->cmd : "", ++ error->code == TD_USE_ERRNO ? strerror(abs(error->saved_errno)) : td_err_code_to_string(error->code), ++ error->reason[0] ? error->reason : "No specific reason provided" ++ ); ++} ++ + static void + tap_cli_list_usage(FILE *stream) + { +@@ -111,7 +125,7 @@ tap_cli_list_dict(tap_list_t *entry) + } + + int +-tap_cli_list(int argc, char **argv) ++tap_cli_list(int argc, char **argv, td_err *error) + { + struct list_head list = LIST_HEAD_INIT(list); + int c, minor, tty, err; +@@ -123,6 +137,7 @@ tap_cli_list(int argc, char **argv) + minor = -1; + type = NULL; + file = NULL; ++ td_err_init_errno(error); + + while ((c = getopt(argc, argv, "m:p:t:f:h")) != -1) { + switch (c) { +@@ -142,7 +157,7 @@ tap_cli_list(int argc, char **argv) + goto usage; + case 'h': + tap_cli_list_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + +@@ -151,7 +166,7 @@ tap_cli_list(int argc, char **argv) + else + err = tap_ctl_list(&list); + if (err) +- return -err; ++ return td_err_set_errno(error, -err); + + tty = isatty(STDOUT_FILENO); + +@@ -186,11 +201,11 @@ tap_cli_list(int argc, char **argv) + + tap_ctl_list_free(&list); + +- return 0; ++ return td_err_set_success(error); + + usage: + tap_cli_list_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -200,13 +215,14 @@ tap_cli_allocate_usage(FILE *stream) + } + + static int +-tap_cli_allocate(int argc, char **argv) ++tap_cli_allocate(int argc, char **argv, td_err *error) + { + char *devname; + int c, minor, err; + char d_flag = 0; + + devname = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "d:h")) != -1) { +@@ -219,22 +235,24 @@ tap_cli_allocate(int argc, char **argv) + goto usage; + case 'h': + tap_cli_allocate_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + err = tap_ctl_allocate(&minor, &devname); +- if (!err) ++ if (!err) { ++ td_err_set_success(error); + printf("%s\n", devname); ++ } + + if (!d_flag) + free(devname); + +- return err; ++ return td_err_set_errno(error, err); + + usage: + tap_cli_allocate_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -244,11 +262,12 @@ tap_cli_free_usage(FILE *stream) + } + + static int +-tap_cli_free(int argc, char **argv) ++tap_cli_free(int argc, char **argv, td_err *error) + { +- int c, minor; ++ int c, minor, err; + + minor = -1; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "m:h")) != -1) { +@@ -260,18 +279,21 @@ tap_cli_free(int argc, char **argv) + goto usage; + case 'h': + tap_cli_free_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + if (minor == -1) + goto usage; + +- return tap_ctl_free(minor); ++ err = tap_ctl_free(minor); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_free_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -287,9 +309,9 @@ tap_cli_create_usage(FILE *stream) + } + + static int +-tap_cli_create(int argc, char **argv) ++tap_cli_create(int argc, char **argv, td_err *error) + { +- int c, err, flags, prt_minor, timeout; ++ int c, flags, prt_minor, timeout, err; + char *args, *devname, *secondary; + char d_flag = 0; + char *logpath = NULL; +@@ -300,6 +322,7 @@ tap_cli_create(int argc, char **argv) + prt_minor = -1; + flags = 0; + timeout = 0; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "a:RDd:e:r2:st:C:h")) != -1) { +@@ -342,7 +365,7 @@ tap_cli_create(int argc, char **argv) + goto usage; + case 'h': + tap_cli_create_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + +@@ -351,17 +374,19 @@ tap_cli_create(int argc, char **argv) + + err = tap_ctl_create(args, &devname, flags, prt_minor, secondary, + timeout, logpath); +- if (!err) ++ if (!err) { ++ td_err_set_success(error); + printf("%s\n", devname); ++ } + + if (!d_flag) + free(devname); + +- return err; ++ return td_err_set_errno(error, err); + + usage: + tap_cli_create_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -382,14 +407,15 @@ tap_cli_timeout(const char *optarg) + } + + static int +-tap_cli_destroy(int argc, char **argv) ++tap_cli_destroy(int argc, char **argv, td_err *error) + { +- int c, pid, minor; ++ int c, pid, minor, err; + struct timeval *timeout; + + pid = -1; + minor = -1; + timeout = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:t:h")) != -1) { +@@ -409,18 +435,21 @@ tap_cli_destroy(int argc, char **argv) + goto usage; + case 'h': + tap_cli_destroy_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_destroy(pid, minor, 0, timeout); ++ err = tap_ctl_destroy(pid, minor, 0, timeout); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_destroy_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -430,10 +459,11 @@ tap_cli_spawn_usage(FILE *stream) + } + + static int +-tap_cli_spawn(int argc, char **argv) ++tap_cli_spawn(int argc, char **argv, td_err *error) + { + int c, tty; + pid_t pid; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "h")) != -1) { +@@ -442,13 +472,13 @@ tap_cli_spawn(int argc, char **argv) + goto usage; + case 'h': + tap_cli_spawn_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + pid = tap_ctl_spawn(); + if (pid < 0) +- return pid; ++ return td_err_set_errno(error, pid); + + tty = isatty(STDOUT_FILENO); + if (tty) +@@ -456,11 +486,11 @@ tap_cli_spawn(int argc, char **argv) + else + printf("%d\n", pid); + +- return 0; ++ return td_err_set_success(error); + + usage: + tap_cli_spawn_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -470,12 +500,13 @@ tap_cli_attach_usage(FILE *stream) + } + + static int +-tap_cli_attach(int argc, char **argv) ++tap_cli_attach(int argc, char **argv, td_err *error) + { +- int c, pid, minor; ++ int c, pid, minor, err; + + pid = -1; + minor = -1; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:h")) != -1) { +@@ -490,18 +521,21 @@ tap_cli_attach(int argc, char **argv) + goto usage; + case 'h': + tap_cli_attach_usage(stderr); +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_attach(pid, minor); ++ err = tap_ctl_attach(pid, minor); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_attach_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -511,12 +545,13 @@ tap_cli_detach_usage(FILE *stream) + } + + static int +-tap_cli_detach(int argc, char **argv) ++tap_cli_detach(int argc, char **argv, td_err *error) + { +- int c, pid, minor; ++ int c, pid, minor, err; + + pid = -1; + minor = -1; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:h")) != -1) { +@@ -531,18 +566,21 @@ tap_cli_detach(int argc, char **argv) + goto usage; + case 'h': + tap_cli_detach_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_detach(pid, minor); ++ err = tap_ctl_detach(pid, minor); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_detach_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -552,15 +590,16 @@ tap_cli_close_usage(FILE *stream) + } + + static int +-tap_cli_close(int argc, char **argv) ++tap_cli_close(int argc, char **argv, td_err *error) + { +- int c, pid, minor, force; ++ int c, pid, minor, force, err; + struct timeval *timeout; + + pid = -1; + minor = -1; + force = 0; + timeout = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:ft:h")) != -1) { +@@ -583,18 +622,21 @@ tap_cli_close(int argc, char **argv) + goto usage; + case 'h': + tap_cli_close_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_close(pid, minor, force, timeout); ++ err = tap_ctl_close(pid, minor, force, timeout); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_close_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -604,14 +646,15 @@ tap_cli_pause_usage(FILE *stream) + } + + static int +-tap_cli_pause(int argc, char **argv) ++tap_cli_pause(int argc, char **argv, td_err *error) + { +- int c, pid, minor; ++ int c, pid, minor, err; + struct timeval *timeout; + + pid = -1; + minor = -1; + timeout = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:t:h")) != -1) { +@@ -630,18 +673,21 @@ tap_cli_pause(int argc, char **argv) + goto usage; + case 'h': + tap_cli_pause_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_pause(pid, minor, timeout); ++ err = tap_ctl_pause(pid, minor, timeout); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_pause_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -653,11 +699,11 @@ tap_cli_unpause_usage(FILE *stream) + } + + int +-tap_cli_unpause(int argc, char **argv) ++tap_cli_unpause(int argc, char **argv, td_err *error) + { + const char *args, *logpath; + char *secondary; +- int c, pid, minor, flags; ++ int c, pid, minor, flags, err; + + pid = -1; + minor = -1; +@@ -665,6 +711,7 @@ tap_cli_unpause(int argc, char **argv) + secondary = NULL; + flags = 0; + logpath = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:a:2:c:h")) != -1) { +@@ -690,18 +737,21 @@ tap_cli_unpause(int argc, char **argv) + goto usage; + case 'h': + tap_cli_unpause_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_unpause(pid, minor, args, flags, secondary, logpath); ++ err = tap_ctl_unpause(pid, minor, args, flags, secondary, logpath, error); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_unpause_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -711,11 +761,12 @@ tap_cli_major_usage(FILE *stream) + } + + static int +-tap_cli_major(int argc, char **argv) ++tap_cli_major(int argc, char **argv, td_err *error) + { + int c, chr, major; + + chr = 0; ++ td_err_init_errno(error); + + while ((c = getopt(argc, argv, "bch")) != -1) { + switch (c) { +@@ -729,7 +780,7 @@ tap_cli_major(int argc, char **argv) + goto usage; + case 'h': + tap_cli_major_usage(stdout); +- return 0; ++ return td_err_set_success(error); + default: + goto usage; + } +@@ -740,16 +791,17 @@ tap_cli_major(int argc, char **argv) + else + major = tap_ctl_blk_major(); + +- if (major < 0) +- return -major; ++ if (major < 0) { ++ return td_err_set_errno(error, -major); ++ } + + printf("%d\n", major); + +- return 0; ++ return td_err_set_success(error); + + usage: + tap_cli_major_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -766,10 +818,10 @@ tap_cli_open_usage(FILE *stream) + } + + static int +-tap_cli_open(int argc, char **argv) ++tap_cli_open(int argc, char **argv, td_err *error) + { + const char *args, *secondary, *logpath; +- int c, pid, minor, flags, prt_minor, timeout; ++ int c, pid, minor, flags, prt_minor, timeout, err; + uint8_t *encryption_key; + ssize_t key_size = 0; + +@@ -782,6 +834,7 @@ tap_cli_open(int argc, char **argv) + secondary = NULL; + logpath = NULL; + encryption_key = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "a:RDm:p:e:r2:st:C:Eh")) != -1) { +@@ -848,22 +901,25 @@ tap_cli_open(int argc, char **argv) + if (encryption_key) { + free(encryption_key); + } +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1 || !args) + goto usage; + +- return tap_ctl_open(pid, minor, args, flags, prt_minor, secondary, ++ err = tap_ctl_open(pid, minor, args, flags, prt_minor, secondary, + timeout, logpath, (uint8_t)key_size, encryption_key); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_open_usage(stderr); + if (encryption_key) { + free(encryption_key); + } +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -876,13 +932,14 @@ tap_cli_stats_usage(FILE *stream) + } + + static int +-tap_cli_stats(int argc, char **argv) ++tap_cli_stats(int argc, char **argv, td_err *error) + { + pid_t pid; + int c, minor, err; + + pid = -1; + minor = -1; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:h")) != -1) { +@@ -897,7 +954,7 @@ tap_cli_stats(int argc, char **argv) + goto usage; + case 'h': + tap_cli_stats_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + +@@ -906,15 +963,15 @@ tap_cli_stats(int argc, char **argv) + + err = tap_ctl_stats_fwrite(pid, minor, stdout); + if (err) +- return err; ++ return td_err_set_errno(error, err); + + fprintf(stdout, "\n"); + +- return 0; ++ return td_err_set_success(error); + + usage: + tap_cli_stats_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -925,22 +982,25 @@ tap_cli_check_usage(FILE *stream) + } + + static int +-tap_cli_check(int argc, char **argv) ++tap_cli_check(int argc, char **argv, td_err *error) + { + int err; + const char *msg; + ++ td_err_init_errno(error); ++ + if (argc != 1) + goto usage; + + err = tap_ctl_check(&msg); + printf("%s\n", msg); +- +- return err; ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_check_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -950,14 +1010,15 @@ tap_cli_commit_usage(FILE *stream) + } + + int +-tap_cli_commit(int argc, char **argv) ++tap_cli_commit(int argc, char **argv, td_err *error) + { + const char *args; +- int c, pid, minor; ++ int c, pid, minor, err; + + pid = -1; + minor = -1; + args = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:a:h")) != -1) { +@@ -982,11 +1043,14 @@ tap_cli_commit(int argc, char **argv) + if (pid == -1 || minor == -1 || args == NULL) + goto usage; + +- return tap_ctl_commit(pid, minor, args); ++ err = tap_ctl_commit(pid, minor, args); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_commit_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -996,12 +1060,13 @@ tap_cli_query_commit_job_usage(FILE *stream) + } + + int +-tap_cli_query_commit_job(int argc, char **argv) ++tap_cli_query_commit_job(int argc, char **argv, td_err *error) + { +- int c, pid, minor; ++ int c, pid, minor, err; + + pid = -1; + minor = -1; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:h")) != -1) { +@@ -1023,11 +1088,14 @@ tap_cli_query_commit_job(int argc, char **argv) + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_query_commit_job(pid, minor); ++ err = tap_ctl_query_commit_job(pid, minor); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_query_commit_job_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -1037,14 +1105,15 @@ tap_cli_cancel_commit_job_usage(FILE *stream) + } + + int +-tap_cli_cancel_commit_job(int argc, char **argv) ++tap_cli_cancel_commit_job(int argc, char **argv, td_err *error) + { +- int c, pid, minor; ++ int c, pid, minor, err; + bool wait; + + pid = -1; + minor = -1; + wait = false; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:wh")) != -1) { +@@ -1069,11 +1138,14 @@ tap_cli_cancel_commit_job(int argc, char **argv) + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_cancel_commit_job(pid, minor, wait); ++ err = tap_ctl_cancel_commit_job(pid, minor, wait); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_cancel_commit_job_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + struct command commands[] = { +@@ -1196,7 +1268,8 @@ main(int argc, char *argv[]) + cargv[cnt++] = arg; + } + +- ret = cmd->func(cnt, cargv); ++ td_err error = td_err_generate_success(); ++ ret = cmd->func(cnt, cargv, &error); + + free(cargv); + free(path); +@@ -1205,5 +1278,7 @@ main(int argc, char *argv[]) + /* FIXME errors are not always returned as negative numbers */ + fprintf(stderr, "%s\n", strerror(abs(ret))); + ++ if (error.code != TD_SUCCESS) ++ td_err_print_error(&error); + return (ret >= 0 ? ret : -ret); + } +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index 4b7aab2..d6fdf43 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -52,6 +52,7 @@ + #include "tapdisk.h" + #include "tapdisk-vbd.h" + #include "tapdisk-blktap.h" ++#include "tapdisk-err.h" + #include "tapdisk-utils.h" + #include "tapdisk-server.h" + #include "tapdisk-message.h" +@@ -129,7 +130,7 @@ struct tapdisk_ctl_conn { + + struct tapdisk_control_info { + int (*handler)(struct tapdisk_ctl_conn *, tapdisk_message_t *, +- tapdisk_message_t * const); ++ tapdisk_message_t * const, td_err *); + int flags; + }; + +@@ -548,7 +549,7 @@ tapdisk_control_validate_request(tapdisk_message_t *request) + + static int + tapdisk_control_list(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + td_vbd_t *vbd; + struct list_head *head; +@@ -584,12 +585,12 @@ tapdisk_control_list(struct tapdisk_ctl_conn *conn, + response->u.list.minor = -1; + response->u.list.path[0] = 0; + +- return 0; ++ return td_err_set_success(error); + } + + static int + tapdisk_control_get_pid(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + ASSERT(response); + +@@ -597,12 +598,12 @@ tapdisk_control_get_pid(struct tapdisk_ctl_conn *conn, + response->cookie = request->cookie; + response->u.tapdisk_pid = getpid(); + +- return 0; ++ return td_err_set_success(error); + } + + static int + tapdisk_control_attach_vbd(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + char *devname = NULL; + td_vbd_t *vbd; +@@ -612,6 +613,8 @@ tapdisk_control_attach_vbd(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + /* + * TODO: check for max vbds per process + */ +@@ -656,9 +659,10 @@ out: + if (!err) { + response->type = TAPDISK_MESSAGE_ATTACH_RSP; + response->cookie = request->cookie; ++ return td_err_set_success(error); + } + +- return err; ++ return td_err_set_errno(error, err); + + fail_vbd: + tapdisk_vbd_detach(vbd); +@@ -668,7 +672,7 @@ fail_vbd: + + static int + tapdisk_control_detach_vbd(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + td_vbd_t *vbd; + int err = 0; +@@ -677,6 +681,8 @@ tapdisk_control_detach_vbd(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + vbd = tapdisk_server_get_vbd(request->cookie); + if (!vbd) { + err = -ENODEV; +@@ -699,16 +705,17 @@ out: + if (!err) { + response->type = TAPDISK_MESSAGE_DETACH_RSP; + response->cookie = request->cookie; ++ return td_err_set_success(error); + } + +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_open_image(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { +- int err, ret; ++ int ret, err = 0; + td_vbd_t *vbd; + td_flag_t flags; + +@@ -716,6 +723,8 @@ tapdisk_control_open_image(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + vbd = tapdisk_server_get_vbd(request->cookie); + if (!vbd) { + err = -EINVAL; +@@ -811,7 +820,7 @@ tapdisk_control_open_image(struct tapdisk_ctl_conn *conn, + } + + err = tapdisk_vbd_open_vdi(vbd, request->u.params.path, flags, +- request->u.params.prt_devnum); ++ request->u.params.prt_devnum, error); + if (err) + goto out; + +@@ -845,16 +854,15 @@ tapdisk_control_open_image(struct tapdisk_ctl_conn *conn, + goto fail_close; + } + +- err = 0; +- + out: + if (!err) { + response->u.image.sectors = vbd->disk_info.size; + response->u.image.sector_size = vbd->disk_info.sector_size; + response->u.image.info = vbd->disk_info.info; + response->type = TAPDISK_MESSAGE_OPEN_RSP; ++ return td_err_set_success(error); + } +- return err; ++ return td_err_set_errno(error, err); + + fail_close: + tapdisk_vbd_close_vdi(vbd); +@@ -869,7 +877,7 @@ fail_close: + + static int + tapdisk_control_close_image(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + td_vbd_t *vbd; + int err = 0; +@@ -879,10 +887,12 @@ tapdisk_control_close_image(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + vbd = tapdisk_server_get_vbd(request->cookie); + if (!vbd) { + EPRINTF("VBD %d does not exist", request->cookie); +- err = -ENODEV; ++ err = -ENODEV; + goto out; + } + +@@ -983,14 +993,16 @@ tapdisk_control_close_image(struct tapdisk_ctl_conn *conn, + + out: + response->cookie = request->cookie; +- if (!err) ++ if (!err) { + response->type = TAPDISK_MESSAGE_CLOSE_RSP; +- return err; ++ return td_err_set_success(error); ++ } ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_pause_vbd(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + struct timeval now, next = { 0, 0 }, interval = { 0, 10000 }; + int err = 0; +@@ -1000,6 +1012,8 @@ tapdisk_control_pause_vbd(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + vbd = tapdisk_server_get_vbd(request->cookie); + if (!vbd) { + /* TODO log error */ +@@ -1033,12 +1047,12 @@ out: + response->cookie = request->cookie; + if (!err) + response->type = TAPDISK_MESSAGE_PAUSE_RSP; +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_resume_vbd(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + int err, ret; + td_vbd_t *vbd; +@@ -1048,6 +1062,8 @@ tapdisk_control_resume_vbd(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + /* TODO validate secondary */ + + INFO("resuming VBD %d, flags=0x%08x, secondary=%p\n", request->cookie, +@@ -1099,17 +1115,19 @@ tapdisk_control_resume_vbd(struct tapdisk_ctl_conn *conn, + if (request->u.params.path[0]) + desc = request->u.params.path; + +- err = tapdisk_vbd_resume(vbd, desc); ++ err = tapdisk_vbd_resume(vbd, desc, error); + out: + response->cookie = request->cookie; +- if (!err) +- response->type = TAPDISK_MESSAGE_RESUME_RSP; +- return err; ++ if (!err) { ++ response->type = TAPDISK_MESSAGE_RESUME_RSP; ++ return td_err_set_success(error); ++ } ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_stats(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + td_stats_t _st, *st = &_st; + td_vbd_t *vbd; +@@ -1121,6 +1139,8 @@ tapdisk_control_stats(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + buf = malloc(TD_CTL_SEND_BUFSZ); + if (!buf) { + rv = -ENOMEM; +@@ -1179,9 +1199,9 @@ out: + response->u.info.length = rv; + tapdisk_control_write_message(conn, response); + conn->out.prod += rv; +- return 0; +- } else +- return rv; ++ return td_err_set_success(error); ++ } ++ return td_err_set_errno(error, rv); + } + + /** +@@ -1193,7 +1213,7 @@ out: + static int + tapdisk_control_xenblkif_connect( + struct tapdisk_ctl_conn *conn __attribute__((unused)), +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + /* + * Get the block interface parameters (domain ID, device ID, etc.). +@@ -1210,6 +1230,7 @@ tapdisk_control_xenblkif_connect( + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); + minor = request->cookie; + + vbd = tapdisk_server_get_vbd(minor); +@@ -1242,13 +1263,13 @@ out: + EPRINTF("VBD %d failed to connect to the shared ring: %s\n", + minor, strerror(-err)); + +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_xenblkif_disconnect( + struct tapdisk_ctl_conn *conn __attribute__((unused)), +- tapdisk_message_t * request, tapdisk_message_t * const response) ++ tapdisk_message_t * request, tapdisk_message_t * const response, td_err *error) + { + tapdisk_message_blkif_t *blkif_msg; + int err; +@@ -1256,6 +1277,7 @@ tapdisk_control_xenblkif_disconnect( + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); + blkif_msg = &request->u.blkif; + + ASSERT(blkif_msg); +@@ -1270,13 +1292,13 @@ tapdisk_control_xenblkif_disconnect( + EPRINTF("failed to disconnect domid=%d, devid=%d from the " + "ring: %s\n", blkif_msg->domid, blkif_msg->devid, + strerror(-err)); +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_disk_info( + struct tapdisk_ctl_conn *conn __attribute__((unused)), +- tapdisk_message_t * request, tapdisk_message_t * const response) ++ tapdisk_message_t * request, tapdisk_message_t * const response, td_err *error) + { + tapdisk_message_image_t *image; + int err = 0; +@@ -1286,6 +1308,7 @@ tapdisk_control_disk_info( + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); + image = &response->u.image; + + vbd = tapdisk_server_get_vbd(request->cookie); +@@ -1304,12 +1327,12 @@ out: + image->sector_size = vbd->disk_info.sector_size; + image->info = vbd->disk_info.info; + } +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_commit(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + int err; + td_vbd_t *vbd; +@@ -1319,6 +1342,7 @@ tapdisk_control_commit(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); + INFO("commit %d\n", request->cookie); + + vbd = tapdisk_server_get_vbd(request->cookie); +@@ -1336,12 +1360,12 @@ out: + response->cookie = request->cookie; + if (!err) + response->type = TAPDISK_MESSAGE_COMMIT_RSP; +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_query_commit_job(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + int err; + td_vbd_t *vbd; +@@ -1351,6 +1375,7 @@ tapdisk_control_query_commit_job(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); + INFO("query commit job %d\n", request->cookie); + + vbd = tapdisk_server_get_vbd(request->cookie); +@@ -1373,12 +1398,12 @@ out: + response->u.query.current_progress = query.current_progress; + response->u.query.total_progress = query.total_progress; + } +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_cancel_commit_job(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + int err; + td_vbd_t *vbd; +@@ -1387,6 +1412,7 @@ tapdisk_control_cancel_commit_job(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); + INFO("cancel commit job %d\n", request->cookie); + + vbd = tapdisk_server_get_vbd(request->cookie); +@@ -1401,7 +1427,7 @@ out: + response->cookie = request->cookie; + if (!err) + response->type = TAPDISK_MESSAGE_CANCEL_COMMIT_JOB_RSP; +- return err; ++ return td_err_set_errno(error, err); + } + + +@@ -1541,6 +1567,8 @@ tapdisk_control_process_request(event_id_t event_id, + ASSERT(conn); + ASSERT(event_id == conn->event_id); + ++ td_err error = td_err_generate_errno_error(0); ++ + if (conn->event_id) + tapdisk_server_unregister_event(conn->event_id); + +@@ -1553,11 +1581,13 @@ tapdisk_control_process_request(event_id_t event_id, + memset(&conn->response, 0, sizeof(conn->response)); + conn->response.cookie = conn->request.cookie; + +- err = conn->info->handler(conn, &conn->request, &conn->response); ++ err = conn->info->handler(conn, &conn->request, &conn->response, &error); + if (err) { + conn->response.type = TAPDISK_MESSAGE_ERROR; + conn->response.u.response.error = -err; + } ++ if (td_err_get_reason(&error)[0]) ++ snprintf(conn->response.u.response.message, TAPDISK_MESSAGE_STRING_LENGTH, "%s", td_err_get_reason(&error)); + if (err || conn->response.type != TAPDISK_MESSAGE_STATS_RSP) + tapdisk_control_write_message(conn, &conn->response); + +diff --git a/drivers/tapdisk-image.c b/drivers/tapdisk-image.c +index eca855a..1368188 100644 +--- a/drivers/tapdisk-image.c ++++ b/drivers/tapdisk-image.c +@@ -231,45 +231,52 @@ fail: + + static int + tapdisk_image_open_parent(td_image_t *image, struct td_vbd_encryption *encryption, +- td_image_t **_parent) ++ td_image_t **_parent, td_err *error) + { + td_image_t *parent = NULL; + td_disk_id_t id; + int err; + ++ td_err_init_errno(error); + memset(&id, 0, sizeof(id)); + id.flags = image->flags; + + err = td_get_parent_id(image, &id); + if (err == TD_NO_PARENT) { +- err = 0; ++ td_err_set_success(error); + goto out; + } + if (err) +- return err; ++ return td_err_set_errno(error, err); + + if (((id.flags & TD_OPEN_NO_O_DIRECT) == TD_OPEN_NO_O_DIRECT) && + ((id.flags & TD_OPEN_LOCAL_CACHE) == TD_OPEN_LOCAL_CACHE)) + id.flags &= ~TD_OPEN_NO_O_DIRECT; + err = tapdisk_image_open(id.type, id.name, id.flags, encryption, &parent); ++ if (err) { ++ td_err_set_reason(error, id.name); ++ free(id.name); ++ return td_err_set_errno(error, err); ++ } ++ td_err_set_success(error); + /* Name has been duped to driver_name */ + free(id.name); +- if (err) +- return err; + + out: + *_parent = parent; +- return err; ++ return td_err_get_errno(error); + } + + static int +-tapdisk_image_open_parents(td_image_t *image, struct td_vbd_encryption *encryption) ++tapdisk_image_open_parents(td_image_t *image, struct td_vbd_encryption *encryption, td_err *error) + { +- td_image_t *parent; ++ td_image_t *parent = NULL; + int err; + ++ td_err_init_errno(error); ++ + do { +- err = tapdisk_image_open_parent(image, encryption, &parent); ++ err = tapdisk_image_open_parent(image, encryption, &parent, error); + if (err) + break; + +@@ -279,7 +286,7 @@ tapdisk_image_open_parents(td_image_t *image, struct td_vbd_encryption *encrypti + } + } while (parent); + +- return err; ++ return td_err_set_errno(error, err); + } + + void +@@ -347,16 +354,19 @@ done: + static int + __tapdisk_image_open_chain(int type, const char *name, int flags, + struct td_vbd_encryption *encryption, struct list_head *_head, +- int prt_devnum) ++ int prt_devnum, td_err *error) + { + char *prt_nbd_path = NULL; + struct list_head head = LIST_HEAD_INIT(head); + td_image_t *image; + int err; + ++ td_err_init_errno(error); + err = tapdisk_image_open(type, name, flags, encryption, &image); +- if (err) ++ if (err) { ++ td_err_set_reason(error, name); + goto fail; ++ } + + list_add_tail(&image->next, &head); + +@@ -371,8 +381,10 @@ __tapdisk_image_open_chain(int type, const char *name, int flags, + err = tapdisk_image_open(DISK_TYPE_NBD, prt_nbd_path, + flags | TD_OPEN_RDONLY, + encryption, &image); +- if (err) ++ if (err) { ++ td_err_set_reason(error, prt_nbd_path); + goto fail; ++ } + + free(prt_nbd_path); + prt_nbd_path = NULL; +@@ -380,19 +392,19 @@ __tapdisk_image_open_chain(int type, const char *name, int flags, + goto done; + } + +- err = tapdisk_image_open_parents(image, encryption); ++ err = tapdisk_image_open_parents(image, encryption, error); + if (err) + goto fail; + + done: + list_splice(&head, _head); +- return 0; ++ return td_err_set_success(error); + + fail: + free(prt_nbd_path); + + tapdisk_image_close_chain(&head); +- return err; ++ return td_err_set_errno(error, err); + } + + int +@@ -434,7 +446,7 @@ fail: + + static int + tapdisk_image_open_x_chain(const char *path, struct td_vbd_encryption *encryption, +- struct list_head *_head) ++ struct list_head *_head, td_err *error) + { + struct list_head head = LIST_HEAD_INIT(head); + td_image_t *image = NULL, *next; +@@ -442,6 +454,8 @@ tapdisk_image_open_x_chain(const char *path, struct td_vbd_encryption *encryptio + FILE *s; + int err; + ++ td_err_init_errno(error); ++ + s = fopen(path, "r"); + if (!s) { + err = -errno; +@@ -506,8 +520,10 @@ tapdisk_image_open_x_chain(const char *path, struct td_vbd_encryption *encryptio + } + + err = tapdisk_image_open(type, path, flags, encryption, &image); +- if (err) ++ if (err) { ++ td_err_set_reason(error, path); + goto fail; ++ } + + list_add_tail(&image->next, &head); + } while (1); +@@ -517,9 +533,10 @@ tapdisk_image_open_x_chain(const char *path, struct td_vbd_encryption *encryptio + goto fail; + } + +- err = tapdisk_image_open_parents(image, encryption); ++ err = tapdisk_image_open_parents(image, encryption, error); + if (err) + goto fail; ++ td_err_set_success(error); + + list_splice(&head, _head); + out: +@@ -530,7 +547,7 @@ out: + if (s) + fclose(s); + +- return err; ++ return td_err_get_errno(error); + + fail: + tapdisk_for_each_image_safe(image, next, &head) +@@ -541,15 +558,19 @@ fail: + + int + tapdisk_image_open_chain(const char *desc, int flags, int prt_devnum, +- struct td_vbd_encryption *encryption, struct list_head *head) ++ struct td_vbd_encryption *encryption, struct list_head *head, td_err *error) + { + const char *name; + int type, err; + ++ td_err_init_errno(error); ++ + type = tapdisk_disktype_parse_params(desc, &name); +- if (type >= 0) +- return __tapdisk_image_open_chain(type, name, flags, encryption, +- head, prt_devnum); ++ if (type >= 0) { ++ err = __tapdisk_image_open_chain(type, name, flags, encryption, ++ head, prt_devnum, error); ++ return td_err_set_errno(error, err); ++ } + + err = type; + +@@ -557,12 +578,12 @@ tapdisk_image_open_chain(const char *desc, int flags, int prt_devnum, + switch (desc[2]) { + case 'c': + if (!strncmp(desc, "x-chain", strlen("x-chain"))) +- err = tapdisk_image_open_x_chain(name, encryption, head); ++ err = tapdisk_image_open_x_chain(name, encryption, head, error); + break; + } + } + +- return err; ++ return td_err_set_errno(error, err); + } + + int +diff --git a/drivers/tapdisk-image.h b/drivers/tapdisk-image.h +index dad72d4..bec01fe 100644 +--- a/drivers/tapdisk-image.h ++++ b/drivers/tapdisk-image.h +@@ -32,6 +32,7 @@ + #define _TAPDISK_IMAGE_H_ + + #include "tapdisk.h" ++#include "tapdisk-err.h" + + struct td_image_handle { + int type; +@@ -79,7 +80,7 @@ struct td_image_handle { + int tapdisk_image_open(int, const char *, int, struct td_vbd_encryption *, td_image_t **); + void tapdisk_image_close(td_image_t *); + +-int tapdisk_image_open_chain(const char *, int, int, struct td_vbd_encryption *, struct list_head *); ++int tapdisk_image_open_chain(const char *, int, int, struct td_vbd_encryption *, struct list_head *, td_err *); + void tapdisk_image_close_chain(struct list_head *); + int tapdisk_image_validate_chain(struct list_head *); + +diff --git a/drivers/tapdisk-stream.c b/drivers/tapdisk-stream.c +index 29588d7..809386a 100644 +--- a/drivers/tapdisk-stream.c ++++ b/drivers/tapdisk-stream.c +@@ -339,7 +339,8 @@ tapdisk_stream_open_image(struct tapdisk_stream *s, const char *name) + goto out; + } + +- err = tapdisk_vbd_open_vdi(s->vbd, name, TD_OPEN_RDONLY, -1); ++ td_err error = td_err_generate_errno_error(0); ++ err = tapdisk_vbd_open_vdi(s->vbd, name, TD_OPEN_RDONLY, -1, &error); + if (err) + goto out; + +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 345fba3..8fcabbb 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -605,11 +605,13 @@ unlock: + } + + int +-tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_devnum) ++tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_devnum, td_err *error) + { + char *tmp = vbd->name; + int err; + ++ td_err_init_errno(error); ++ + if (!list_empty(&vbd->images)) { + err = -EBUSY; + goto fail; +@@ -628,7 +630,7 @@ tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_d + } + } + +- err = tapdisk_image_open_chain(vbd->name, flags, prt_devnum, &vbd->encryption, &vbd->images); ++ err = tapdisk_image_open_chain(vbd->name, flags, prt_devnum, &vbd->encryption, &vbd->images, error); + if (err) + goto fail; + +@@ -681,7 +683,7 @@ tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_d + if (vbd->nbd_mirror_failed != 1) + goto fail; + INFO("Ignoring failed NBD secondary attach\n"); +- err = 0; ++ td_err_set_success(error); + } + } + +@@ -701,7 +703,7 @@ tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_d + if (tmp != vbd->name) + free(tmp); + +- return err; ++ return td_err_get_errno(error); + + fail: + if (vbd->name != tmp) { +@@ -722,7 +724,7 @@ fail: + + vbd->flags = 0; + +- return err; ++ return td_err_set_errno(error, err); + } + + void +@@ -1062,23 +1064,25 @@ tapdisk_vbd_pause(td_vbd_t *vbd) + } + + int +-tapdisk_vbd_resume(td_vbd_t *vbd, const char *name) ++tapdisk_vbd_resume(td_vbd_t *vbd, const char *name, td_err *error) + { +- int i, err; ++ int i, err = 0; + struct td_xenblkif *blkif; + ++ td_err_init_errno(error); ++ + DBG(TLOG_DBG, "resume requested\n"); + + pthread_mutex_lock(&vbd->mutex); + if (!td_flag_test(vbd->state, TD_VBD_PAUSED)) { + pthread_mutex_unlock(&vbd->mutex); + EPRINTF("resume request for unpaused vbd %s\n", vbd->name); +- return -EINVAL; ++ return td_err_set_errno(error, -EINVAL); + } + pthread_mutex_unlock(&vbd->mutex); + + for (i = 0; i < TD_VBD_EIO_RETRIES; i++) { +- err = tapdisk_vbd_open_vdi(vbd, name, vbd->flags | TD_OPEN_STRICT, -1); ++ err = tapdisk_vbd_open_vdi(vbd, name, vbd->flags | TD_OPEN_STRICT, -1, error); + if (!err) + break; + +@@ -1107,7 +1111,7 @@ resume_failed: + td_flag_set(vbd->state, TD_VBD_RESUME_FAILED); + pthread_mutex_unlock(&vbd->mutex); + tapdisk_vbd_close_vdi(vbd); +- return err; ++ return td_err_set_errno(error, err); + } + td_flag_clear(vbd->state, TD_VBD_RESUME_FAILED); + +@@ -1130,7 +1134,7 @@ resume_failed: + + DBG(TLOG_DBG, "state checked\n"); + +- return 0; ++ return td_err_set_success(error); + } + + int +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index bfac0c6..0993007 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -209,7 +209,7 @@ int tapdisk_vbd_close(td_vbd_t *); + * @returns 0 on success + */ + int tapdisk_vbd_open_vdi(td_vbd_t * vbd, const char *params, td_flag_t flags, +- int prt_devnum); ++ int prt_devnum, td_err *error); + void tapdisk_vbd_close_vdi(td_vbd_t *); + + int tapdisk_vbd_attach(td_vbd_t *, const char *, int); +@@ -226,7 +226,7 @@ int tapdisk_vbd_issue_requests(td_vbd_t *); + int tapdisk_vbd_kill_queue(td_vbd_t *); + int tapdisk_vbd_pause(td_vbd_t *); + void tapdisk_vbd_squash_pause_logging(bool squash); +-int tapdisk_vbd_resume(td_vbd_t *, const char *); ++int tapdisk_vbd_resume(td_vbd_t *, const char *, td_err *); + void tapdisk_vbd_kick(td_vbd_t *, bool); + void tapdisk_vbd_check_state(td_vbd_t *); + void tapdisk_vbd_free(td_vbd_t *); +diff --git a/include/Makefile.am b/include/Makefile.am +index 91b3680..604128a 100644 +--- a/include/Makefile.am ++++ b/include/Makefile.am +@@ -16,6 +16,7 @@ blktap_HEADERS += blktap3.h + blktap_HEADERS += xen_blkif.h + blktap_HEADERS += tapdisk-message.h + blktap_HEADERS += tap-ctl.h ++blktap_HEADERS += tapdisk-err.h + blktap_HEADERS += debug.h + blktap_HEADERS += util.h + blktap_HEADERS += ../drivers/tapdisk-metrics-stats.h +diff --git a/include/tap-ctl.h b/include/tap-ctl.h +index f093912..160ecfc 100644 +--- a/include/tap-ctl.h ++++ b/include/tap-ctl.h +@@ -34,6 +34,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -136,7 +137,7 @@ int tap_ctl_pause(const int id, const int minor, struct timeval *timeout); + * Unpauses the VBD + */ + int tap_ctl_unpause(const int id, const int minor, const char *params, +- int flags, char *secondary, const char *logpath); ++ int flags, char *secondary, const char *logpath, td_err *error); + + ssize_t tap_ctl_stats(pid_t pid, int minor, char *buf, size_t size); + int tap_ctl_stats_fwrite(pid_t pid, int minor, FILE *out); +diff --git a/include/tapdisk-err.h b/include/tapdisk-err.h +new file mode 100644 +index 0000000..b29530a +--- /dev/null ++++ b/include/tapdisk-err.h +@@ -0,0 +1,182 @@ ++/* ++ * Copyright (c) 2026, Vates SAS ++ * ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the copyright holder nor the names of its ++ * contributors may be used to endorse or promote products derived from ++ * this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER ++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef _TAPDISK_ERR_H_ ++#define _TAPDISK_ERR_H_ ++ ++#define td_err_init_errno(e) _td_err_init_errno((e), __func__) ++#define td_err_generate_success() _td_err_generate_success(__func__) ++#define td_err_generate_errno_error(e) _td_err_generate_errno_error((e), __func__) ++ ++#include ++#include ++#include "debug.h" ++ ++#define TAPDISK_ERR_STRING_LENGTH 256 ++ ++typedef enum _td_err_code_e { ++ TD_SUCCESS, ++ TD_USE_ERRNO, ++ TD_UNKNOWN ++} td_err_code; ++ ++typedef struct _td_err_s { ++ char cmd[TAPDISK_ERR_STRING_LENGTH]; ++ char reason[TAPDISK_ERR_STRING_LENGTH]; ++ td_err_code code; ++ int saved_errno; ++} td_err; ++ ++static inline const char * ++td_err_code_to_string(const td_err_code code) ++{ ++ switch (code) { ++ case TD_SUCCESS: ++ return "success"; ++ case TD_USE_ERRNO: ++ case TD_UNKNOWN: ++ return "invalid error code"; ++ default: ++ ASSERT(0); ++ return "unhandled code"; ++ } ++} ++ ++static inline const char * ++td_err_get_cmd(const td_err *err) ++{ ++ ASSERT(err) ++ return err->cmd; ++} ++ ++static inline const char * ++td_err_get_reason(const td_err *err) ++{ ++ ASSERT(err) ++ return err->reason; ++} ++ ++static inline td_err_code ++td_err_get_code(const td_err *err) ++{ ++ ASSERT(err) ++ return err->code; ++} ++ ++static inline int ++td_err_get_errno(const td_err *err) ++{ ++ ASSERT(err) ++ return err->saved_errno; ++} ++ ++static inline int ++td_err_set_cmd(td_err *err, const char *cmd) ++{ ++ ASSERT(err) ++ if (!cmd) { ++ err->cmd[0] = '\0'; ++ return 0; ++ } ++ return snprintf(err->cmd, TAPDISK_ERR_STRING_LENGTH, "%s", cmd); ++} ++ ++static inline int ++td_err_set_reason(td_err *err, const char *reason) ++{ ++ ASSERT(err) ++ if (!reason) { ++ err->reason[0] = '\0'; ++ return 0; ++ } ++ return snprintf(err->reason, TAPDISK_ERR_STRING_LENGTH, "%s", reason); ++} ++ ++static inline td_err_code ++td_err_set_code(td_err *err, const td_err_code code) ++{ ++ ASSERT(err) ++ return err->code = code; ++} ++ ++static inline int ++td_err_set_errno(td_err *err, const int saved_errno) ++{ ++ ASSERT(err) ++ return err->saved_errno = saved_errno; ++} ++ ++static inline void ++td_err_reset(td_err *err) ++{ ++ ASSERT(err) ++ memset(err, 0, sizeof(*err)); ++} ++ ++static inline int ++td_err_set_success(td_err *err) ++{ ++ td_err_set_code(err, TD_SUCCESS); ++ return td_err_set_errno(err, 0); ++} ++ ++static inline void ++_td_err_init_errno(td_err *err, const char *cmd) ++{ ++ td_err_set_cmd(err, cmd); ++ td_err_set_code(err, TD_USE_ERRNO); ++} ++ ++static inline void ++td_err_init(td_err *err, const int saved_errno, const td_err_code code, const char *cmd) ++{ ++ td_err_reset(err); ++ td_err_set_code(err, code); ++ td_err_set_errno(err, saved_errno); ++ td_err_set_cmd(err, cmd); ++} ++ ++static inline td_err ++_td_err_generate_success(const char *cmd) ++{ ++ td_err ret; ++ td_err_init(&ret, 0, TD_SUCCESS, cmd); ++ return ret; ++} ++ ++static inline td_err ++_td_err_generate_errno_error(const int err, const char *cmd) ++{ ++ td_err ret; ++ td_err_init(&ret, err, TD_USE_ERRNO, cmd); ++ return ret; ++} ++ ++#endif //_TAPDISK_ERR_H_ diff --git a/SOURCES/0046-fix-skip-cbtlog-disks-in-commit-related-operations.patch b/SOURCES/0046-fix-skip-cbtlog-disks-in-commit-related-operations.patch new file mode 100644 index 0000000..0a7a4b2 --- /dev/null +++ b/SOURCES/0046-fix-skip-cbtlog-disks-in-commit-related-operations.patch @@ -0,0 +1,64 @@ +From 7034c039d092ade9ce6eef37f1a249289fd42a88 Mon Sep 17 00:00:00 2001 +From: Mathieu Labourier +Date: Mon, 17 Aug 2026 15:08:15 +0200 +Subject: [PATCH] fix: skip cbtlog disks in commit related operations + +This prevents a bug where tapdisk will return a cbtlog +disk in commit related operations, causing them to fail early +since the cbtlog driver doesn't include a commit action + +Signed-off-by: Mathieu Labourier +--- + drivers/tapdisk-vbd.c | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 8fcabbbd..7b5171cd 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -167,6 +167,18 @@ tapdisk_vbd_first_image(td_vbd_t *vbd) + return image; + } + ++static inline td_image_t * ++tapdisk_vbd_first_non_log_image(td_vbd_t *vbd) ++{ ++ td_image_t *image = NULL, *tmp; ++ ++ tapdisk_vbd_for_each_image(vbd, image, tmp) ++ if (image->type != DISK_TYPE_LOG) ++ return image; ++ ++ return image; ++} ++ + static inline td_image_t * + tapdisk_vbd_last_image(td_vbd_t *vbd) + { +@@ -1146,7 +1158,7 @@ tapdisk_vbd_commit(td_vbd_t *vbd, const char *name) + INFO("commit %s\n", name); + } + +- err = td_commit(tapdisk_vbd_first_image(vbd), name); ++ err = td_commit(tapdisk_vbd_first_non_log_image(vbd), name); + + INFO("commit started (%d)\n", err); + +@@ -1162,7 +1174,7 @@ tapdisk_vbd_query_commit_job(td_vbd_t *vbd, td_query_t *query) + INFO("query commit job.\n"); + } + +- err = td_query_commit_job(tapdisk_vbd_first_image(vbd), query); ++ err = td_query_commit_job(tapdisk_vbd_first_non_log_image(vbd), query); + + INFO("query commit job (%d)\n", err); + +@@ -1178,7 +1190,7 @@ tapdisk_vbd_cancel_commit_job(td_vbd_t *vbd, bool wait) + INFO("cancel commit job.\n"); + } + +- err = td_cancel_commit_job(tapdisk_vbd_first_image(vbd), wait); ++ err = td_cancel_commit_job(tapdisk_vbd_first_non_log_image(vbd), wait); + + INFO("cancel commit job (%d)\n", err); + diff --git a/SOURCES/CP-308382_fix_sign_conversion_in_coalesce b/SOURCES/CP-308382_fix_sign_conversion_in_coalesce new file mode 100644 index 0000000..9372c83 --- /dev/null +++ b/SOURCES/CP-308382_fix_sign_conversion_in_coalesce @@ -0,0 +1,75 @@ +From f4ec626158a4803d4042f1161d39ffe1ca8a630a Mon Sep 17 00:00:00 2001 +From: Mark Syms +Date: Tue, 17 Jun 2025 17:03:08 +0100 +Subject: [PATCH] CP-308382: fix sign conversion in coalesce + +A number of places in vhd-util-coalesce.c produce warnings when +compiled with -Wsign-conversion and might be contributing to the +coalesced_size being reported as a negative error. Ensure there are no +sign-conversion warnings in this code file. + +Signed-off-by: Mark Syms +--- + vhd/lib/vhd-util-coalesce.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +diff --git a/vhd/lib/vhd-util-coalesce.c b/vhd/lib/vhd-util-coalesce.c +index 41132c2..d3a85d6 100644 +--- a/vhd/lib/vhd-util-coalesce.c ++++ b/vhd/lib/vhd-util-coalesce.c +@@ -46,10 +46,10 @@ static int + __raw_io_write(int fd, char* buf, uint64_t sec, uint32_t secs) + { + off64_t off; +- size_t ret; ++ ssize_t ret; + + errno = 0; +- off = lseek64(fd, vhd_sectors_to_bytes(sec), SEEK_SET); ++ off = lseek64(fd, (off64_t)vhd_sectors_to_bytes(sec), SEEK_SET); + if (off == (off64_t)-1) { + printf("raw parent: seek(0x%08"PRIx64") failed: %d\n", + vhd_sectors_to_bytes(sec), -errno); +@@ -78,7 +78,8 @@ static int64_t + vhd_util_coalesce_block(vhd_context_t *vhd, vhd_context_t *parent, + int parent_fd, uint64_t block) + { +- int i, err; ++ int err; ++ uint32_t i; + int64_t coalesced_size = 0; + char *buf; + char *map; +@@ -141,8 +142,8 @@ vhd_util_coalesce_block(vhd_context_t *vhd, vhd_context_t *parent, + if (err) + goto done; + +- coalesced_size += secs; +- i += secs; ++ coalesced_size += (int64_t)secs; ++ i += (uint32_t)secs; + } + + err = 0; +@@ -169,7 +170,8 @@ static int64_t + vhd_util_coalesce_onto(vhd_context_t *from, + vhd_context_t *to, int to_fd, int progress) + { +- int i, err; ++ int i; ++ int64_t err; + int64_t coalesced_size = 0; + + err = vhd_get_bat(from); +@@ -188,7 +190,7 @@ vhd_util_coalesce_onto(vhd_context_t *from, + ((float)i / (float)from->bat.entries) * 100.00); + fflush(stdout); + } +- err = vhd_util_coalesce_block(from, to, to_fd, i); ++ err = vhd_util_coalesce_block(from, to, to_fd, (uint64_t)i); + if (err < 0) + goto out; + +-- +2.49.0 + diff --git a/SOURCES/blktap-3.55.4.tar.gz b/SOURCES/blktap-3.55.4.tar.gz deleted file mode 100644 index f1bf78a..0000000 --- a/SOURCES/blktap-3.55.4.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:222dda0280d4221dc9af75bf86257bf37bd50f957fb35d6cd7485c6cd176ebc5 -size 389168 diff --git a/SOURCES/blktap-3.55.5.tar.gz b/SOURCES/blktap-3.55.5.tar.gz new file mode 100644 index 0000000..0567889 --- /dev/null +++ b/SOURCES/blktap-3.55.5.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c73ee301b0b668b4c956571cdf16d0672d2396b6bf4e31f4ce704cfec6c1ff4c +size 389274 diff --git a/SOURCES/ca-404370__enable_nbd_client_only_after_completing_handshake.patch b/SOURCES/ca-404370__enable_nbd_client_only_after_completing_handshake.patch new file mode 100644 index 0000000..b6e3aa7 --- /dev/null +++ b/SOURCES/ca-404370__enable_nbd_client_only_after_completing_handshake.patch @@ -0,0 +1,80 @@ +CA-404370: enable NBD client only after completing handshake + +From: Mark Syms + +Previously the client was being enabled as part of +`tapdisk_nbdserver_newclient_fd_new_fixed` after calling +`tapdisk_nbdserver_new_protocol_handshake`. This erroneously assumed +that protocol handshake was complete by the time that function +returned, which is not the case as it simply adds the fd to an event +for the scheduler. Protocol handshake is only complete when we reach +the end of the `tapdisk_nbdserver_handshake_cb` callback and enabling +the client before this means we have two events registered for the +same fd. + +This was causing quicktest to fail after blktap2 was removed and NBD +is used for all control domain access to the I/O datapath. + +Signed-off-by: Mark Syms + +diff --git a/drivers/tapdisk-nbdserver.c b/drivers/tapdisk-nbdserver.c +index 2c332c2..63ab4d5 100644 +--- a/drivers/tapdisk-nbdserver.c ++++ b/drivers/tapdisk-nbdserver.c +@@ -1029,6 +1029,7 @@ void + tapdisk_nbdserver_handshake_cb(event_id_t id, char mode, void *data) + { + uint32_t cflags = 0; ++ int tmp_fd; + + td_nbdserver_client_t *client = (td_nbdserver_client_t*)data; + td_nbdserver_t *server = client->server; +@@ -1036,7 +1037,8 @@ tapdisk_nbdserver_handshake_cb(event_id_t id, char mode, void *data) + int rc = recv_fully_or_fail(server->handshake_fd, &cflags, sizeof(cflags)); + if(rc < 0) { + ERR("Could not receive client flags"); +- return; ++ close(server->handshake_fd); ++ goto out; + } + + cflags = be32toh (cflags); +@@ -1048,6 +1050,15 @@ tapdisk_nbdserver_handshake_cb(event_id_t id, char mode, void *data) + close(server->handshake_fd); + } + ++ INFO("About to enable client on fd %d", client->client_fd); ++ if (tapdisk_nbdserver_enable_client(client) < 0) { ++ ERR("Error enabling client"); ++ tmp_fd = client->client_fd; ++ tapdisk_nbdserver_free_client(client); ++ close(tmp_fd); ++ } ++ ++out: + tapdisk_server_unregister_event(id); + } + +@@ -1157,13 +1168,6 @@ tapdisk_nbdserver_newclient_fd_new_fixed(td_nbdserver_t *server, int new_fd) + close(new_fd); + return; + } +- +- INFO("About to enable client on fd %d", client->client_fd); +- if (tapdisk_nbdserver_enable_client(client) < 0) { +- ERR("Error enabling client"); +- tapdisk_nbdserver_free_client(client); +- close(new_fd); +- } + } + + static void +@@ -1402,7 +1406,7 @@ tapdisk_nbdserver_newclient_unix(event_id_t id, char mode, void *data) + return; + } + +- INFO("server: got connection\n"); ++ INFO("server: got connection fd = %d\n", new_fd); + + tapdisk_nbdserver_newclient_fd_new_fixed(server, new_fd); + } diff --git a/SOURCES/ca-408175__distinguish_logging_for_long_nbd_operations.patch b/SOURCES/ca-408175__distinguish_logging_for_long_nbd_operations.patch new file mode 100644 index 0000000..7805faf --- /dev/null +++ b/SOURCES/ca-408175__distinguish_logging_for_long_nbd_operations.patch @@ -0,0 +1,28 @@ +CA-408175: distinguish logging for long NBD operations + +From: Mark Syms + +Signed-off-by: Mark Syms + +diff --git a/drivers/tapdisk-nbdserver.c b/drivers/tapdisk-nbdserver.c +index 63ab4d5..95ff964 100644 +--- a/drivers/tapdisk-nbdserver.c ++++ b/drivers/tapdisk-nbdserver.c +@@ -915,7 +915,7 @@ __tapdisk_nbdserver_structured_read_cb( + interval = timeval_to_us(&now) - timeval_to_us(&vreq->ts); + + if (interval > 20 * 1000 * 1000) { +- INFO("request took %llu microseconds to complete", interval); ++ INFO("Structured read took %llu microseconds to complete", interval); + } + + if (client->client_fd < 0) { +@@ -980,7 +980,7 @@ __tapdisk_nbdserver_request_cb(td_vbd_request_t *vreq, int error, + interval = timeval_to_us(&now) - timeval_to_us(&vreq->ts); + + if (interval > 20 * 1000 * 1000) { +- INFO("request took %llu microseconds to complete", interval); ++ INFO("Op %d request took %llu microseconds to complete", vreq->op, interval); + } + + if (client->client_fd < 0) { diff --git a/SOURCES/ca-416464__return_blkif_rsp_eopnotsupp_for_eopnotsupp.patch b/SOURCES/ca-416464__return_blkif_rsp_eopnotsupp_for_eopnotsupp.patch new file mode 100644 index 0000000..6ce178c --- /dev/null +++ b/SOURCES/ca-416464__return_blkif_rsp_eopnotsupp_for_eopnotsupp.patch @@ -0,0 +1,26 @@ +CA-416464: return BLKIF_RSP_EOPNOTSUPP for EOPNOTSUPP + +From: Mark Syms + +Returning BLKIF_RSP_ERROR if an operation is not supported does not +allow the frontend to exercise any discretion on how to handle the +response and may lead to an operating system crash. As different +backends may support different feature sets and we might, during a +migration, switch backends, an in-flight request might be issued (or +reissued) which is then not supported by this backend. + +Signed-off-by: Mark Syms + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index 7b168dc..2a3fb05 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -531,6 +531,8 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + + if (likely(err == 0)) + _err = BLKIF_RSP_OKAY; ++ else if (err == EOPNOTSUPP) ++ _err = BLKIF_RSP_EOPNOTSUPP; + else + _err = BLKIF_RSP_ERROR; + diff --git a/SOURCES/cp_54256_log_eopnotsupp b/SOURCES/cp_54256_log_eopnotsupp new file mode 100644 index 0000000..8b37456 --- /dev/null +++ b/SOURCES/cp_54256_log_eopnotsupp @@ -0,0 +1,65 @@ +From 4935013b85bad9672247510e4d6816b199b16b2c Mon Sep 17 00:00:00 2001 +From: Mark Syms +Date: Fri, 4 Apr 2025 10:13:21 +0100 +Subject: [PATCH] CP-54256: log errors when reporting EOPNOTSUP + +In cases where a request cannot be dispatched because a driver doesn't +implement the target function log the error before returing the error +code. + +Additionally log the image name when processing a failure in +td_complete_request. + +Signed-off-by: Mark Syms +--- + drivers/tapdisk-interface.c | 3 +++ + drivers/tapdisk-vbd.c | 3 ++- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/tapdisk-interface.c b/drivers/tapdisk-interface.c +index facb6d0..2cdd128 100644 +--- a/drivers/tapdisk-interface.c ++++ b/drivers/tapdisk-interface.c +@@ -183,6 +183,7 @@ td_queue_write(td_image_t *image, td_request_t treq) + } + + if (!driver->ops->td_queue_write) { ++ EPRINTF("Driver %s does not support write", driver->name); + err = -EOPNOTSUPP; + goto fail; + } +@@ -217,6 +218,7 @@ td_queue_read(td_image_t *image, td_request_t treq) + } + + if (!driver->ops->td_queue_read) { ++ EPRINTF("Driver %s does not support read", driver->name); + err = -EOPNOTSUPP; + goto fail; + } +@@ -251,6 +253,7 @@ td_queue_block_status(td_image_t *image, td_request_t *treq) + } + + if (!driver->ops->td_queue_block_status) { ++ EPRINTF("Driver %s does not support block status", driver->name); + err = -EOPNOTSUPP; + goto fail; + } +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 628df4c..8438570 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -1255,9 +1255,10 @@ __tapdisk_vbd_complete_td_request(td_vbd_t *vbd, td_vbd_request_t *vreq, + if (!vreq->error && + err != vreq->prev_error) + tlog_drv_error(image->driver, err, +- "req %s: %s 0x%04x secs @ 0x%08"PRIx64" - %s", ++ "req %s: %s %s 0x%04x secs @ 0x%08"PRIx64" - %s", + vreq->name, + op_strings[treq.op], ++ image->name, + treq.secs, treq.sec, strerror(abs(err))); + vbd->errors++; + } +-- +2.49.0 + diff --git a/SOURCES/fix_coalesced_size_conversion_in_vhd-util-coalesce.patch b/SOURCES/fix_coalesced_size_conversion_in_vhd-util-coalesce.patch new file mode 100644 index 0000000..028fa05 --- /dev/null +++ b/SOURCES/fix_coalesced_size_conversion_in_vhd-util-coalesce.patch @@ -0,0 +1,61 @@ +Fix coalesced size conversion in vhd-util-coalesce + +From: Ronan Abhamon + +If coalesced_size exceeds the max size of an int32, +it will be incorrectly converted from int64 to int32 +and result in a negative value during assignment of +the return from vhd_util_coalesce_onto. + +Signed-off-by: Ronan Abhamon + +diff --git a/vhd/lib/vhd-util-coalesce.c b/vhd/lib/vhd-util-coalesce.c +index d3a85d6..8bcfb79 100644 +--- a/vhd/lib/vhd-util-coalesce.c ++++ b/vhd/lib/vhd-util-coalesce.c +@@ -221,7 +221,8 @@ static int64_t + vhd_util_coalesce_parent(const char *name, int sparse, int progress) + { + char *pname; +- int err, parent_fd; ++ int64_t err; ++ int parent_fd; + vhd_context_t vhd, parent; + + parent_fd = -1; +@@ -229,7 +230,7 @@ vhd_util_coalesce_parent(const char *name, int sparse, int progress) + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY); + if (err) { +- printf("error opening %s: %d\n", name, err); ++ printf("error opening %s: %" PRId64 "\n", name, err); + return err; + } + +@@ -241,7 +242,7 @@ vhd_util_coalesce_parent(const char *name, int sparse, int progress) + + err = vhd_parent_locator_get(&vhd, &pname); + if (err) { +- printf("error finding %s parent: %d\n", name, err); ++ printf("error finding %s parent: %" PRId64 "\n", name, err); + vhd_close(&vhd); + return err; + } +@@ -250,7 +251,7 @@ vhd_util_coalesce_parent(const char *name, int sparse, int progress) + parent_fd = open_optional_odirect(pname, O_RDWR | O_DIRECT | O_LARGEFILE, 0644); + if (parent_fd == -1) { + err = -errno; +- printf("failed to open parent %s: %d\n", pname, err); ++ printf("failed to open parent %s: %" PRId64 "\n", pname, err); + free(pname); + vhd_close(&vhd); + return err; +@@ -260,7 +261,7 @@ vhd_util_coalesce_parent(const char *name, int sparse, int progress) + if (sparse) printf("opening for sparse writes\n"); + err = vhd_open(&parent, pname, VHD_OPEN_RDWR | flags); + if (err) { +- printf("error opening %s: %d\n", pname, err); ++ printf("error opening %s: %" PRId64 "\n", pname, err); + free(pname); + vhd_close(&vhd); + return err; diff --git a/SOURCES/prevent_segfault_of_vhd-util_scan_on_vhd_with_corrupt_footer.patch b/SOURCES/prevent_segfault_of_vhd-util_scan_on_vhd_with_corrupt_footer.patch new file mode 100644 index 0000000..8360de0 --- /dev/null +++ b/SOURCES/prevent_segfault_of_vhd-util_scan_on_vhd_with_corrupt_footer.patch @@ -0,0 +1,55 @@ +Prevent segfault of vhd-util scan on VHD with corrupt footer + +From: Mathieu Labourier + +vhd-util scan can crash when ran against a VHD with a corrupt footer. + +Happens when the footer reports a VHD type that should have a parent +(like HD_TYPE_DIFF) while having none, causing the segfault. + +Signed-off-by: Mathieu Labourier + +diff --git a/vhd/lib/vhd-util-scan.c b/vhd/lib/vhd-util-scan.c +index eae9d68..17392ab 100644 +--- a/vhd/lib/vhd-util-scan.c ++++ b/vhd/lib/vhd-util-scan.c +@@ -46,6 +46,7 @@ + #include + #include + ++#include "debug.h" + #include "list.h" + #include "libvhd.h" + #include "lvm-util.h" +@@ -482,6 +483,14 @@ copy_name(char *dst, const char *src) + static int + vhd_util_scan_extract_volume_name(char *dst, const char *src, size_t size) + { ++ ASSERT(dst); ++ ASSERT(src); ++ ++ if (!*src) { ++ EPRINTF("parent name is empty\n"); ++ return -EINVAL; ++ } ++ + char copy[VHD_MAX_NAME_LEN], *name, *s, *c; + + name = strrchr(src, '/'); +@@ -509,6 +518,7 @@ vhd_util_scan_extract_volume_name(char *dst, const char *src, size_t size) + return -EINVAL; + } + ++ ASSERT(c && *c == '/'); + safe_strncpy(dst, ++c, size); + return 0; + } +@@ -542,7 +552,7 @@ found: + if (!err) + return copy_name(image->parent, name); + +- return 0; ++ return err; + } + + static int diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index 5981568..897dc5b 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -1,23 +1,34 @@ -%global package_speccommit 266dddc02a877617a4c52a36d4dd86ef515c45ec -%global package_srccommit v3.55.4 +%global package_speccommit 284310adc02e3a383ece9d885a11231c1cc56374 +%global usver 3.55.5 +%global xsver 9 +%global xsrel %{xsver}%{?xscount}%{?xshash} +%global package_srccommit v3.55.5 Summary: blktap user space utilities Name: blktap -Version: 3.55.4 -Release: 1%{?xsrel}.1%{?dist} -License: BSD +Version: 3.55.5 +Release: %{?xsrel}.4%{?dist} +License: BSD AND GPL-2.0-or-later Group: System/Hypervisor URL: https://github.com/xapi-project/blktap -Source0: blktap-3.55.4.tar.gz +Source0: blktap-3.55.5.tar.gz +Patch0: ca-404370__enable_nbd_client_only_after_completing_handshake.patch +Patch1: cp_54256_log_eopnotsupp +Patch2: ca-408175__distinguish_logging_for_long_nbd_operations.patch +Patch3: CP-308382_fix_sign_conversion_in_coalesce +Patch4: fix_coalesced_size_conversion_in_vhd-util-coalesce.patch +Patch5: ca-416464__return_blkif_rsp_eopnotsupp_for_eopnotsupp.patch +Patch6: prevent_segfault_of_vhd-util_scan_on_vhd_with_corrupt_footer.patch BuildRoot: %{_tmppath}/%{name}-%{release}-buildroot Obsoletes: xen-blktap < 4 -BuildRequires: e2fsprogs-devel, libaio-devel, systemd, autogen, autoconf, automake, libtool, libuuid-devel +BuildRequires: e2fsprogs-devel, libaio-devel, systemd, autoconf, automake, libtool, libuuid-devel BuildRequires: kernel-headers, xen-libs-devel, zlib-devel, libcmocka-devel, lcov, git -BuildRequires: xs-openssl-devel >= 1.1.1 +BuildRequires: openssl-devel >= 3.0.9 BuildRequires: devtoolset-11-gcc BuildRequires: devtoolset-11-binutils BuildRequires: devtoolset-11-liblsan-devel +BuildRequires: glib2-devel, gnutls-devel, libzstd-devel %{?_cov_buildrequires} Requires(post): systemd Requires(post): /sbin/ldconfig @@ -30,10 +41,54 @@ Conflicts: sm < 3.0.1 Provides: blktap(nbd) = 2.0 # XCP-ng patches -# Required by XOSTOR. Upstream PR: https://github.com/xapi-project/blktap/pull/378 -Patch1001: 0001-Add-an-option-to-never-resolve-parent-path-when-vhd-.patch +# git format-patch XS-v3.55.5-1..v3.55.5-qcow2 --no-signature --no-numbered # Required by sm (qcow2). Upstream PR: https://github.com/xapi-project/blktap/pull/417 -Patch1002: 0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch +Patch1001: 0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch +Patch1002: 0002-tapdisk-deduplicate-double-assignment-code.patch +Patch1003: 0003-blktap-fix-a-typo-in-libaio-backend.h-header.patch +Patch1004: 0004-tapdisk-document-final-param-in-__tapdisk_xenblkif_r.patch +Patch1005: 0005-tapdisk-use-tapdisk_vbd_for_each_blkif-abstraction.patch +Patch1006: 0006-blkif-Avoid-use-after-free-on-BLKIF_OP_WRITE_BARRIER.patch +Patch1007: 0007-tapdisk-vbd-remove-double-assignment-of-error-variab.patch +Patch1008: 0008-tapdisk-replace-flag-number-by-its-name.patch +Patch1009: 0009-tapdisk-set-generic-TAPDISK_MESSAGE_MAX-limit-inside.patch +Patch1010: 0010-tapdisk-remove-unused-file-tapdisk-diff.c.patch +Patch1011: 0011-blkif-add-a-comment-on-memory-barrier-usage.patch +Patch1012: 0012-tapback-Synchronise-usage-with-code.patch +Patch1013: 0013-tap-ctl-fix-comments-of-tap_ctl_info-function.patch +Patch1014: 0014-tapdisk-fix-hardcoded-array-size-with-a-macro.patch +Patch1015: 0015-tapdisk-Replace-structure-name-in-sizeof.patch +Patch1016: 0016-tapdisk-check-if-RD-macros-are-defined-in-ring.h-sin.patch +Patch1017: 0017-tapdisk-Fix-a-typo-in-util.h-header.patch +Patch1018: 0018-tapdisk-rename-field-pool-to-pool_name.patch +Patch1019: 0019-td-req-remove-unused-field-gref.patch +Patch1020: 0020-td-req-rename-tapreq-as-req.patch +Patch1021: 0021-td-req-remove-old-code.patch +Patch1022: 0022-td-req-fix-typo-in-a-comment.patch +Patch1023: 0023-libqcow2-manage-libqcow2-sources-import.patch +Patch1024: 0024-libqcow2-import-vanilla-sources-from-qemu-9.1.1.patch +Patch1025: 0025-libqcow2-build-qcow2-library-for-tapdisk.patch +Patch1026: 0026-libqcow2-fix-support-for-old-components-gcc-glibc-gl.patch +Patch1027: 0027-tapdisk-protect-td_vbd_t-structure.patch +Patch1028: 0028-tapdisk-protect-td_blktap_t-structure.patch +Patch1029: 0029-tapdisk-protect-td_xenblkif-structure.patch +Patch1030: 0030-tapdisk-protect-scheduler-structure.patch +Patch1031: 0031-mocka-fix-scheduler-tests-according-mutex-protection.patch +Patch1032: 0032-tapdisk-protect-td_nbdserver-structures.patch +Patch1033: 0033-libqcow2-prepare-proper-cleanup-of-libqcow2-on-close.patch +Patch1034: 0034-libqcow2-mask-signals-used-by-tapdisk.patch +Patch1035: 0035-tapdisk-replace-signals-handling-by-signalfd.patch +Patch1036: 0036-qcow2-driver-support-qcow2-images-in-tapdisk.patch +Patch1037: 0037-vbd-wake-up-scheduler-to-force-check-ring.patch +Patch1038: 0038-tapdisk-support-new-commit-command.patch +Patch1039: 0039-qcow2-support-commit-command.patch +Patch1040: 0040-tapdisk-support-new-query-command.patch +Patch1041: 0041-qcow2-support-query-command.patch +Patch1042: 0042-tapdisk-support-new-cancel-command.patch +Patch1043: 0043-qcow2-support-cancel-command.patch +Patch1044: 0044-libqcow2-fix-abort-commit-without-crash.patch +Patch1045: 0045-feat-pass-error-details-up-to-tap-ctl.patch +Patch1046: 0046-fix-skip-cbtlog-disks-in-commit-related-operations.patch %description Blktap creates kernel block devices which realize I/O requests to @@ -68,15 +123,16 @@ source /opt/rh/devtoolset-11/enable echo -n %{version} > VERSION sh autogen.sh # The following can be used for leak tracing -#%%configure LDFLAGS="$LDFLAGS -Wl,-rpath=/lib64/citrix -lrt -static-liblsan" CFLAGS="$CFLAGS -Wno-stringop-truncation -fsanitize=leak -ggdb -fno-omit-frame-pointer" -#%%configure LDFLAGS="$LDFLAGS -Wl,-rpath=/lib64/citrix" CFLAGS="$CFLAGS -Wno-stringop-truncation -Wno-error=analyzer-malloc-leak -Wno-error=analyzer-use-after-free -Wno-error=analyzer-double-free -Wno-error=analyzer-null-dereference -fanalyzer" -%configure LDFLAGS="$LDFLAGS -Wl,-rpath=/lib64/citrix" CFLAGS="$CFLAGS -Wno-stringop-truncation" +#%%configure LDFLAGS="$LDFLAGS -lrt -static-liblsan" CFLAGS="$CFLAGS -Wno-stringop-truncation -fsanitize=leak -ggdb -fno-omit-frame-pointer" +#%%configure CFLAGS="$CFLAGS -Wno-stringop-truncation -Wno-error=analyzer-malloc-leak -Wno-error=analyzer-use-after-free -Wno-error=analyzer-double-free -Wno-error=analyzer-null-dereference -fanalyzer" +%configure CFLAGS="$CFLAGS -Wno-stringop-truncation" %{?_cov_wrap} make %{?coverage:GCOV=true} %check +source /opt/rh/devtoolset-11/enable make clean make check GCOV=true || (find mockatests -name \*.log -print -exec cat {} \; && false) -./collect-test-results.sh %{buildroot}/testresults +#./collect-test-results.sh %{buildroot}/testresults %install rm -rf %{buildroot} @@ -91,10 +147,6 @@ rm -f %{buildroot}%{_libdir}/*.la ## Remove static libraries; they should not be used by other packages rm -f %{buildroot}%{_libdir}/*.a -%triggerin -- mdadm -echo 'KERNEL=="td[a-z]*", GOTO="md_end"' > /etc/udev/rules.d/65-md-incremental.rules -cat /usr/lib/udev/rules.d/65-md-incremental.rules >> /etc/udev/rules.d/65-md-incremental.rules - %files %defattr(-,root,root,-) %docdir /usr/share/doc/%{name} @@ -104,6 +156,7 @@ cat /usr/lib/udev/rules.d/65-md-incremental.rules >> /etc/udev/rules.d/65-md-inc %{_bindir}/vhd-index %{_bindir}/tapback %{_bindir}/cpumond +%{_bindir}/qemu-img %{_sbindir}/cbt-util %{_sbindir}/lvm-util %{_sbindir}/tap-ctl @@ -147,19 +200,8 @@ fi # The posttrans invocation of ldconfig is needed because older # versions of blktap did not have ldconfig in their postun script. -%posttrans -p /sbin/ldconfig - -%{?_cov_results_package} - -%package testresults -Group: System/Hypervisor -Summary: test results for blktap package - -%description testresults -The package contains the build time test results for the blktap package - -%files testresults -/testresults +%posttrans +/sbin/ldconfig %package -n vhd-util-standalone Group: System/Hypervisor @@ -176,6 +218,82 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Wed Aug 19 2026 Anthoine Bourgeois - 3.55.5-9.4 +- Fix commit command with cbt enabled. + +* Fri Jul 03 2026 Mathieu Labourier - 3.55.5-9.3 +- Pass error details up to tap-ctl. + * The error printed by tap-ctl is now clearer and parsable for tools like SM. + * Also adds details relative to the tapdisk error that caused the issue (if any). + +* Thu Jun 18 2026 Anthoine Bourgeois - 3.55.5-9.2 +- Avoid concurrent access on vbd requests. +- Fix deadlock on NBD export. +- Fix use-after-free on VDI close. + +* Wed May 20 2026 Anthoine Bourgeois - 3.55.5-9.1 +- Sync with 3.55.5-9 +- Drop patch 0002-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch, upstream now +- *** Upstream changelog *** + * Thu Apr 09 2026 Mark Syms - 3.55.5-9 + - Remove old, obsolete, udev rule override. + * Wed Jan 28 2026 Mark Syms - 3.55.5-8 + - Prevent segfault of vhd-util scan on VHD with corrupt footer + * Thu Aug 28 2025 Mark Syms - 3.55.5-7 + - CA-416464: return BLKIF_RSP_EOPNOTSUPP for EOPNOTSUPP + +* Thu Apr 30 2026 Anthoine Bourgeois - 3.55.5-6.7 +- Fix tapdisk crash and prevent infinite coalesce + +* Fri Apr 24 2026 Anthoine Bourgeois - 3.55.5-6.6 +- Add GPLv2 to the RPM license list as libqcow2 use this license + +* Thu Apr 09 2026 Philippe Coval - 3.55.5-6.5 +- Fix scriptlet to use udev rule aligned to mdadm + +* Tue Apr 07 2026 Damien Thenot - 3.55.5-6.4 +- Release of QCOW2 support + +* Thu Feb 26 2026 Mathieu Labourier - 3.55.5.6.3 +- Prevent segfault of vhd-util scan on VHD with corrupt footer + +* Fri Feb 13 2026 Philippe Coval - 3.55.5-6.2 +- Rebuild with openssl-3 + +* Mon Sep 08 2025 Anthoine Bourgeois - 3.55.5-6.1 +- Sync with 3.55.5-6 +- Revert openssl 3 dependency from XS specfile (no change compared to previous XCP-ng release) +- Drop patch 0002-CP-308382-fix-sign-conversion-in-coalesce.patch, upstream now +- Drop patch 0003-Fix-coalesced-size-conversion-in-vhd-util-coalesce.patch, upstream now +- *** Upstream changelog *** + * Thu Jul 31 2025 Mark Syms - 3.55.5-6 + - Fix coalesced size conversion in vhd-util-coalesce (CP-308382) + * Mon Jun 23 2025 Mark Syms - 3.55.5-5 + - Backport fix for CA-408175 + - CP-308382: fix sign conversion in coalesce + * Mon Apr 07 2025 Mark Syms - 3.55.5-4 + - CP-54256: log when reporting EOPNOTSUPP + * Fri Feb 21 2025 Deli Zhang - 3.55.5-3 + - CP-50273: Move CCM dependency to OpenSSL 3 + +* Thu Jul 31 2025 Ronan Abhamon - 3.55.5-2.3 +- Fix a bad integer conversion that interrupts valid coalesce calls on large VDIs +- Add 0002-CP-308382-fix-sign-conversion-in-coalesce.patch +- Add 0003-Fix-coalesced-size-conversion-in-vhd-util-coalesce.patch + +* Fri Jul 04 2025 Yann Dirson - 3.55.5-2.2 +- Drop useless autogen build-dep + +* Fri Mar 28 2025 Samuel Verschelde - 3.55.5-2.1 +- Sync with 3.55.5-2 +- Drop patch "Add an option to never resolve parent path when vhd-util query is +called" already included in upstream v3.55.5 +- *** Upstream changelog *** + * Tue Feb 11 2025 Mark Syms - 3.55.5-2 + - CA-404370: enable NBD client only after completing handshake + * Mon Jan 06 2025 Mark Syms - 3.55.5-1 + - Add an option to never resolve parent path when vhd-util query is called + * Tue Mar 04 2025 Samuel Verschelde - 3.55.4-1.1 - Sync with 3.55.4-1 - *** Upstream changelog ***