Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2227472/?format=api
{ "id": 2227472, "url": "http://patchwork.ozlabs.org/api/patches/2227472/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260423191958.1440-33-farosas@suse.de/", "project": { "id": 14, "url": "http://patchwork.ozlabs.org/api/projects/14/?format=api", "name": "QEMU Development", "link_name": "qemu-devel", "list_id": "qemu-devel.nongnu.org", "list_email": "qemu-devel@nongnu.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260423191958.1440-33-farosas@suse.de>", "list_archive_url": null, "date": "2026-04-23T19:19:46", "name": "[PULL,32/43] vmstate: Implement VMS_ARRAY_OF_POINTER_AUTO_ALLOC", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "197c15811969af8878ee3476c075cc684059b376", "submitter": { "id": 85343, "url": "http://patchwork.ozlabs.org/api/people/85343/?format=api", "name": "Fabiano Rosas", "email": "farosas@suse.de" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260423191958.1440-33-farosas@suse.de/mbox/", "series": [ { "id": 501236, "url": "http://patchwork.ozlabs.org/api/series/501236/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=501236", "date": "2026-04-23T19:19:14", "name": "[PULL,01/43] checkpatch: Allow spaces after all coroutine annotations", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/501236/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2227472/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2227472/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=suse.de header.i=@suse.de header.a=rsa-sha256\n header.s=susede2_rsa header.b=gGzSVAFh;\n\tdkim=pass header.d=suse.de header.i=@suse.de header.a=ed25519-sha256\n header.s=susede2_ed25519 header.b=HwQjmtMP;\n\tdkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de\n header.a=rsa-sha256 header.s=susede2_rsa header.b=gGzSVAFh;\n\tdkim=neutral header.d=suse.de header.i=@suse.de header.a=ed25519-sha256\n header.s=susede2_ed25519 header.b=HwQjmtMP;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists1p.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)", "smtp-out1.suse.de;\n dkim=pass header.d=suse.de header.s=susede2_rsa header.b=gGzSVAFh;\n dkim=pass header.d=suse.de header.s=susede2_ed25519 header.b=HwQjmtMP" ], "Received": [ "from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g1mHL4ZBQz1y2d\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 24 Apr 2026 05:24:14 +1000 (AEST)", "from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists1p.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1wFzdL-0006RU-0c; Thu, 23 Apr 2026 15:22:43 -0400", "from eggs.gnu.org ([2001:470:142:3::10])\n by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <farosas@suse.de>) id 1wFzct-00050P-Bj\n for qemu-devel@nongnu.org; Thu, 23 Apr 2026 15:22:16 -0400", "from smtp-out1.suse.de ([2a07:de40:b251:101:10:150:64:1])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128)\n (Exim 4.90_1) (envelope-from <farosas@suse.de>) id 1wFzcp-0002PF-WC\n for qemu-devel@nongnu.org; Thu, 23 Apr 2026 15:22:15 -0400", "from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org\n [IPv6:2a07:de40:b281:104:10:150:64:97])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest\n SHA256)\n (No client certificate requested)\n by smtp-out1.suse.de (Postfix) with ESMTPS id 905376A8E3;\n Thu, 23 Apr 2026 19:20:50 +0000 (UTC)", "from imap1.dmz-prg2.suse.org (localhost [127.0.0.1])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest\n SHA256)\n (No client certificate requested)\n by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 52646593A3;\n Thu, 23 Apr 2026 19:20:49 +0000 (UTC)", "from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167])\n by imap1.dmz-prg2.suse.org with ESMTPSA id sHfaBxFx6mlJCwAAD6G6ig\n (envelope-from <farosas@suse.de>); Thu, 23 Apr 2026 19:20:49 +0000" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de;\n s=susede2_rsa;\n t=1776972050;\n h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc:\n mime-version:mime-version:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=CPuRrUsaEYWsuQOW5Bg2WU0kkK7920z9rqtJrvE9AB8=;\n b=gGzSVAFhRR5ZQSTYuNgsVFvd709hVdKH2dtEGhtGFwQS4V3Ze7Vgq9Yfx9icaOdesoRi4D\n WIjeDwO1OYBUlp9F3GM917NLT7b0xjygT99svQCHYB9ioO21MMfcxyq6GLHhAyhf/I8Ljq\n SS9pQUFSpHnpXdUnmHN00/5vQHpSS8s=", "v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de;\n s=susede2_ed25519; t=1776972050;\n h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc:\n mime-version:mime-version:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=CPuRrUsaEYWsuQOW5Bg2WU0kkK7920z9rqtJrvE9AB8=;\n b=HwQjmtMPEhCf4PPx44uQpS4hu/6KnBNPZuEB+hzAlCs9JwlQQvc15smDJJJF/F2HoYAYWc\n 9DdBHeU2+IUPzHAA==", "v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de;\n s=susede2_rsa;\n t=1776972050;\n h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc:\n mime-version:mime-version:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=CPuRrUsaEYWsuQOW5Bg2WU0kkK7920z9rqtJrvE9AB8=;\n b=gGzSVAFhRR5ZQSTYuNgsVFvd709hVdKH2dtEGhtGFwQS4V3Ze7Vgq9Yfx9icaOdesoRi4D\n WIjeDwO1OYBUlp9F3GM917NLT7b0xjygT99svQCHYB9ioO21MMfcxyq6GLHhAyhf/I8Ljq\n SS9pQUFSpHnpXdUnmHN00/5vQHpSS8s=", "v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de;\n s=susede2_ed25519; t=1776972050;\n h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc:\n mime-version:mime-version:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=CPuRrUsaEYWsuQOW5Bg2WU0kkK7920z9rqtJrvE9AB8=;\n b=HwQjmtMPEhCf4PPx44uQpS4hu/6KnBNPZuEB+hzAlCs9JwlQQvc15smDJJJF/F2HoYAYWc\n 9DdBHeU2+IUPzHAA==" ], "From": "Fabiano Rosas <farosas@suse.de>", "To": "qemu-devel@nongnu.org", "Cc": "Peter Xu <peterx@redhat.com>,\n Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>,\n Juraj Marcin <jmarcin@redhat.com>", "Subject": "[PULL 32/43] vmstate: Implement VMS_ARRAY_OF_POINTER_AUTO_ALLOC", "Date": "Thu, 23 Apr 2026 16:19:46 -0300", "Message-ID": "<20260423191958.1440-33-farosas@suse.de>", "X-Mailer": "git-send-email 2.51.0", "In-Reply-To": "<20260423191958.1440-1-farosas@suse.de>", "References": "<20260423191958.1440-1-farosas@suse.de>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-Rspamd-Action": "no action", "X-Rspamd-Server": "rspamd2.dmz-prg2.suse.org", "X-Spamd-Result": "default: False [-3.01 / 50.00]; BAYES_HAM(-3.00)[100.00%];\n MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000];\n R_MISSING_CHARSET(0.50)[];\n R_DKIM_ALLOW(-0.20)[suse.de:s=susede2_rsa,suse.de:s=susede2_ed25519];\n NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain];\n MX_GOOD(-0.01)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; ARC_NA(0.00)[];\n DKIM_SIGNED(0.00)[suse.de:s=susede2_rsa,suse.de:s=susede2_ed25519];\n TO_DN_SOME(0.00)[]; MIME_TRACE(0.00)[0:+];\n FUZZY_RATELIMITED(0.00)[rspamd.com];\n RBL_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[2a07:de40:b281:104:10:150:64:97:from];\n RCVD_TLS_ALL(0.00)[]; DKIM_TRACE(0.00)[suse.de:+];\n RCVD_COUNT_TWO(0.00)[2]; FROM_EQ_ENVFROM(0.00)[];\n FROM_HAS_DN(0.00)[];\n SPAMHAUS_XBL(0.00)[2a07:de40:b281:104:10:150:64:97:from];\n RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[2a07:de40:b281:106:10:150:64:167:received];\n R_RATELIMIT(0.00)[to_ip_from(RLjefarcnsteocuw7wptuakujg)];\n RCVD_VIA_SMTP_AUTH(0.00)[]; RCPT_COUNT_THREE(0.00)[4];\n DBL_BLOCKED_OPENRESOLVER(0.00)[suse.de:dkim, suse.de:mid, suse.de:email,\n imap1.dmz-prg2.suse.org:helo, imap1.dmz-prg2.suse.org:rdns]", "X-Rspamd-Queue-Id": "905376A8E3", "X-Spam-Score": "-3.01", "Received-SPF": "pass client-ip=2a07:de40:b251:101:10:150:64:1;\n envelope-from=farosas@suse.de; helo=smtp-out1.suse.de", "X-Spam_score_int": "-20", "X-Spam_score": "-2.1", "X-Spam_bar": "--", "X-Spam_report": "(-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1,\n DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001,\n SPF_PASS=-0.001 autolearn=ham autolearn_force=no", "X-Spam_action": "no action", "X-BeenThere": "qemu-devel@nongnu.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "qemu development <qemu-devel.nongnu.org>", "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>", "List-Archive": "<https://lists.nongnu.org/archive/html/qemu-devel>", "List-Post": "<mailto:qemu-devel@nongnu.org>", "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>", "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>", "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org", "Sender": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org" }, "content": "From: Peter Xu <peterx@redhat.com>\n\nIntroduce a new flag, VMS_ARRAY_OF_POINTER_AUTO_ALLOC, for VMSD field. It\nmust be used together with VMS_ARRAY_OF_POINTER.\n\nIt can be used to allow migration of an array of pointers where the\npointers may point to NULLs.\n\nNote that we used to allow migration of a NULL pointer within an array that\nis being migrated. That corresponds to the code around vmstate_info_nullptr\nwhere we may get/put one byte showing that the element of an array is NULL.\n\nThat usage is fine but very limited, it's because even if it will migrate a\nNULL pointer with a marker, it still works in a way that both src and dest\nQEMUs must know exactly which elements of the array are non-NULL, so\ninstead of dynamically loading an array (which can have NULL pointers), it\nactually only verifies the known NULL pointers are still NULL pointers\nafter migration.\n\nAlso, in that case since dest QEMU knows exactly which element is NULL,\nwhich is not NULL, dest QEMU's device code will manage all allocations for\nthe elements before invoking vmstate_load_vmsd().\n\nThat's not enough per evolving needs of new device states that may want to\nprovide real dynamic array of pointers, like what Alexander proposed here\nwith the NVMe device migration:\n\nhttps://lore.kernel.org/r/20260317102708.126725-1-alexander@mihalicyn.com\n\nThis patch is an alternative approach to address the problem.\n\nAlong with the flag, introduce two new macros:\n\n VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT{8|32}_ALLOC()\n\nWhich will be used very soon in the NVMe series.\n\nReviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>\nTested-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>\nSigned-off-by: Peter Xu <peterx@redhat.com>\nReviewed-by: Juraj Marcin <jmarcin@redhat.com>\nLink: https://lore.kernel.org/qemu-devel/20260401202844.673494-10-peterx@redhat.com\nSigned-off-by: Fabiano Rosas <farosas@suse.de>\n---\n include/migration/vmstate.h | 51 ++++++++++++-\n migration/savevm.c | 27 ++++++-\n migration/vmstate.c | 145 ++++++++++++++++++++++++++++++------\n 3 files changed, 199 insertions(+), 24 deletions(-)", "diff": "diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h\nindex 492069b8d2..28e3640e60 100644\n--- a/include/migration/vmstate.h\n+++ b/include/migration/vmstate.h\n@@ -161,8 +161,21 @@ enum VMStateFlags {\n * structure we are referencing to use. */\n VMS_VSTRUCT = 0x8000,\n \n+ /*\n+ * This is a sub-flag for VMS_ARRAY_OF_POINTER. When this flag is set,\n+ * VMS_ARRAY_OF_POINTER must also be set. When set, it means array\n+ * elements can contain either valid or NULL pointers, vmstate core\n+ * will be responsible for synchronizing the pointer status, providing\n+ * proper memory allocations on the pointer when it is populated on the\n+ * source QEMU. It also means the user of the field must make sure all\n+ * the elements in the array are NULL pointers before loading. This\n+ * should also work with VMS_ALLOC when the array itself also needs to\n+ * be allocated.\n+ */\n+ VMS_ARRAY_OF_POINTER_AUTO_ALLOC = 0x10000,\n+\n /* Marker for end of list */\n- VMS_END = 0x10000\n+ VMS_END = 0x20000,\n };\n \n typedef enum {\n@@ -580,6 +593,42 @@ extern const VMStateInfo vmstate_info_qlist;\n .offset = vmstate_offset_array(_s, _f, _type*, _n), \\\n }\n \n+/*\n+ * For migrating a dynamically allocated uint{8,32}-indexed array of\n+ * pointers to structures (with NULL entries and with auto memory\n+ * allocation).\n+ *\n+ * _type: type of structure pointed to\n+ * _vmsd: VMSD for structure _type (when VMS_STRUCT is set)\n+ * _info: VMStateInfo for _type (when VMS_STRUCT is not set)\n+ * start: size of (_type) pointed to (for auto memory allocation)\n+ */\n+#define VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT8_ALLOC(\\\n+ _field, _state, _field_num, _version, _vmsd, _type) { \\\n+ .name = (stringify(_field)), \\\n+ .version_id = (_version), \\\n+ .num_offset = vmstate_offset_value(_state, _field_num, uint8_t), \\\n+ .vmsd = &(_vmsd), \\\n+ .size = sizeof(_type), \\\n+ .flags = VMS_POINTER | VMS_VARRAY_UINT8 | \\\n+ VMS_ARRAY_OF_POINTER | VMS_STRUCT | \\\n+ VMS_ARRAY_OF_POINTER_AUTO_ALLOC, \\\n+ .offset = vmstate_offset_pointer(_state, _field, _type *), \\\n+}\n+\n+#define VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT32_ALLOC(\\\n+ _field, _state, _field_num, _version, _vmsd, _type) { \\\n+ .name = (stringify(_field)), \\\n+ .version_id = (_version), \\\n+ .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \\\n+ .vmsd = &(_vmsd), \\\n+ .size = sizeof(_type), \\\n+ .flags = VMS_POINTER | VMS_VARRAY_UINT32 | \\\n+ VMS_ARRAY_OF_POINTER | VMS_STRUCT | \\\n+ VMS_ARRAY_OF_POINTER_AUTO_ALLOC, \\\n+ .offset = vmstate_offset_pointer(_state, _field, _type *), \\\n+}\n+\n #define VMSTATE_VARRAY_OF_POINTER_UINT32(_field, _state, _field_num, _version, _info, _type) { \\\n .name = (stringify(_field)), \\\n .version_id = (_version), \\\ndiff --git a/migration/savevm.c b/migration/savevm.c\nindex f5a6fd0c66..765df8ce2d 100644\n--- a/migration/savevm.c\n+++ b/migration/savevm.c\n@@ -869,8 +869,33 @@ static void vmstate_check(const VMStateDescription *vmsd)\n if (field) {\n while (field->name) {\n if (field->flags & VMS_ARRAY_OF_POINTER) {\n- assert(field->size == 0);\n+ if (field->flags & VMS_ARRAY_OF_POINTER_AUTO_ALLOC) {\n+ /*\n+ * Size must be provided because dest QEMU needs that\n+ * info to know what to allocate\n+ */\n+ assert(field->size || field->size_offset);\n+ } else {\n+ /*\n+ * Otherwise size info isn't useful (because it's\n+ * always the size of host pointer), detect accidental\n+ * setup of sizes in this case.\n+ */\n+ assert(field->size == 0 && field->size_offset == 0);\n+ }\n+ /*\n+ * VMS_ARRAY_OF_POINTER must be used only together with one\n+ * of VMS_(V)ARRAY* flags.\n+ */\n+ assert(field->flags & (VMS_ARRAY | VMS_VARRAY_INT32 |\n+ VMS_VARRAY_UINT16 | VMS_VARRAY_UINT8 |\n+ VMS_VARRAY_UINT32));\n }\n+\n+ if (field->flags & VMS_ARRAY_OF_POINTER_AUTO_ALLOC) {\n+ assert(field->flags & VMS_ARRAY_OF_POINTER);\n+ }\n+\n if (field->flags & (VMS_STRUCT | VMS_VSTRUCT)) {\n /* Recurse to sub structures */\n vmstate_check(field->vmsd);\ndiff --git a/migration/vmstate.c b/migration/vmstate.c\nindex 47812eb882..de2ad822e8 100644\n--- a/migration/vmstate.c\n+++ b/migration/vmstate.c\n@@ -153,6 +153,12 @@ static bool vmstate_ptr_marker_load(QEMUFile *f, bool *load_field,\n return true;\n }\n \n+ if (byte == VMS_MARKER_PTR_VALID) {\n+ /* We need to load the field right after the marker */\n+ *load_field = true;\n+ return true;\n+ }\n+\n error_setg(errp, \"Unexpected ptr marker: %d\", byte);\n return false;\n }\n@@ -234,6 +240,76 @@ static bool vmstate_post_load(const VMStateDescription *vmsd,\n return true;\n }\n \n+/*\n+ * Try to prepare loading the next element, the object pointer to be put\n+ * into @next_elem. When @next_elem is NULL, it means we should skip\n+ * loading this element.\n+ *\n+ * Returns false for errors, in which case *errp will be set, migration\n+ * must be aborted.\n+ */\n+static bool vmstate_load_next(QEMUFile *f, const VMStateField *field,\n+ void *first_elem, void **next_elem,\n+ int size, int i, Error **errp)\n+{\n+ bool auto_alloc = field->flags & VMS_ARRAY_OF_POINTER_AUTO_ALLOC;\n+ void *ptr = first_elem + size * i, **pptr;\n+ bool load_field;\n+\n+ if (!(field->flags & VMS_ARRAY_OF_POINTER)) {\n+ /* Simplest case, no pointer involved */\n+ *next_elem = ptr;\n+ return true;\n+ }\n+\n+ /*\n+ * We're loading an array of pointers, switch to use pptr to make it\n+ * easier to read later\n+ */\n+ pptr = (void **)ptr;\n+\n+ /*\n+ * If auto_alloc is on, making sure the user provided an array of NULL\n+ * pointers to start with\n+ */\n+ assert(!auto_alloc || *pptr == NULL);\n+\n+ /*\n+ * When pointer is null, we must expect a ptr marker first. Use cases:\n+ *\n+ * (1) _AUTO_ALLOC implies a ptr marker will always exist, or,\n+ *\n+ * (2) the element on destination is NULL, which expects the src to send a\n+ * NULL-only marker.\n+ *\n+ * Here, checking against a NULL pointer will work for both.\n+ */\n+ if (!*pptr) {\n+ if (!vmstate_ptr_marker_load(f, &load_field, errp)) {\n+ trace_vmstate_load_field_error(field->name, -EINVAL);\n+ return false;\n+ }\n+\n+ /*\n+ * If loading is needed, do pre-allocation first (otherwise keeping\n+ * *pptr==NULL to imply a skip below)\n+ */\n+ if (load_field) {\n+ /* Only applies when auto_alloc=on on the field */\n+ assert(auto_alloc);\n+ /*\n+ * NOTE: do not use vmstate_size() here, because we need the\n+ * object size, not entry size of the array.\n+ */\n+ *pptr = g_malloc0(field->size);\n+ }\n+ }\n+\n+ /* Move the cursor to the next element for loading */\n+ *next_elem = *pptr;\n+ return true;\n+}\n+\n bool vmstate_load_vmsd(QEMUFile *f, const VMStateDescription *vmsd,\n void *opaque, int version_id, Error **errp)\n {\n@@ -279,27 +355,22 @@ bool vmstate_load_vmsd(QEMUFile *f, const VMStateDescription *vmsd,\n }\n \n for (i = 0; i < n_elems; i++) {\n- /* If we will process the load of field? */\n- bool load_field = true;\n- bool ok = true;\n- void *curr_elem = first_elem + size * i;\n+ void *curr_elem;\n+ bool ok;\n \n- if (field->flags & VMS_ARRAY_OF_POINTER) {\n- curr_elem = *(void **)curr_elem;\n- if (!curr_elem) {\n- /* Read the marker instead of VMSD itself */\n- if (!vmstate_ptr_marker_load(f, &load_field, errp)) {\n- trace_vmstate_load_field_error(field->name,\n- -EINVAL);\n- return false;\n- }\n- }\n+ ok = vmstate_load_next(f, field, first_elem, &curr_elem,\n+ size, i, errp);\n+ if (!ok) {\n+ return false;\n }\n \n- if (load_field) {\n- ok = vmstate_load_field(f, curr_elem, size, field, errp);\n+ if (!curr_elem) {\n+ /* Implies a skip */\n+ continue;\n }\n \n+ ok = vmstate_load_field(f, curr_elem, size, field, errp);\n+\n if (ok) {\n int ret = qemu_file_get_error(f);\n if (ret < 0) {\n@@ -397,6 +468,16 @@ static bool vmsd_can_compress(const VMStateField *field)\n return false;\n }\n \n+ if (field->flags & VMS_ARRAY_OF_POINTER_AUTO_ALLOC) {\n+ /*\n+ * This may involve two VMSD fields to be saved, one for the\n+ * marker to show if the pointer is NULL, followed by the real\n+ * vmstate object. To make it simple at least for now, skip\n+ * compression for this one.\n+ */\n+ return false;\n+ }\n+\n if (field->flags & VMS_STRUCT) {\n const VMStateField *sfield = field->vmsd->fields;\n while (sfield->name) {\n@@ -583,6 +664,12 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,\n int size = vmstate_size(opaque, field);\n JSONWriter *vmdesc_loop = vmdesc;\n bool is_prev_null = false;\n+ /*\n+ * When this is enabled, it means we will always push a ptr\n+ * marker first for each element saying if it's populated.\n+ */\n+ bool use_dynamic_array =\n+ field->flags & VMS_ARRAY_OF_POINTER_AUTO_ALLOC;\n \n trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems);\n if (field->flags & VMS_POINTER) {\n@@ -603,14 +690,9 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,\n }\n \n is_null = !curr_elem && size;\n- use_marker_field = is_null;\n+ use_marker_field = use_dynamic_array || is_null;\n \n if (use_marker_field) {\n- /*\n- * If null pointer found (which should only happen in\n- * an array of pointers), use null placeholder and do\n- * not follow.\n- */\n inner_field = vmsd_create_ptr_marker_field(field);\n } else {\n inner_field = field;\n@@ -657,6 +739,25 @@ static bool vmstate_save_vmsd_v(QEMUFile *f, const VMStateDescription *vmsd,\n goto out;\n }\n \n+ /*\n+ * If we're using dynamic array and the element is\n+ * populated, save the real object right after the marker.\n+ */\n+ if (use_dynamic_array && curr_elem) {\n+ /*\n+ * NOTE: do not use vmstate_size() here because we want\n+ * to save the real VMSD object now.\n+ */\n+ ok = vmstate_save_field_with_vmdesc(f, curr_elem,\n+ field->size, vmsd,\n+ field, vmdesc_loop,\n+ i, max_elems, errp);\n+\n+ if (!ok) {\n+ goto out;\n+ }\n+ }\n+\n /* Compressed arrays only care about the first element */\n if (vmdesc_loop && max_elems > 1) {\n vmdesc_loop = NULL;\n", "prefixes": [ "PULL", "32/43" ] }