From b65310ab34cbada47a5570c94d6f8f5efd39d171 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 4 Jun 2021 14:09:15 +0200 Subject: [PATCH 01/45] vnc: avoid deprecation warnings for SASL on OS X Apple has deprecated sasl.h functions in OS X 10.11. Therefore, all files that use SASL API need to disable -Wdeprecated-declarations. Remove the only use that is outside vnc-auth-sasl.c and add the relevant #pragma GCC diagnostic there. Signed-off-by: Paolo Bonzini Message-Id: <20210604120915.286195-1-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- ui/vnc-auth-sasl.c | 20 ++++++++++++++++++++ ui/vnc-auth-sasl.h | 1 + ui/vnc.c | 10 ++-------- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index df7dc08e9fc5..47fdae5b215b 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -28,10 +28,30 @@ #include "vnc.h" #include "trace.h" +/* + * Apple has deprecated sasl.h functions in OS X 10.11. Therefore, + * files that use SASL API need to disable -Wdeprecated-declarations. + */ +#ifdef CONFIG_DARWIN +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + /* Max amount of data we send/recv for SASL steps to prevent DOS */ #define SASL_DATA_MAX_LEN (1024 * 1024) +bool vnc_sasl_server_init(Error **errp) +{ + int saslErr = sasl_server_init(NULL, "qemu"); + + if (saslErr != SASL_OK) { + error_setg(errp, "Failed to initialize SASL auth: %s", + sasl_errstring(saslErr, NULL, NULL)); + return false; + } + return true; +} + void vnc_sasl_client_cleanup(VncState *vs) { if (vs->sasl.conn) { diff --git a/ui/vnc-auth-sasl.h b/ui/vnc-auth-sasl.h index 1bfb86c6f59c..367b8672cc2b 100644 --- a/ui/vnc-auth-sasl.h +++ b/ui/vnc-auth-sasl.h @@ -63,6 +63,7 @@ struct VncDisplaySASL { char *authzid; }; +bool vnc_sasl_server_init(Error **errp); void vnc_sasl_client_cleanup(VncState *vs); size_t vnc_client_read_sasl(VncState *vs); diff --git a/ui/vnc.c b/ui/vnc.c index b3d4d7b9a5f5..f0a1550d58c1 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -4154,14 +4154,8 @@ void vnc_display_open(const char *id, Error **errp) trace_vnc_auth_init(vd, 1, vd->ws_auth, vd->ws_subauth); #ifdef CONFIG_VNC_SASL - if (sasl) { - int saslErr = sasl_server_init(NULL, "qemu"); - - if (saslErr != SASL_OK) { - error_setg(errp, "Failed to initialize SASL auth: %s", - sasl_errstring(saslErr, NULL, NULL)); - goto fail; - } + if (sasl && !vnc_sasl_server_init(errp)) { + goto fail; } #endif vd->lock_key_sync = lock_key_sync; From 38f71349c7c4969bc14da4da1c70b8cc4078d596 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 10 Jun 2021 16:47:41 +0800 Subject: [PATCH 02/45] vl: Fix an assert failure in error path Based on the description of error_setg(), the local variable err in qemu_maybe_daemonize() should be initialized to NULL. Without fix, the uninitialized *errp triggers assert failure which doesn't show much valuable information. Before the fix: qemu-system-x86_64: ../util/error.c:59: error_setv: Assertion `*errp == NULL' failed. After fix: qemu-system-x86_64: cannot create PID file: Cannot open pid file: Permission denied Signed-off-by: Zhenzhong Duan Message-Id: <20210610084741.456260-1-zhenzhong.duan@intel.com> Cc: qemu-stable@nongnu.org Fixes: 0546c0609c ("vl: split various early command line options to a separate function", 2020-12-10) Signed-off-by: Paolo Bonzini --- softmmu/vl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/softmmu/vl.c b/softmmu/vl.c index 326c1e908008..feb4d201f30f 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -2522,7 +2522,7 @@ static void qemu_process_help_options(void) static void qemu_maybe_daemonize(const char *pid_file) { - Error *err; + Error *err = NULL; os_daemonize(); rcu_disable_atfork(); From e7d85d955a7a3405934a104f35228aae1d338a6d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 9 Jun 2021 14:34:35 +0200 Subject: [PATCH 03/45] qemu-config: use qemu_opts_from_qdict Using qemu_opts_absorb_qdict, and then checking for any leftover options, is redundant because there is already a function that does the same, qemu_opts_from_qdict. qemu_opts_from_qdict consumes the whole dictionary and therefore can just return an error message if an option fails to validate. This also fixes a bug, because the "id" entry was retrieved in qemu_config_do_parse and then left there by qemu_opts_absorb_qdict. As a result, it was reported as an unrecognized option. Reported-by: Markus Armbruster Reviewed-by: Markus Armbruster Fixes: 3770141139 ("qemu-config: parse configuration files to a QDict") Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- util/qemu-config.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/util/qemu-config.c b/util/qemu-config.c index 374f3bc4600c..84ee6dc4ea58 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -429,29 +429,14 @@ static int qemu_config_foreach(FILE *fp, QEMUConfigCB *cb, void *opaque, void qemu_config_do_parse(const char *group, QDict *qdict, void *opaque, Error **errp) { QemuOptsList **lists = opaque; - const char *id = qdict_get_try_str(qdict, "id"); QemuOptsList *list; - QemuOpts *opts; - const QDictEntry *unrecognized; list = find_list(lists, group, errp); if (!list) { return; } - opts = qemu_opts_create(list, id, 1, errp); - if (!opts) { - return; - } - if (!qemu_opts_absorb_qdict(opts, qdict, errp)) { - qemu_opts_del(opts); - return; - } - unrecognized = qdict_first(qdict); - if (unrecognized) { - error_setg(errp, QERR_INVALID_PARAMETER, unrecognized->key); - qemu_opts_del(opts); - } + qemu_opts_from_qdict(list, qdict, errp); } int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp) From 06b80795ee25d23e69747b2cb435003c6db2a6ab Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Thu, 17 Dec 2020 18:56:12 +0200 Subject: [PATCH 04/45] block/scsi: correctly emulate the VPD block limits page When the device doesn't support the VPD block limits page, we emulate it even for SCSI passthrough. As a part of the emulation we need to add it to the 'Supported VPD Pages' The code that does this adds it to the page, but it doesn't increase the length of the data to be copied to the guest, thus the guest never sees the VPD block limits page as supported. Bump the transfer size by 1 in this case. Signed-off-by: Maxim Levitsky Reviewed-by: Max Reitz Message-Id: <20201217165612.942849-6-mlevitsk@redhat.com> Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-generic.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 98c30c5d5c3d..40e039864fc8 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -147,7 +147,7 @@ static int execute_command(BlockBackend *blk, return 0; } -static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) +static int scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s, int len) { uint8_t page, page_idx; @@ -213,8 +213,13 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) r->buf[page_idx] = 0xb0; } stw_be_p(r->buf + 2, lduw_be_p(r->buf + 2) + 1); + + if (len < r->buflen) { + len++; + } } } + return len; } static int scsi_generic_emulate_block_limits(SCSIGenericReq *r, SCSIDevice *s) @@ -332,7 +337,7 @@ static void scsi_read_complete(void * opaque, int ret) } } if (r->req.cmd.buf[0] == INQUIRY) { - scsi_handle_inquiry_reply(r, s); + len = scsi_handle_inquiry_reply(r, s, len); } req_complete: From 6e1da3d305499d3907f3c7f6638243e2e09b5085 Mon Sep 17 00:00:00 2001 From: Peng Liang Date: Thu, 10 Jun 2021 21:17:29 +0800 Subject: [PATCH 05/45] runstate: Initialize Error * to NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on the description of error_setg(), the local variable err in qemu_init_subsystems() should be initialized to NULL. Fixes: efd7ab22fb ("vl: extract qemu_init_subsystems") Cc: qemu-stable@nongnu.org Signed-off-by: Peng Liang Message-Id: <20210610131729.3906565-1-liangpeng10@huawei.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- softmmu/runstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/softmmu/runstate.c b/softmmu/runstate.c index 15640572c090..10d9b7365aa7 100644 --- a/softmmu/runstate.c +++ b/softmmu/runstate.c @@ -747,7 +747,7 @@ static void qemu_run_exit_notifiers(void) void qemu_init_subsystems(void) { - Error *err; + Error *err = NULL; os_set_line_buffering(); From cf1a7a9b3721544aaa3e43d111eb383c30d71a62 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 18 May 2021 22:25:10 +0100 Subject: [PATCH 06/45] esp: only assert INTR_DC interrupt flag if selection fails The datasheet sequence tables confirm that when a target selection fails, only the INTR_DC interrupt flag should be asserted. Signed-off-by: Mark Cave-Ayland Fixes: cf47a41e05 ("esp: latch individual bits in ESP_RINTR register") Message-Id: <20210518212511.21688-2-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini --- hw/scsi/esp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index b668acef82df..000e45a62454 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -213,7 +213,7 @@ static int esp_select(ESPState *s) if (!s->current_dev) { /* No such drive */ s->rregs[ESP_RSTAT] = 0; - s->rregs[ESP_RINTR] |= INTR_DC; + s->rregs[ESP_RINTR] = INTR_DC; s->rregs[ESP_RSEQ] = SEQ_0; esp_raise_irq(s); return -1; From af947a3d853a235943681a00f07f3081f5143cc3 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 18 May 2021 22:25:11 +0100 Subject: [PATCH 07/45] esp: only set ESP_RSEQ at the start of the select sequence When processing a command to select a target and send a CDB, the ESP device maintains a sequence step register so that if an error occurs the host can determine which part of the selection/CDB submission sequence failed. The old Linux 2.6 driver is really pedantic here: it checks the sequence step register even if a command succeeds and complains loudly on the console if the sequence step register doesn't match the expected bus phase and interrupt flags. This reason this mismatch occurs is because the ESP emulation currently doesn't update the bus phase until the next TI (Transfer Information) command and so the cleared sequence step register is considered invalid for the stale bus phase. Normally this isn't an issue as the host only checks the sequence step register if an error occurs but the old Linux 2.6 driver does this in several places causing a large stream of "esp0: STEP_ASEL for tgt 0" messages to appear on the console during the boot process. Fix this by not clearing the sequence step register when reading the interrupt register and clearing the DMA status, so the guest sees a valid sequence step and bus phase combination at the end of the command phase. No other change is required since the sequence step register is correctly updated throughout the selection/CDB submission sequence once one of the select commands is issued. Signed-off-by: Mark Cave-Ayland Fixes: 1b9e48a5bd ("esp: implement non-DMA transfers in PDMA mode") Message-Id: <20210518212511.21688-3-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini --- hw/scsi/esp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 000e45a62454..18d4d5639207 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -481,7 +481,6 @@ static void esp_dma_done(ESPState *s) { s->rregs[ESP_RSTAT] |= STAT_TC; s->rregs[ESP_RINTR] |= INTR_BS; - s->rregs[ESP_RSEQ] = 0; s->rregs[ESP_RFLAGS] = 0; esp_set_tc(s, 0); esp_raise_irq(s); @@ -917,7 +916,15 @@ uint64_t esp_reg_read(ESPState *s, uint32_t saddr) val = s->rregs[ESP_RINTR]; s->rregs[ESP_RINTR] = 0; s->rregs[ESP_RSTAT] &= ~STAT_TC; - s->rregs[ESP_RSEQ] = SEQ_0; + /* + * According to the datasheet ESP_RSEQ should be cleared, but as the + * emulation currently defers information transfers to the next TI + * command leave it for now so that pedantic guests such as the old + * Linux 2.6 driver see the correct flags before the next SCSI phase + * transition. + * + * s->rregs[ESP_RSEQ] = SEQ_0; + */ esp_lower_irq(s); break; case ESP_TCHI: From 880d3089f1c667d7c84730ba9e9a2518220f7caf Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 19 May 2021 11:07:59 +0100 Subject: [PATCH 08/45] esp: allow non-DMA callback in esp_transfer_data() initial transfer The current implementation only resumes DMA transfers when incoming data is received from the target device, but this is also required for non-DMA transfers with the next set of non-DMA changes. Rather than duplicate the DMA/non-DMA dispatch logic in the initial transfer section, update the code so that the initial transfer section can just fallthrough to the main DMA/non-DMA dispatch logic. Signed-off-by: Mark Cave-Ayland Message-Id: <20210519100803.10293-2-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini --- hw/scsi/esp.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 18d4d5639207..50757e926479 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -803,16 +803,6 @@ void esp_transfer_data(SCSIRequest *req, uint32_t len) s->rregs[ESP_RSTAT] |= STAT_TC; s->rregs[ESP_RINTR] |= INTR_BS; esp_raise_irq(s); - - /* - * If data is ready to transfer and the TI command has already - * been executed, start DMA immediately. Otherwise DMA will start - * when host sends the TI command - */ - if (s->ti_size && (s->rregs[ESP_CMD] == (CMD_TI | CMD_DMA))) { - esp_do_dma(s); - } - return; } if (s->ti_cmd == 0) { @@ -826,7 +816,7 @@ void esp_transfer_data(SCSIRequest *req, uint32_t len) return; } - if (s->ti_cmd & CMD_DMA) { + if (s->ti_cmd == (CMD_TI | CMD_DMA)) { if (dmalen) { esp_do_dma(s); } else if (s->ti_size <= 0) { @@ -837,7 +827,7 @@ void esp_transfer_data(SCSIRequest *req, uint32_t len) esp_dma_done(s); esp_lower_drq(s); } - } else { + } else if (s->ti_cmd == CMD_TI) { esp_do_nodma(s); } } From 6ef2cabc7c4231207cfbac326853c0242d9c4617 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 19 May 2021 11:08:00 +0100 Subject: [PATCH 09/45] esp: handle non-DMA transfers from the target one byte at a time The initial implementation of non-DMA transfers was based upon analysis of traces from the MacOS toolbox ROM for handling unaligned reads but missed one key aspect - during a non-DMA transfer from the target, the bus service interrupt should be raised for every single byte received from the bus and not just at either the end of the transfer or when the FIFO is full. Adjust the non-DMA code accordingly so that esp_do_nodma() is called for every byte received from the target. This also includes special handling for managing the change from DATA IN to STATUS phase as this needs to occur when the final byte is read out from the FIFO, and not at the end of the transfer of the last byte into the FIFO. Signed-off-by: Mark Cave-Ayland Message-Id: <20210519100803.10293-3-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini --- hw/scsi/esp.c | 72 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 50757e926479..a0dab319f2fb 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -739,20 +739,17 @@ static void esp_do_nodma(ESPState *s) s->async_len -= len; s->ti_size += len; } else { - len = MIN(s->ti_size, s->async_len); - len = MIN(len, fifo8_num_free(&s->fifo)); - fifo8_push_all(&s->fifo, s->async_buf, len); - s->async_buf += len; - s->async_len -= len; - s->ti_size -= len; + if (fifo8_is_empty(&s->fifo)) { + fifo8_push(&s->fifo, s->async_buf[0]); + s->async_buf++; + s->async_len--; + s->ti_size--; + } } if (s->async_len == 0) { scsi_req_continue(s->current_req); - - if (to_device || s->ti_size == 0) { - return; - } + return; } s->rregs[ESP_RINTR] |= INTR_BS; @@ -762,20 +759,37 @@ static void esp_do_nodma(ESPState *s) void esp_command_complete(SCSIRequest *req, size_t resid) { ESPState *s = req->hba_private; + int to_device = ((s->rregs[ESP_RSTAT] & 7) == STAT_DO); trace_esp_command_complete(); - if (s->ti_size != 0) { - trace_esp_command_complete_unexpected(); + + /* + * Non-DMA transfers from the target will leave the last byte in + * the FIFO so don't reset ti_size in this case + */ + if (s->dma || to_device) { + if (s->ti_size != 0) { + trace_esp_command_complete_unexpected(); + } + s->ti_size = 0; } - s->ti_size = 0; + s->async_len = 0; if (req->status) { trace_esp_command_complete_fail(); } s->status = req->status; - s->rregs[ESP_RSTAT] = STAT_ST; - esp_dma_done(s); - esp_lower_drq(s); + + /* + * If the transfer is finished, switch to status phase. For non-DMA + * transfers from the target the last byte is still in the FIFO + */ + if (s->ti_size == 0) { + s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST; + esp_dma_done(s); + esp_lower_drq(s); + } + if (s->current_req) { scsi_req_unref(s->current_req); s->current_req = NULL; @@ -894,6 +908,17 @@ uint64_t esp_reg_read(ESPState *s, uint32_t saddr) qemu_log_mask(LOG_UNIMP, "esp: PIO data read not implemented\n"); s->rregs[ESP_FIFO] = 0; } else { + if ((s->rregs[ESP_RSTAT] & 0x7) == STAT_DI) { + if (s->ti_size) { + esp_do_nodma(s); + } else { + /* + * The last byte of a non-DMA transfer has been read out + * of the FIFO so switch to status phase + */ + s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST; + } + } s->rregs[ESP_FIFO] = esp_fifo_pop(&s->fifo); } val = s->rregs[ESP_FIFO]; @@ -952,15 +977,18 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) case ESP_FIFO: if (s->do_cmd) { esp_fifo_push(&s->cmdfifo, val); + + /* + * If any unexpected message out/command phase data is + * transferred using non-DMA, raise the interrupt + */ + if (s->rregs[ESP_CMD] == CMD_TI) { + s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); + } } else { esp_fifo_push(&s->fifo, val); } - - /* Non-DMA transfers raise an interrupt after every byte */ - if (s->rregs[ESP_CMD] == CMD_TI) { - s->rregs[ESP_RINTR] |= INTR_FC | INTR_BS; - esp_raise_irq(s); - } break; case ESP_CMD: s->rregs[saddr] = val; From e62a959afd2b1a13b27dda9d03f10c7feb36aa9b Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 19 May 2021 11:08:01 +0100 Subject: [PATCH 10/45] esp: ensure PDMA write transfers are flushed from the FIFO to the target immediately After each PDMA write transfer the MacOS CDROM driver waits until the FIFO is empty (i.e. its contents have been written out to the SCSI bus) by polling the FIFO count register until it reads 0. This doesn't work with the current PDMA write implementation which waits until either the FIFO is full or the transfer is complete before invoking the PDMA callback to process the FIFO contents. Change the PDMA write transfer logic so that the PDMA callback is invoked after each PDMA write to transfer the FIFO contents to the target buffer immediately, and hence avoid getting stuck in the FIFO count register polling loop. Signed-off-by: Mark Cave-Ayland Message-Id: <20210519100803.10293-4-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini --- hw/scsi/esp.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index a0dab319f2fb..8e314ef156d2 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -338,9 +338,9 @@ static void do_cmd(ESPState *s) static void satn_pdma_cb(ESPState *s) { - s->do_cmd = 0; - if (!fifo8_is_empty(&s->cmdfifo)) { + if (!esp_get_tc(s) && !fifo8_is_empty(&s->cmdfifo)) { s->cmdfifo_cdb_offset = 1; + s->do_cmd = 0; do_cmd(s); } } @@ -369,12 +369,9 @@ static void handle_satn(ESPState *s) static void s_without_satn_pdma_cb(ESPState *s) { - uint32_t len; - - s->do_cmd = 0; - len = fifo8_num_used(&s->cmdfifo); - if (len) { + if (!esp_get_tc(s) && !fifo8_is_empty(&s->cmdfifo)) { s->cmdfifo_cdb_offset = 0; + s->do_cmd = 0; do_busid_cmd(s, 0); } } @@ -403,8 +400,7 @@ static void handle_s_without_atn(ESPState *s) static void satn_stop_pdma_cb(ESPState *s) { - s->do_cmd = 0; - if (!fifo8_is_empty(&s->cmdfifo)) { + if (!esp_get_tc(s) && !fifo8_is_empty(&s->cmdfifo)) { trace_esp_handle_satn_stop(fifo8_num_used(&s->cmdfifo)); s->do_cmd = 1; s->cmdfifo_cdb_offset = 1; @@ -493,6 +489,11 @@ static void do_dma_pdma_cb(ESPState *s) uint32_t n; if (s->do_cmd) { + /* Ensure we have received complete command after SATN and stop */ + if (esp_get_tc(s) || fifo8_is_empty(&s->cmdfifo)) { + return; + } + s->ti_size = 0; s->do_cmd = 0; do_cmd(s); @@ -1220,7 +1221,6 @@ static void sysbus_esp_pdma_write(void *opaque, hwaddr addr, { SysBusESPState *sysbus = opaque; ESPState *s = ESP(&sysbus->esp); - uint32_t dmalen; trace_esp_pdma_write(size); @@ -1233,10 +1233,7 @@ static void sysbus_esp_pdma_write(void *opaque, hwaddr addr, esp_pdma_write(s, val); break; } - dmalen = esp_get_tc(s); - if (dmalen == 0 || fifo8_num_free(&s->fifo) < 2) { - s->pdma_cb(s); - } + s->pdma_cb(s); } static uint64_t sysbus_esp_pdma_read(void *opaque, hwaddr addr, From 35579b523cf8f441da12f968ce5dcf6ae0bfbfea Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 19 May 2021 11:08:02 +0100 Subject: [PATCH 11/45] esp: revert 75ef849696 "esp: correctly fill bus id with requested lun" This commit from nearly 10 years ago is now broken due to the improvements in esp emulation (or perhaps was never correct). It shows up as a bug in detecting the CDROM drive under MacOS. The error is caused by the MacOS CDROM driver sending this CDB with an "S without ATN" command and without DMA: 0x12 0x00 0x00 0x00 0x05 0x00 (INQUIRY) This is a valid INQUIRY command, however with this logic present the 3rd byte (0x0) is copied over the 1st byte (0x12) which silently converts the INQUIRY command to a TEST UNIT READY command before passing it to the QEMU SCSI layer. Since the TEST UNIT READY command has a zero length response the MacOS CDROM driver never receives a response and assumes the CDROM is not present. The logic was to ignore the IDENTIFY byte and copy the LUN over from the CDB, which did store the LUN in bits 5-7 of the second byte in olden times. This however is all obsolete, so just drop the code. Signed-off-by: Mark Cave-Ayland Message-Id: <20210519100803.10293-5-mark.cave-ayland@ilande.co.uk> [Tweaked commit message. - Paolo] Signed-off-by: Paolo Bonzini --- hw/scsi/esp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 8e314ef156d2..16e4b7ead6d8 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -260,9 +260,6 @@ static uint32_t get_cmd(ESPState *s, uint32_t maxlen) return 0; } n = esp_fifo_pop_buf(&s->fifo, buf, dmalen); - if (n >= 3) { - buf[0] = buf[2] >> 5; - } n = MIN(fifo8_num_free(&s->cmdfifo), n); fifo8_push_all(&s->cmdfifo, buf, n); } From c348458f357784629c36a6eb1493c0c0c33b74e7 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 19 May 2021 11:08:03 +0100 Subject: [PATCH 12/45] esp: correctly accumulate extended messages for PDMA Commit 799d90d818 "esp: transition to message out phase after SATN and stop command" added logic to correctly handle extended messages for DMA requests but not for PDMA requests. Apply the same logic in esp_do_dma() to do_dma_pdma_cb() so that extended messages terminated with a PDMA request are accumulated correctly. This allows the ESP device to respond correctly to the SDTR negotiation initiated by the NetBSD ESP driver without causing errors and timeouts on boot. Signed-off-by: Mark Cave-Ayland Message-Id: <20210519100803.10293-6-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini --- hw/scsi/esp.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 16e4b7ead6d8..3e6f4094fcc8 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -492,9 +492,26 @@ static void do_dma_pdma_cb(ESPState *s) } s->ti_size = 0; - s->do_cmd = 0; - do_cmd(s); - esp_lower_drq(s); + if ((s->rregs[ESP_RSTAT] & 7) == STAT_CD) { + /* No command received */ + if (s->cmdfifo_cdb_offset == fifo8_num_used(&s->cmdfifo)) { + return; + } + + /* Command has been received */ + s->do_cmd = 0; + do_cmd(s); + } else { + /* + * Extra message out bytes received: update cmdfifo_cdb_offset + * and then switch to commmand phase + */ + s->cmdfifo_cdb_offset = fifo8_num_used(&s->cmdfifo); + s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD; + s->rregs[ESP_RSEQ] = SEQ_CD; + s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); + } return; } From 0bcd5a18940e1c1e3350b93cfadcdc6b58ca1c0e Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sun, 13 Jun 2021 11:26:14 +0100 Subject: [PATCH 13/45] esp: fix migration version check in esp_is_version_5() Commit 4e78f3bf35 "esp: defer command completion interrupt on incoming data transfers" added a version check for use with VMSTATE_*_TEST macros to allow migration from older QEMU versions. Unfortunately the version check fails to work in its current form since if the VMStateDescription version_id is incremented, the test returns false and so the fields are not included in the outgoing migration stream. Change the version check to use >= rather == to ensure that migration works correctly when the ESPState VMStateDescription has version_id > 5. Signed-off-by: Mark Cave-Ayland Fixes: 4e78f3bf35 ("esp: defer command completion interrupt on incoming data transfers") Message-Id: <20210613102614.5438-1-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini --- hw/scsi/esp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 3e6f4094fcc8..8fad87be9d60 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -1128,7 +1128,7 @@ static bool esp_is_version_5(void *opaque, int version_id) ESPState *s = ESP(opaque); version_id = MIN(version_id, s->mig_version_id); - return version_id == 5; + return version_id >= 5; } int esp_pre_save(void *opaque) From 4eb86065603400fe27c17a346985a97b489b55d4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 11 Jun 2021 13:38:58 +0200 Subject: [PATCH 14/45] esp: store lun coming from the MESSAGE OUT phase The LUN is selected with an IDENTIFY message, and persists until the next message out phase. Instead of passing it to do_busid_cmd, store it in ESPState. Because do_cmd can simply skip the message out phase if cmdfifo_cdb_offset is zero, it can now be used for the S without ATN cases as well. Reviewed-by: Mark Cave-Ayland Tested-by: Mark Cave-Ayland Signed-off-by: Paolo Bonzini --- hw/scsi/esp.c | 47 ++++++++++++++++++++++++++++--------------- hw/scsi/trace-events | 3 ++- include/hw/scsi/esp.h | 1 + 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 8fad87be9d60..4ac211478886 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -221,7 +221,7 @@ static int esp_select(ESPState *s) /* * Note that we deliberately don't raise the IRQ here: this will be done - * either in do_busid_cmd() for DATA OUT transfers or by the deferred + * either in do_command_phase() for DATA OUT transfers or by the deferred * IRQ mechanism in esp_transfer_data() for DATA IN transfers */ s->rregs[ESP_RINTR] |= INTR_FC; @@ -272,24 +272,22 @@ static uint32_t get_cmd(ESPState *s, uint32_t maxlen) return dmalen; } -static void do_busid_cmd(ESPState *s, uint8_t busid) +static void do_command_phase(ESPState *s) { uint32_t cmdlen; int32_t datalen; - int lun; SCSIDevice *current_lun; uint8_t buf[ESP_CMDFIFO_SZ]; - trace_esp_do_busid_cmd(busid); - lun = busid & 7; + trace_esp_do_command_phase(s->lun); cmdlen = fifo8_num_used(&s->cmdfifo); if (!cmdlen || !s->current_dev) { return; } esp_fifo_pop_buf(&s->cmdfifo, buf, cmdlen); - current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, lun); - s->current_req = scsi_req_new(current_lun, 0, lun, buf, s); + current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, s->lun); + s->current_req = scsi_req_new(current_lun, 0, s->lun, buf, s); datalen = scsi_req_enqueue(s->current_req); s->ti_size = datalen; fifo8_reset(&s->cmdfifo); @@ -316,21 +314,29 @@ static void do_busid_cmd(ESPState *s, uint8_t busid) } } -static void do_cmd(ESPState *s) +static void do_message_phase(ESPState *s) { - uint8_t busid = esp_fifo_pop(&s->cmdfifo); - int len; + if (s->cmdfifo_cdb_offset) { + uint8_t message = esp_fifo_pop(&s->cmdfifo); - s->cmdfifo_cdb_offset--; + trace_esp_do_identify(message); + s->lun = message & 7; + s->cmdfifo_cdb_offset--; + } /* Ignore extended messages for now */ if (s->cmdfifo_cdb_offset) { - len = MIN(s->cmdfifo_cdb_offset, fifo8_num_used(&s->cmdfifo)); + int len = MIN(s->cmdfifo_cdb_offset, fifo8_num_used(&s->cmdfifo)); esp_fifo_pop_buf(&s->cmdfifo, NULL, len); s->cmdfifo_cdb_offset = 0; } +} - do_busid_cmd(s, busid); +static void do_cmd(ESPState *s) +{ + do_message_phase(s); + assert(s->cmdfifo_cdb_offset == 0); + do_command_phase(s); } static void satn_pdma_cb(ESPState *s) @@ -369,7 +375,7 @@ static void s_without_satn_pdma_cb(ESPState *s) if (!esp_get_tc(s) && !fifo8_is_empty(&s->cmdfifo)) { s->cmdfifo_cdb_offset = 0; s->do_cmd = 0; - do_busid_cmd(s, 0); + do_cmd(s); } } @@ -386,7 +392,7 @@ static void handle_s_without_atn(ESPState *s) if (cmdlen > 0) { s->cmdfifo_cdb_offset = 0; s->do_cmd = 0; - do_busid_cmd(s, 0); + do_cmd(s); } else if (cmdlen == 0) { s->do_cmd = 1; /* Target present, but no cmd yet - switch to command phase */ @@ -1131,6 +1137,14 @@ static bool esp_is_version_5(void *opaque, int version_id) return version_id >= 5; } +static bool esp_is_version_6(void *opaque, int version_id) +{ + ESPState *s = ESP(opaque); + + version_id = MIN(version_id, s->mig_version_id); + return version_id >= 6; +} + int esp_pre_save(void *opaque) { ESPState *s = ESP(object_resolve_path_component( @@ -1168,7 +1182,7 @@ static int esp_post_load(void *opaque, int version_id) const VMStateDescription vmstate_esp = { .name = "esp", - .version_id = 5, + .version_id = 6, .minimum_version_id = 3, .post_load = esp_post_load, .fields = (VMStateField[]) { @@ -1197,6 +1211,7 @@ const VMStateDescription vmstate_esp = { VMSTATE_FIFO8_TEST(fifo, ESPState, esp_is_version_5), VMSTATE_FIFO8_TEST(cmdfifo, ESPState, esp_is_version_5), VMSTATE_UINT8_TEST(ti_cmd, ESPState, esp_is_version_5), + VMSTATE_UINT8_TEST(lun, ESPState, esp_is_version_6), VMSTATE_END_OF_LIST() }, }; diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events index 1a27e141aebe..92d5b40f892a 100644 --- a/hw/scsi/trace-events +++ b/hw/scsi/trace-events @@ -166,7 +166,8 @@ esp_dma_disable(void) "Lower enable" esp_pdma_read(int size) "pDMA read %u bytes" esp_pdma_write(int size) "pDMA write %u bytes" esp_get_cmd(uint32_t dmalen, int target) "len %d target %d" -esp_do_busid_cmd(uint8_t busid) "busid 0x%x" +esp_do_command_phase(uint8_t busid) "busid 0x%x" +esp_do_identify(uint8_t byte) "0x%x" esp_handle_satn_stop(uint32_t cmdlen) "cmdlen %d" esp_write_response(uint32_t status) "Transfer status (status=%d)" esp_do_dma(uint32_t cmdlen, uint32_t len) "command len %d + %d" diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h index aada3680b757..b1ec27612f64 100644 --- a/include/hw/scsi/esp.h +++ b/include/hw/scsi/esp.h @@ -37,6 +37,7 @@ struct ESPState { SCSIRequest *current_req; Fifo8 cmdfifo; uint8_t cmdfifo_cdb_offset; + uint8_t lun; uint32_t do_cmd; bool data_in_ready; From 7ce18ca0257dac6e3ac92c8bc3d610abbf14bcce Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 6 Apr 2021 10:01:24 +0200 Subject: [PATCH 15/45] softmmu/physmem: Mark shared anonymous memory RAM_SHARED Let's drop the "shared" parameter from ram_block_add() and properly store it in the flags of the ram block instead, such that qemu_ram_is_shared() properly succeeds on all ram blocks that were mapped MAP_SHARED. We'll use this information next to fix some cases with shared anonymous memory. Reviewed-by: Igor Kotrasinski Reviewed-by: Richard Henderson Reviewed-by: Peter Xu Signed-off-by: David Hildenbrand Message-Id: <20210406080126.24010-2-david@redhat.com> Signed-off-by: Paolo Bonzini --- softmmu/physmem.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 1c8717684ab2..b78b30e7bac2 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -1946,8 +1946,9 @@ static void dirty_memory_extend(ram_addr_t old_ram_size, } } -static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared) +static void ram_block_add(RAMBlock *new_block, Error **errp) { + const bool shared = qemu_ram_is_shared(new_block); RAMBlock *block; RAMBlock *last_block = NULL; ram_addr_t old_ram_size, new_ram_size; @@ -2084,7 +2085,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, return NULL; } - ram_block_add(new_block, &local_err, ram_flags & RAM_SHARED); + ram_block_add(new_block, &local_err); if (local_err) { g_free(new_block); error_propagate(errp, local_err); @@ -2147,10 +2148,13 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, if (host) { new_block->flags |= RAM_PREALLOC; } + if (share) { + new_block->flags |= RAM_SHARED; + } if (resizeable) { new_block->flags |= RAM_RESIZEABLE; } - ram_block_add(new_block, &local_err, share); + ram_block_add(new_block, &local_err); if (local_err) { g_free(new_block); error_propagate(errp, local_err); From cdfa56c551bb48f286cfe1f2daa1083d333ee45d Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 6 Apr 2021 10:01:25 +0200 Subject: [PATCH 16/45] softmmu/physmem: Fix ram_block_discard_range() to handle shared anonymous memory We can create shared anonymous memory via "-object memory-backend-ram,share=on,..." which is, for example, required by PVRDMA for mremap() to work. Shared anonymous memory is weird, though. Instead of MADV_DONTNEED, we have to use MADV_REMOVE: MADV_DONTNEED will only remove / zap all relevant page table entries of the current process, the backend storage will not get removed, resulting in no reduced memory consumption and a repopulation of previous content on next access. Shared anonymous memory is internally really just shmem, but without a fd exposed. As we cannot use fallocate() without the fd to discard the backing storage, MADV_REMOVE gets the same job done without a fd as documented in "man 2 madvise". Removing backing storage implicitly invalidates all page table entries with relevant mappings - an additional MADV_DONTNEED is not required. Fixes: 06329ccecfa0 ("mem: add share parameter to memory-backend-ram") Reviewed-by: Peter Xu Reviewed-by: Dr. David Alan Gilbert Signed-off-by: David Hildenbrand Message-Id: <20210406080126.24010-3-david@redhat.com> Signed-off-by: Paolo Bonzini --- include/qemu/osdep.h | 4 ++-- softmmu/physmem.c | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 236a0456713c..18a9e3fb4c93 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -410,7 +410,7 @@ void qemu_anon_ram_free(void *ptr, size_t size); #ifdef MADV_REMOVE #define QEMU_MADV_REMOVE MADV_REMOVE #else -#define QEMU_MADV_REMOVE QEMU_MADV_INVALID +#define QEMU_MADV_REMOVE QEMU_MADV_DONTNEED #endif #elif defined(CONFIG_POSIX_MADVISE) @@ -424,7 +424,7 @@ void qemu_anon_ram_free(void *ptr, size_t size); #define QEMU_MADV_DONTDUMP QEMU_MADV_INVALID #define QEMU_MADV_HUGEPAGE QEMU_MADV_INVALID #define QEMU_MADV_NOHUGEPAGE QEMU_MADV_INVALID -#define QEMU_MADV_REMOVE QEMU_MADV_INVALID +#define QEMU_MADV_REMOVE QEMU_MADV_DONTNEED #else /* no-op */ diff --git a/softmmu/physmem.c b/softmmu/physmem.c index b78b30e7bac2..c0a3c47167c2 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -3527,6 +3527,7 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) /* The logic here is messy; * madvise DONTNEED fails for hugepages * fallocate works on hugepages and shmem + * shared anonymous memory requires madvise REMOVE */ need_madvise = (rb->page_size == qemu_host_page_size); need_fallocate = rb->fd != -1; @@ -3560,7 +3561,11 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) * fallocate'd away). */ #if defined(CONFIG_MADVISE) - ret = madvise(host_startaddr, length, MADV_DONTNEED); + if (qemu_ram_is_shared(rb) && rb->fd < 0) { + ret = madvise(host_startaddr, length, QEMU_MADV_REMOVE); + } else { + ret = madvise(host_startaddr, length, QEMU_MADV_DONTNEED); + } if (ret) { ret = -errno; error_report("ram_block_discard_range: Failed to discard range " From dbb92eea3857ffeb770d006ad0306e408d33dd62 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 6 Apr 2021 10:01:26 +0200 Subject: [PATCH 17/45] softmmu/physmem: Fix qemu_ram_remap() to handle shared anonymous memory RAM_SHARED now also properly indicates shared anonymous memory. Let's check that flag for anonymous memory as well, to restore the proper mapping. Fixes: 06329ccecfa0 ("mem: add share parameter to memory-backend-ram") Reviewed-by: Peter Xu Signed-off-by: David Hildenbrand Message-Id: <20210406080126.24010-4-david@redhat.com> Signed-off-by: Paolo Bonzini --- softmmu/physmem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/softmmu/physmem.c b/softmmu/physmem.c index c0a3c47167c2..b75d205e8a4a 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -2243,13 +2243,13 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) abort(); } else { flags = MAP_FIXED; + flags |= block->flags & RAM_SHARED ? + MAP_SHARED : MAP_PRIVATE; if (block->fd >= 0) { - flags |= (block->flags & RAM_SHARED ? - MAP_SHARED : MAP_PRIVATE); area = mmap(vaddr, length, PROT_READ | PROT_WRITE, flags, block->fd, offset); } else { - flags |= MAP_PRIVATE | MAP_ANONYMOUS; + flags |= MAP_ANONYMOUS; area = mmap(vaddr, length, PROT_READ | PROT_WRITE, flags, -1, 0); } From adad0b3ae8446acef0670efaa1e835ba37fca4bc Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:14 +0200 Subject: [PATCH 18/45] util/mmap-alloc: Factor out calculation of the pagesize for the guard page Let's factor out calculating the size of the guard page and rename the variable to make it clearer that this pagesize only applies to the guard page. Reviewed-by: Peter Xu Acked-by: Murilo Opsfelder Araujo Acked-by: Eduardo Habkost for memory backend and machine core Cc: Igor Kotrasinski Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-2-david@redhat.com> Signed-off-by: Paolo Bonzini --- util/mmap-alloc.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index e6fa8b598b26..24854064b444 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -82,6 +82,16 @@ size_t qemu_mempath_getpagesize(const char *mem_path) return qemu_real_host_page_size; } +static inline size_t mmap_guard_pagesize(int fd) +{ +#if defined(__powerpc64__) && defined(__linux__) + /* Mappings in the same segment must share the same page size */ + return qemu_fd_getpagesize(fd); +#else + return qemu_real_host_page_size; +#endif +} + void *qemu_ram_mmap(int fd, size_t size, size_t align, @@ -90,12 +100,12 @@ void *qemu_ram_mmap(int fd, bool is_pmem, off_t map_offset) { + const size_t guard_pagesize = mmap_guard_pagesize(fd); int prot; int flags; int map_sync_flags = 0; int guardfd; size_t offset; - size_t pagesize; size_t total; void *guardptr; void *ptr; @@ -116,8 +126,7 @@ void *qemu_ram_mmap(int fd, * anonymous memory is OK. */ flags = MAP_PRIVATE; - pagesize = qemu_fd_getpagesize(fd); - if (fd == -1 || pagesize == qemu_real_host_page_size) { + if (fd == -1 || guard_pagesize == qemu_real_host_page_size) { guardfd = -1; flags |= MAP_ANONYMOUS; } else { @@ -126,7 +135,6 @@ void *qemu_ram_mmap(int fd, } #else guardfd = -1; - pagesize = qemu_real_host_page_size; flags = MAP_PRIVATE | MAP_ANONYMOUS; #endif @@ -138,7 +146,7 @@ void *qemu_ram_mmap(int fd, assert(is_power_of_2(align)); /* Always align to host page size */ - assert(align >= pagesize); + assert(align >= guard_pagesize); flags = MAP_FIXED; flags |= fd == -1 ? MAP_ANONYMOUS : 0; @@ -193,8 +201,8 @@ void *qemu_ram_mmap(int fd, * a guard page guarding against potential buffer overflows. */ total -= offset; - if (total > size + pagesize) { - munmap(ptr + size + pagesize, total - size - pagesize); + if (total > size + guard_pagesize) { + munmap(ptr + size + guard_pagesize, total - size - guard_pagesize); } return ptr; @@ -202,15 +210,8 @@ void *qemu_ram_mmap(int fd, void qemu_ram_munmap(int fd, void *ptr, size_t size) { - size_t pagesize; - if (ptr) { /* Unmap both the RAM block and the guard page */ -#if defined(__powerpc64__) && defined(__linux__) - pagesize = qemu_fd_getpagesize(fd); -#else - pagesize = qemu_real_host_page_size; -#endif - munmap(ptr, size + pagesize); + munmap(ptr, size + mmap_guard_pagesize(fd)); } } From 01c26ad6affae8c00279f5cc8e1af4d6aac004ce Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:15 +0200 Subject: [PATCH 19/45] util/mmap-alloc: Factor out reserving of a memory region to mmap_reserve() We want to reserve a memory region without actually populating memory. Let's factor that out. Reviewed-by: Igor Kotrasinski Acked-by: Murilo Opsfelder Araujo Reviewed-by: Richard Henderson Reviewed-by: Peter Xu Acked-by: Eduardo Habkost for memory backend and machine core Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-3-david@redhat.com> Signed-off-by: Paolo Bonzini --- util/mmap-alloc.c | 58 +++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index 24854064b444..223d66219c6c 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -82,6 +82,38 @@ size_t qemu_mempath_getpagesize(const char *mem_path) return qemu_real_host_page_size; } +/* + * Reserve a new memory region of the requested size to be used for mapping + * from the given fd (if any). + */ +static void *mmap_reserve(size_t size, int fd) +{ + int flags = MAP_PRIVATE; + +#if defined(__powerpc64__) && defined(__linux__) + /* + * On ppc64 mappings in the same segment (aka slice) must share the same + * page size. Since we will be re-allocating part of this segment + * from the supplied fd, we should make sure to use the same page size, to + * this end we mmap the supplied fd. In this case, set MAP_NORESERVE to + * avoid allocating backing store memory. + * We do this unless we are using the system page size, in which case + * anonymous memory is OK. + */ + if (fd == -1 || qemu_fd_getpagesize(fd) == qemu_real_host_page_size) { + fd = -1; + flags |= MAP_ANONYMOUS; + } else { + flags |= MAP_NORESERVE; + } +#else + fd = -1; + flags |= MAP_ANONYMOUS; +#endif + + return mmap(0, size, PROT_NONE, flags, fd, 0); +} + static inline size_t mmap_guard_pagesize(int fd) { #if defined(__powerpc64__) && defined(__linux__) @@ -104,7 +136,6 @@ void *qemu_ram_mmap(int fd, int prot; int flags; int map_sync_flags = 0; - int guardfd; size_t offset; size_t total; void *guardptr; @@ -116,30 +147,7 @@ void *qemu_ram_mmap(int fd, */ total = size + align; -#if defined(__powerpc64__) && defined(__linux__) - /* On ppc64 mappings in the same segment (aka slice) must share the same - * page size. Since we will be re-allocating part of this segment - * from the supplied fd, we should make sure to use the same page size, to - * this end we mmap the supplied fd. In this case, set MAP_NORESERVE to - * avoid allocating backing store memory. - * We do this unless we are using the system page size, in which case - * anonymous memory is OK. - */ - flags = MAP_PRIVATE; - if (fd == -1 || guard_pagesize == qemu_real_host_page_size) { - guardfd = -1; - flags |= MAP_ANONYMOUS; - } else { - guardfd = fd; - flags |= MAP_NORESERVE; - } -#else - guardfd = -1; - flags = MAP_PRIVATE | MAP_ANONYMOUS; -#endif - - guardptr = mmap(0, total, PROT_NONE, flags, guardfd, 0); - + guardptr = mmap_reserve(total, fd); if (guardptr == MAP_FAILED) { return MAP_FAILED; } From d01cbf82ce748955e622712356d8f56bc762ba9d Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:16 +0200 Subject: [PATCH 20/45] util/mmap-alloc: Factor out activating of memory to mmap_activate() We want to activate memory within a reserved memory region, to make it accessible. Let's factor that out. Reviewed-by: Richard Henderson Acked-by: Murilo Opsfelder Araujo Reviewed-by: Peter Xu Acked-by: Eduardo Habkost for memory backend and machine core Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-4-david@redhat.com> Signed-off-by: Paolo Bonzini --- util/mmap-alloc.c | 94 +++++++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index 223d66219c6c..0e2bd7bc0ee1 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -114,6 +114,52 @@ static void *mmap_reserve(size_t size, int fd) return mmap(0, size, PROT_NONE, flags, fd, 0); } +/* + * Activate memory in a reserved region from the given fd (if any), to make + * it accessible. + */ +static void *mmap_activate(void *ptr, size_t size, int fd, bool readonly, + bool shared, bool is_pmem, off_t map_offset) +{ + const int prot = PROT_READ | (readonly ? 0 : PROT_WRITE); + int map_sync_flags = 0; + int flags = MAP_FIXED; + void *activated_ptr; + + flags |= fd == -1 ? MAP_ANONYMOUS : 0; + flags |= shared ? MAP_SHARED : MAP_PRIVATE; + if (shared && is_pmem) { + map_sync_flags = MAP_SYNC | MAP_SHARED_VALIDATE; + } + + activated_ptr = mmap(ptr, size, prot, flags | map_sync_flags, fd, + map_offset); + if (activated_ptr == MAP_FAILED && map_sync_flags) { + if (errno == ENOTSUP) { + char *proc_link = g_strdup_printf("/proc/self/fd/%d", fd); + char *file_name = g_malloc0(PATH_MAX); + int len = readlink(proc_link, file_name, PATH_MAX - 1); + + if (len < 0) { + len = 0; + } + file_name[len] = '\0'; + fprintf(stderr, "Warning: requesting persistence across crashes " + "for backend file %s failed. Proceeding without " + "persistence, data might become corrupted in case of host " + "crash.\n", file_name); + g_free(proc_link); + g_free(file_name); + } + /* + * If mmap failed with MAP_SHARED_VALIDATE | MAP_SYNC, we will try + * again without these flags to handle backwards compatibility. + */ + activated_ptr = mmap(ptr, size, prot, flags, fd, map_offset); + } + return activated_ptr; +} + static inline size_t mmap_guard_pagesize(int fd) { #if defined(__powerpc64__) && defined(__linux__) @@ -133,13 +179,8 @@ void *qemu_ram_mmap(int fd, off_t map_offset) { const size_t guard_pagesize = mmap_guard_pagesize(fd); - int prot; - int flags; - int map_sync_flags = 0; - size_t offset; - size_t total; - void *guardptr; - void *ptr; + size_t offset, total; + void *ptr, *guardptr; /* * Note: this always allocates at least one extra page of virtual address @@ -156,45 +197,10 @@ void *qemu_ram_mmap(int fd, /* Always align to host page size */ assert(align >= guard_pagesize); - flags = MAP_FIXED; - flags |= fd == -1 ? MAP_ANONYMOUS : 0; - flags |= shared ? MAP_SHARED : MAP_PRIVATE; - if (shared && is_pmem) { - map_sync_flags = MAP_SYNC | MAP_SHARED_VALIDATE; - } - offset = QEMU_ALIGN_UP((uintptr_t)guardptr, align) - (uintptr_t)guardptr; - prot = PROT_READ | (readonly ? 0 : PROT_WRITE); - - ptr = mmap(guardptr + offset, size, prot, - flags | map_sync_flags, fd, map_offset); - - if (ptr == MAP_FAILED && map_sync_flags) { - if (errno == ENOTSUP) { - char *proc_link, *file_name; - int len; - proc_link = g_strdup_printf("/proc/self/fd/%d", fd); - file_name = g_malloc0(PATH_MAX); - len = readlink(proc_link, file_name, PATH_MAX - 1); - if (len < 0) { - len = 0; - } - file_name[len] = '\0'; - fprintf(stderr, "Warning: requesting persistence across crashes " - "for backend file %s failed. Proceeding without " - "persistence, data might become corrupted in case of host " - "crash.\n", file_name); - g_free(proc_link); - g_free(file_name); - } - /* - * if map failed with MAP_SHARED_VALIDATE | MAP_SYNC, - * we will remove these flags to handle compatibility. - */ - ptr = mmap(guardptr + offset, size, prot, flags, fd, map_offset); - } - + ptr = mmap_activate(guardptr + offset, size, fd, readonly, shared, is_pmem, + map_offset); if (ptr == MAP_FAILED) { munmap(guardptr, total); return MAP_FAILED; From d5015b80134047013eeec10000df5ce2014ee114 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:17 +0200 Subject: [PATCH 21/45] softmmu/memory: Pass ram_flags to qemu_ram_alloc_from_fd() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's pass in ram flags just like we do with qemu_ram_alloc_from_file(), to clean up and prepare for more flags. Simplify the documentation of passed ram flags: Looking at our documentation of RAM_SHARED and RAM_PMEM is sufficient, no need to be repetitive. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Acked-by: Eduardo Habkost for memory backend and machine core Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-5-david@redhat.com> Signed-off-by: Paolo Bonzini --- backends/hostmem-memfd.c | 7 ++++--- hw/misc/ivshmem.c | 5 ++--- include/exec/memory.h | 9 +++------ include/exec/ram_addr.h | 6 +----- softmmu/memory.c | 7 +++---- 5 files changed, 13 insertions(+), 21 deletions(-) diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c index da75e270573f..3076da146d9a 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -35,6 +35,7 @@ static void memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) { HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(backend); + uint32_t ram_flags; char *name; int fd; @@ -52,9 +53,9 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) } name = host_memory_backend_get_name(backend); - memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), - name, backend->size, - backend->share, fd, 0, errp); + ram_flags = backend->share ? RAM_SHARED : 0; + memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), name, + backend->size, ram_flags, fd, 0, errp); g_free(name); } diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index a1fa4878bef9..1ba4a98377c6 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -493,9 +493,8 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) size = buf.st_size; /* mmap the region and map into the BAR2 */ - memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s), - "ivshmem.bar2", size, true, fd, 0, - &local_err); + memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s), "ivshmem.bar2", + size, RAM_SHARED, fd, 0, &local_err); if (local_err) { error_propagate(errp, local_err); return; diff --git a/include/exec/memory.h b/include/exec/memory.h index b114f5454b15..8aa69a10d76d 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1005,10 +1005,7 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, * @size: size of the region. * @align: alignment of the region base address; if 0, the default alignment * (getpagesize()) will be used. - * @ram_flags: Memory region features: - * - RAM_SHARED: memory must be mmaped with the MAP_SHARED flag - * - RAM_PMEM: the memory is persistent memory - * Other bits are ignored now. + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM. * @path: the path in which to allocate the RAM. * @readonly: true to open @path for reading, false for read/write. * @errp: pointer to Error*, to store an error if it happens. @@ -1034,7 +1031,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, * @owner: the object that tracks the region's reference count * @name: the name of the region. * @size: size of the region. - * @share: %true if memory must be mmaped with the MAP_SHARED flag + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM. * @fd: the fd to mmap. * @offset: offset within the file referenced by fd * @errp: pointer to Error*, to store an error if it happens. @@ -1046,7 +1043,7 @@ void memory_region_init_ram_from_fd(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, - bool share, + uint32_t ram_flags, int fd, ram_addr_t offset, Error **errp); diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 3cb9791df3b9..a7e337834009 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -104,11 +104,7 @@ long qemu_maxrampagesize(void); * Parameters: * @size: the size in bytes of the ram block * @mr: the memory region where the ram block is - * @ram_flags: specify the properties of the ram block, which can be one - * or bit-or of following values - * - RAM_SHARED: mmap the backing file or device with MAP_SHARED - * - RAM_PMEM: the backend @mem_path or @fd is persistent memory - * Other bits are ignored. + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM. * @mem_path or @fd: specify the backing file or device * @readonly: true to open @path for reading, false for read/write. * @errp: pointer to Error*, to store an error if it happens diff --git a/softmmu/memory.c b/softmmu/memory.c index c19b0be6b11b..f777504ac567 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -1609,7 +1609,7 @@ void memory_region_init_ram_from_fd(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, - bool share, + uint32_t ram_flags, int fd, ram_addr_t offset, Error **errp) @@ -1619,9 +1619,8 @@ void memory_region_init_ram_from_fd(MemoryRegion *mr, mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram; - mr->ram_block = qemu_ram_alloc_from_fd(size, mr, - share ? RAM_SHARED : 0, - fd, offset, false, &err); + mr->ram_block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, offset, + false, &err); if (err) { mr->size = int128_zero(); object_unparent(OBJECT(mr)); From 7f863cba4d8d35adacd513cba634dc0dd08d7904 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:18 +0200 Subject: [PATCH 22/45] softmmu/memory: Pass ram_flags to memory_region_init_ram_shared_nomigrate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's forward ram_flags instead, renaming memory_region_init_ram_shared_nomigrate() into memory_region_init_ram_flags_nomigrate(). Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Acked-by: Eduardo Habkost for memory backend and machine core Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-6-david@redhat.com> Signed-off-by: Paolo Bonzini --- backends/hostmem-ram.c | 6 +++-- hw/m68k/next-cube.c | 4 ++-- include/exec/memory.h | 24 +++++++++---------- .../memory-region-housekeeping.cocci | 8 +++---- softmmu/memory.c | 18 +++++++------- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/backends/hostmem-ram.c b/backends/hostmem-ram.c index 5cc53e76c9de..741e70106289 100644 --- a/backends/hostmem-ram.c +++ b/backends/hostmem-ram.c @@ -19,6 +19,7 @@ static void ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) { + uint32_t ram_flags; char *name; if (!backend->size) { @@ -27,8 +28,9 @@ ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) } name = host_memory_backend_get_name(backend); - memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), name, - backend->size, backend->share, errp); + ram_flags = backend->share ? RAM_SHARED : 0; + memory_region_init_ram_flags_nomigrate(&backend->mr, OBJECT(backend), name, + backend->size, ram_flags, errp); g_free(name); } diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index de951ffe5d39..e0d4a94f9db3 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -984,8 +984,8 @@ static void next_cube_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 1, 0x02100000); /* BMAP memory */ - memory_region_init_ram_shared_nomigrate(bmapm1, NULL, "next.bmapmem", 64, - true, &error_fatal); + memory_region_init_ram_flags_nomigrate(bmapm1, NULL, "next.bmapmem", 64, + RAM_SHARED, &error_fatal); memory_region_add_subregion(sysmem, 0x020c0000, bmapm1); /* The Rev_2.5_v66.bin firmware accesses it at 0x820c0020, too */ memory_region_init_alias(bmapm2, NULL, "next.bmapmem2", bmapm1, 0x0, 64); diff --git a/include/exec/memory.h b/include/exec/memory.h index 8aa69a10d76d..b1f8fa1df0ae 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -940,27 +940,27 @@ void memory_region_init_ram_nomigrate(MemoryRegion *mr, Error **errp); /** - * memory_region_init_ram_shared_nomigrate: Initialize RAM memory region. - * Accesses into the region will - * modify memory directly. + * memory_region_init_ram_flags_nomigrate: Initialize RAM memory region. + * Accesses into the region will + * modify memory directly. * * @mr: the #MemoryRegion to be initialized. * @owner: the object that tracks the region's reference count * @name: Region name, becomes part of RAMBlock name used in migration stream * must be unique within any device * @size: size of the region. - * @share: allow remapping RAM to different addresses + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED. * @errp: pointer to Error*, to store an error if it happens. * - * Note that this function is similar to memory_region_init_ram_nomigrate. - * The only difference is part of the RAM region can be remapped. + * Note that this function does not do anything to cause the data in the + * RAM memory region to be migrated; that is the responsibility of the caller. */ -void memory_region_init_ram_shared_nomigrate(MemoryRegion *mr, - Object *owner, - const char *name, - uint64_t size, - bool share, - Error **errp); +void memory_region_init_ram_flags_nomigrate(MemoryRegion *mr, + Object *owner, + const char *name, + uint64_t size, + uint32_t ram_flags, + Error **errp); /** * memory_region_init_resizeable_ram: Initialize memory region with resizeable diff --git a/scripts/coccinelle/memory-region-housekeeping.cocci b/scripts/coccinelle/memory-region-housekeeping.cocci index c768d8140abd..29651ebde909 100644 --- a/scripts/coccinelle/memory-region-housekeeping.cocci +++ b/scripts/coccinelle/memory-region-housekeeping.cocci @@ -127,8 +127,8 @@ static void device_fn(DeviceState *dev, ...) - memory_region_init_rom(E1, NULL, E2, E3, E4); + memory_region_init_rom(E1, obj, E2, E3, E4); | -- memory_region_init_ram_shared_nomigrate(E1, NULL, E2, E3, E4, E5); -+ memory_region_init_ram_shared_nomigrate(E1, obj, E2, E3, E4, E5); +- memory_region_init_ram_flags_nomigrate(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_ram_flags_nomigrate(E1, obj, E2, E3, E4, E5); ) ...+> } @@ -152,8 +152,8 @@ static void device_fn(DeviceState *dev, ...) - memory_region_init_rom(E1, NULL, E2, E3, E4); + memory_region_init_rom(E1, OBJECT(dev), E2, E3, E4); | -- memory_region_init_ram_shared_nomigrate(E1, NULL, E2, E3, E4, E5); -+ memory_region_init_ram_shared_nomigrate(E1, OBJECT(dev), E2, E3, E4, E5); +- memory_region_init_ram_flags_nomigrate(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_ram_flags_nomigrate(E1, OBJECT(dev), E2, E3, E4, E5); ) ...+> } diff --git a/softmmu/memory.c b/softmmu/memory.c index f777504ac567..5c0ff76c0623 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -1531,22 +1531,22 @@ void memory_region_init_ram_nomigrate(MemoryRegion *mr, uint64_t size, Error **errp) { - memory_region_init_ram_shared_nomigrate(mr, owner, name, size, false, errp); + memory_region_init_ram_flags_nomigrate(mr, owner, name, size, 0, errp); } -void memory_region_init_ram_shared_nomigrate(MemoryRegion *mr, - Object *owner, - const char *name, - uint64_t size, - bool share, - Error **errp) +void memory_region_init_ram_flags_nomigrate(MemoryRegion *mr, + Object *owner, + const char *name, + uint64_t size, + uint32_t ram_flags, + Error **errp) { Error *err = NULL; memory_region_init(mr, owner, name, size); mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram; - mr->ram_block = qemu_ram_alloc(size, share, mr, &err); + mr->ram_block = qemu_ram_alloc(size, ram_flags & RAM_SHARED, mr, &err); if (err) { mr->size = int128_zero(); object_unparent(OBJECT(mr)); @@ -1682,7 +1682,7 @@ void memory_region_init_rom_nomigrate(MemoryRegion *mr, uint64_t size, Error **errp) { - memory_region_init_ram_shared_nomigrate(mr, owner, name, size, false, errp); + memory_region_init_ram_flags_nomigrate(mr, owner, name, size, 0, errp); mr->readonly = true; } From ebef62d0e527d4a021f94a405fb38db263f3c4a5 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:19 +0200 Subject: [PATCH 23/45] softmmu/memory: Pass ram_flags to qemu_ram_alloc() and qemu_ram_alloc_internal() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's pass ram_flags to qemu_ram_alloc() and qemu_ram_alloc_internal(), preparing for passing additional flags. Reviewed-by: Philippe Mathieu-Daudé Acked-by: Eduardo Habkost for memory backend and machine core Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-7-david@redhat.com> Signed-off-by: Paolo Bonzini --- include/exec/ram_addr.h | 2 +- softmmu/memory.c | 4 ++-- softmmu/physmem.c | 29 ++++++++++++----------------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index a7e337834009..6d4513f8e25a 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -122,7 +122,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, MemoryRegion *mr, Error **errp); -RAMBlock *qemu_ram_alloc(ram_addr_t size, bool share, MemoryRegion *mr, +RAMBlock *qemu_ram_alloc(ram_addr_t size, uint32_t ram_flags, MemoryRegion *mr, Error **errp); RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t max_size, void (*resized)(const char*, diff --git a/softmmu/memory.c b/softmmu/memory.c index 5c0ff76c0623..f0161515e96d 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -1546,7 +1546,7 @@ void memory_region_init_ram_flags_nomigrate(MemoryRegion *mr, mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram; - mr->ram_block = qemu_ram_alloc(size, ram_flags & RAM_SHARED, mr, &err); + mr->ram_block = qemu_ram_alloc(size, ram_flags, mr, &err); if (err) { mr->size = int128_zero(); object_unparent(OBJECT(mr)); @@ -1702,7 +1702,7 @@ void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, mr->terminates = true; mr->rom_device = true; mr->destructor = memory_region_destructor_ram; - mr->ram_block = qemu_ram_alloc(size, false, mr, &err); + mr->ram_block = qemu_ram_alloc(size, 0, mr, &err); if (err) { mr->size = int128_zero(); object_unparent(OBJECT(mr)); diff --git a/softmmu/physmem.c b/softmmu/physmem.c index b75d205e8a4a..f00304e254f2 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -2128,12 +2128,15 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, void (*resized)(const char*, uint64_t length, void *host), - void *host, bool resizeable, bool share, + void *host, uint32_t ram_flags, MemoryRegion *mr, Error **errp) { RAMBlock *new_block; Error *local_err = NULL; + assert((ram_flags & ~(RAM_SHARED | RAM_RESIZEABLE | RAM_PREALLOC)) == 0); + assert(!host ^ (ram_flags & RAM_PREALLOC)); + size = HOST_PAGE_ALIGN(size); max_size = HOST_PAGE_ALIGN(max_size); new_block = g_malloc0(sizeof(*new_block)); @@ -2145,15 +2148,7 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, new_block->fd = -1; new_block->page_size = qemu_real_host_page_size; new_block->host = host; - if (host) { - new_block->flags |= RAM_PREALLOC; - } - if (share) { - new_block->flags |= RAM_SHARED; - } - if (resizeable) { - new_block->flags |= RAM_RESIZEABLE; - } + new_block->flags = ram_flags; ram_block_add(new_block, &local_err); if (local_err) { g_free(new_block); @@ -2166,15 +2161,15 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, MemoryRegion *mr, Error **errp) { - return qemu_ram_alloc_internal(size, size, NULL, host, false, - false, mr, errp); + return qemu_ram_alloc_internal(size, size, NULL, host, RAM_PREALLOC, mr, + errp); } -RAMBlock *qemu_ram_alloc(ram_addr_t size, bool share, +RAMBlock *qemu_ram_alloc(ram_addr_t size, uint32_t ram_flags, MemoryRegion *mr, Error **errp) { - return qemu_ram_alloc_internal(size, size, NULL, NULL, false, - share, mr, errp); + assert((ram_flags & ~RAM_SHARED) == 0); + return qemu_ram_alloc_internal(size, size, NULL, NULL, ram_flags, mr, errp); } RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz, @@ -2183,8 +2178,8 @@ RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz, void *host), MemoryRegion *mr, Error **errp) { - return qemu_ram_alloc_internal(size, maxsz, resized, NULL, true, - false, mr, errp); + return qemu_ram_alloc_internal(size, maxsz, resized, NULL, + RAM_RESIZEABLE, mr, errp); } static void reclaim_ramblock(RAMBlock *block) From b444f5c079fdb8019d2c59ffa6b67069e857f4e1 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:20 +0200 Subject: [PATCH 24/45] util/mmap-alloc: Pass flags instead of separate bools to qemu_ram_mmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's pass flags instead of bools to prepare for passing other flags and update the documentation of qemu_ram_mmap(). Introduce new QEMU_MAP_ flags that abstract the mmap() PROT_ and MAP_ flag handling and simplify it. We expose only flags that are currently supported by qemu_ram_mmap(). Maybe, we'll see qemu_mmap() in the future as well that can implement these flags. Note: We don't use MAP_ flags as some flags (e.g., MAP_SYNC) are only defined for some systems and we want to always be able to identify these flags reliably inside qemu_ram_mmap() -- for example, to properly warn when some future flags are not available or effective on a system. Also, this way we can simplify PROT_ handling as well. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Acked-by: Eduardo Habkost for memory backend and machine core Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-8-david@redhat.com> Signed-off-by: Paolo Bonzini --- include/qemu/mmap-alloc.h | 16 +++++++++------- include/qemu/osdep.h | 18 ++++++++++++++++++ softmmu/physmem.c | 8 +++++--- util/mmap-alloc.c | 15 ++++++++------- util/oslib-posix.c | 3 ++- 5 files changed, 42 insertions(+), 18 deletions(-) diff --git a/include/qemu/mmap-alloc.h b/include/qemu/mmap-alloc.h index 456ff87df167..90d0eee70535 100644 --- a/include/qemu/mmap-alloc.h +++ b/include/qemu/mmap-alloc.h @@ -7,18 +7,22 @@ size_t qemu_fd_getpagesize(int fd); size_t qemu_mempath_getpagesize(const char *mem_path); /** - * qemu_ram_mmap: mmap the specified file or device. + * qemu_ram_mmap: mmap anonymous memory, the specified file or device. + * + * mmap() abstraction to map guest RAM, simplifying flag handling, taking + * care of alignment requirements and installing guard pages. * * Parameters: * @fd: the file or the device to mmap * @size: the number of bytes to be mmaped * @align: if not zero, specify the alignment of the starting mapping address; * otherwise, the alignment in use will be determined by QEMU. - * @readonly: true for a read-only mapping, false for read/write. - * @shared: map has RAM_SHARED flag. - * @is_pmem: map has RAM_PMEM flag. + * @qemu_map_flags: QEMU_MAP_* flags * @map_offset: map starts at offset of map_offset from the start of fd * + * Internally, MAP_PRIVATE, MAP_ANONYMOUS and MAP_SHARED_VALIDATE are set + * implicitly based on other parameters. + * * Return: * On success, return a pointer to the mapped area. * On failure, return MAP_FAILED. @@ -26,9 +30,7 @@ size_t qemu_mempath_getpagesize(const char *mem_path); void *qemu_ram_mmap(int fd, size_t size, size_t align, - bool readonly, - bool shared, - bool is_pmem, + uint32_t qemu_map_flags, off_t map_offset); void qemu_ram_munmap(int fd, void *ptr, size_t size); diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 18a9e3fb4c93..37a38c4af32e 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -366,6 +366,24 @@ void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared); void qemu_vfree(void *ptr); void qemu_anon_ram_free(void *ptr, size_t size); +/* + * Abstraction of PROT_ and MAP_ flags as passed to mmap(), for example, + * consumed by qemu_ram_mmap(). + */ + +/* Map PROT_READ instead of PROT_READ | PROT_WRITE. */ +#define QEMU_MAP_READONLY (1 << 0) + +/* Use MAP_SHARED instead of MAP_PRIVATE. */ +#define QEMU_MAP_SHARED (1 << 1) + +/* + * Use MAP_SYNC | MAP_SHARED_VALIDATE if supported. Ignored without + * QEMU_MAP_SHARED. If mapping fails, warn and fallback to !QEMU_MAP_SYNC. + */ +#define QEMU_MAP_SYNC (1 << 2) + + #define QEMU_MADV_INVALID -1 #if defined(CONFIG_MADVISE) diff --git a/softmmu/physmem.c b/softmmu/physmem.c index f00304e254f2..a110aa67fd28 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -1540,6 +1540,7 @@ static void *file_ram_alloc(RAMBlock *block, off_t offset, Error **errp) { + uint32_t qemu_map_flags; void *area; block->page_size = qemu_fd_getpagesize(fd); @@ -1587,9 +1588,10 @@ static void *file_ram_alloc(RAMBlock *block, perror("ftruncate"); } - area = qemu_ram_mmap(fd, memory, block->mr->align, readonly, - block->flags & RAM_SHARED, block->flags & RAM_PMEM, - offset); + qemu_map_flags = readonly ? QEMU_MAP_READONLY : 0; + qemu_map_flags |= (block->flags & RAM_SHARED) ? QEMU_MAP_SHARED : 0; + qemu_map_flags |= (block->flags & RAM_PMEM) ? QEMU_MAP_SYNC : 0; + area = qemu_ram_mmap(fd, memory, block->mr->align, qemu_map_flags, offset); if (area == MAP_FAILED) { error_setg_errno(errp, errno, "unable to map backing store for guest RAM"); diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index 0e2bd7bc0ee1..1ddc0e2a1eca 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -118,9 +118,12 @@ static void *mmap_reserve(size_t size, int fd) * Activate memory in a reserved region from the given fd (if any), to make * it accessible. */ -static void *mmap_activate(void *ptr, size_t size, int fd, bool readonly, - bool shared, bool is_pmem, off_t map_offset) +static void *mmap_activate(void *ptr, size_t size, int fd, + uint32_t qemu_map_flags, off_t map_offset) { + const bool readonly = qemu_map_flags & QEMU_MAP_READONLY; + const bool shared = qemu_map_flags & QEMU_MAP_SHARED; + const bool sync = qemu_map_flags & QEMU_MAP_SYNC; const int prot = PROT_READ | (readonly ? 0 : PROT_WRITE); int map_sync_flags = 0; int flags = MAP_FIXED; @@ -128,7 +131,7 @@ static void *mmap_activate(void *ptr, size_t size, int fd, bool readonly, flags |= fd == -1 ? MAP_ANONYMOUS : 0; flags |= shared ? MAP_SHARED : MAP_PRIVATE; - if (shared && is_pmem) { + if (shared && sync) { map_sync_flags = MAP_SYNC | MAP_SHARED_VALIDATE; } @@ -173,9 +176,7 @@ static inline size_t mmap_guard_pagesize(int fd) void *qemu_ram_mmap(int fd, size_t size, size_t align, - bool readonly, - bool shared, - bool is_pmem, + uint32_t qemu_map_flags, off_t map_offset) { const size_t guard_pagesize = mmap_guard_pagesize(fd); @@ -199,7 +200,7 @@ void *qemu_ram_mmap(int fd, offset = QEMU_ALIGN_UP((uintptr_t)guardptr, align) - (uintptr_t)guardptr; - ptr = mmap_activate(guardptr + offset, size, fd, readonly, shared, is_pmem, + ptr = mmap_activate(guardptr + offset, size, fd, qemu_map_flags, map_offset); if (ptr == MAP_FAILED) { munmap(guardptr, total); diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 7b4bec1402ea..0dd7784a8893 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -229,8 +229,9 @@ void *qemu_memalign(size_t alignment, size_t size) /* alloc shared memory pages */ void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared) { + const uint32_t qemu_map_flags = shared ? QEMU_MAP_SHARED : 0; size_t align = QEMU_VMALLOC_ALIGN; - void *ptr = qemu_ram_mmap(-1, size, align, false, shared, false, 0); + void *ptr = qemu_ram_mmap(-1, size, align, qemu_map_flags, 0); if (ptr == MAP_FAILED) { return NULL; From 8dbe22c6868b8a5efd1df3d0c5150524fabe61ff Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:21 +0200 Subject: [PATCH 25/45] memory: Introduce RAM_NORESERVE and wire it up in qemu_ram_mmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's introduce RAM_NORESERVE, allowing mmap'ing with MAP_NORESERVE. The new flag has the following semantics: " RAM is mmap-ed with MAP_NORESERVE. When set, reserving swap space (or huge pages if applicable) is skipped: will bail out if not supported. When not set, the OS will do the reservation, if supported for the memory type. " Allow passing it into: - memory_region_init_ram_nomigrate() - memory_region_init_resizeable_ram() - memory_region_init_ram_from_file() ... and teach qemu_ram_mmap() and qemu_anon_ram_alloc() about the flag. Bail out if the flag is not supported, which is the case right now for both, POSIX and win32. We will add Linux support next and allow specifying RAM_NORESERVE via memory backends. The target use case is virtio-mem, which dynamically exposes memory inside a large, sparse memory area to the VM. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Acked-by: Eduardo Habkost for memory backend and machine core Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-9-david@redhat.com> Signed-off-by: Paolo Bonzini --- include/exec/cpu-common.h | 1 + include/exec/memory.h | 15 ++++++++++++--- include/exec/ram_addr.h | 3 ++- include/qemu/osdep.h | 9 ++++++++- migration/ram.c | 3 +-- softmmu/physmem.c | 15 ++++++++++++--- util/mmap-alloc.c | 7 +++++++ util/oslib-posix.c | 6 ++++-- util/oslib-win32.c | 13 ++++++++++++- 9 files changed, 59 insertions(+), 13 deletions(-) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index ccabed4003a6..039d422bf4cb 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -59,6 +59,7 @@ ram_addr_t qemu_ram_get_offset(RAMBlock *rb); ram_addr_t qemu_ram_get_used_length(RAMBlock *rb); ram_addr_t qemu_ram_get_max_length(RAMBlock *rb); bool qemu_ram_is_shared(RAMBlock *rb); +bool qemu_ram_is_noreserve(RAMBlock *rb); bool qemu_ram_is_uf_zeroable(RAMBlock *rb); void qemu_ram_set_uf_zeroable(RAMBlock *rb); bool qemu_ram_is_migratable(RAMBlock *rb); diff --git a/include/exec/memory.h b/include/exec/memory.h index b1f8fa1df0ae..b116f7c64edb 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -155,6 +155,13 @@ typedef struct IOMMUTLBEvent { */ #define RAM_UF_WRITEPROTECT (1 << 6) +/* + * RAM is mmap-ed with MAP_NORESERVE. When set, reserving swap space (or huge + * pages if applicable) is skipped: will bail out if not supported. When not + * set, the OS will do the reservation, if supported for the memory type. + */ +#define RAM_NORESERVE (1 << 7) + static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, IOMMUNotifierFlag flags, hwaddr start, hwaddr end, @@ -949,7 +956,7 @@ void memory_region_init_ram_nomigrate(MemoryRegion *mr, * @name: Region name, becomes part of RAMBlock name used in migration stream * must be unique within any device * @size: size of the region. - * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED. + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_NORESERVE. * @errp: pointer to Error*, to store an error if it happens. * * Note that this function does not do anything to cause the data in the @@ -1005,7 +1012,8 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, * @size: size of the region. * @align: alignment of the region base address; if 0, the default alignment * (getpagesize()) will be used. - * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM. + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, + * RAM_NORESERVE, * @path: the path in which to allocate the RAM. * @readonly: true to open @path for reading, false for read/write. * @errp: pointer to Error*, to store an error if it happens. @@ -1031,7 +1039,8 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, * @owner: the object that tracks the region's reference count * @name: the name of the region. * @size: size of the region. - * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM. + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, + * RAM_NORESERVE. * @fd: the fd to mmap. * @offset: offset within the file referenced by fd * @errp: pointer to Error*, to store an error if it happens. diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 6d4513f8e25a..551876bed041 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -104,7 +104,8 @@ long qemu_maxrampagesize(void); * Parameters: * @size: the size in bytes of the ram block * @mr: the memory region where the ram block is - * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM. + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, + * RAM_NORESERVE. * @mem_path or @fd: specify the backing file or device * @readonly: true to open @path for reading, false for read/write. * @errp: pointer to Error*, to store an error if it happens diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 37a38c4af32e..c2c7fe5c4785 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -362,7 +362,8 @@ extern "C" { int qemu_daemon(int nochdir, int noclose); void *qemu_try_memalign(size_t alignment, size_t size); void *qemu_memalign(size_t alignment, size_t size); -void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared); +void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared, + bool noreserve); void qemu_vfree(void *ptr); void qemu_anon_ram_free(void *ptr, size_t size); @@ -383,6 +384,12 @@ void qemu_anon_ram_free(void *ptr, size_t size); */ #define QEMU_MAP_SYNC (1 << 2) +/* + * Use MAP_NORESERVE to skip reservation of swap space (or huge pages if + * applicable). Bail out if not supported/effective. + */ +#define QEMU_MAP_NORESERVE (1 << 3) + #define QEMU_MADV_INVALID -1 diff --git a/migration/ram.c b/migration/ram.c index 60ea913c5439..723af67c2e91 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3343,8 +3343,7 @@ int colo_init_ram_cache(void) WITH_RCU_READ_LOCK_GUARD() { RAMBLOCK_FOREACH_NOT_IGNORED(block) { block->colo_cache = qemu_anon_ram_alloc(block->used_length, - NULL, - false); + NULL, false, false); if (!block->colo_cache) { error_report("%s: Can't alloc memory for COLO cache of block %s," "size 0x" RAM_ADDR_FMT, __func__, block->idstr, diff --git a/softmmu/physmem.c b/softmmu/physmem.c index a110aa67fd28..11ea8e19a627 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -1591,6 +1591,7 @@ static void *file_ram_alloc(RAMBlock *block, qemu_map_flags = readonly ? QEMU_MAP_READONLY : 0; qemu_map_flags |= (block->flags & RAM_SHARED) ? QEMU_MAP_SHARED : 0; qemu_map_flags |= (block->flags & RAM_PMEM) ? QEMU_MAP_SYNC : 0; + qemu_map_flags |= (block->flags & RAM_NORESERVE) ? QEMU_MAP_NORESERVE : 0; area = qemu_ram_mmap(fd, memory, block->mr->align, qemu_map_flags, offset); if (area == MAP_FAILED) { error_setg_errno(errp, errno, @@ -1716,6 +1717,11 @@ bool qemu_ram_is_shared(RAMBlock *rb) return rb->flags & RAM_SHARED; } +bool qemu_ram_is_noreserve(RAMBlock *rb) +{ + return rb->flags & RAM_NORESERVE; +} + /* Note: Only set at the start of postcopy */ bool qemu_ram_is_uf_zeroable(RAMBlock *rb) { @@ -1950,6 +1956,7 @@ static void dirty_memory_extend(ram_addr_t old_ram_size, static void ram_block_add(RAMBlock *new_block, Error **errp) { + const bool noreserve = qemu_ram_is_noreserve(new_block); const bool shared = qemu_ram_is_shared(new_block); RAMBlock *block; RAMBlock *last_block = NULL; @@ -1973,7 +1980,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) } else { new_block->host = qemu_anon_ram_alloc(new_block->max_length, &new_block->mr->align, - shared); + shared, noreserve); if (!new_block->host) { error_setg_errno(errp, errno, "cannot set up guest memory '%s'", @@ -2045,7 +2052,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, int64_t file_size, file_align; /* Just support these ram flags by now. */ - assert((ram_flags & ~(RAM_SHARED | RAM_PMEM)) == 0); + assert((ram_flags & ~(RAM_SHARED | RAM_PMEM | RAM_NORESERVE)) == 0); if (xen_enabled()) { error_setg(errp, "-mem-path not supported with Xen"); @@ -2137,6 +2144,8 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, Error *local_err = NULL; assert((ram_flags & ~(RAM_SHARED | RAM_RESIZEABLE | RAM_PREALLOC)) == 0); + assert((ram_flags & ~(RAM_SHARED | RAM_RESIZEABLE | RAM_PREALLOC | + RAM_NORESERVE)) == 0); assert(!host ^ (ram_flags & RAM_PREALLOC)); size = HOST_PAGE_ALIGN(size); @@ -2170,7 +2179,7 @@ RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, RAMBlock *qemu_ram_alloc(ram_addr_t size, uint32_t ram_flags, MemoryRegion *mr, Error **errp) { - assert((ram_flags & ~RAM_SHARED) == 0); + assert((ram_flags & ~(RAM_SHARED | RAM_NORESERVE)) == 0); return qemu_ram_alloc_internal(size, size, NULL, NULL, ram_flags, mr, errp); } diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index 1ddc0e2a1eca..d0cf4aaee533 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu/mmap-alloc.h" #include "qemu/host-utils.h" +#include "qemu/error-report.h" #define HUGETLBFS_MAGIC 0x958458f6 @@ -121,6 +122,7 @@ static void *mmap_reserve(size_t size, int fd) static void *mmap_activate(void *ptr, size_t size, int fd, uint32_t qemu_map_flags, off_t map_offset) { + const bool noreserve = qemu_map_flags & QEMU_MAP_NORESERVE; const bool readonly = qemu_map_flags & QEMU_MAP_READONLY; const bool shared = qemu_map_flags & QEMU_MAP_SHARED; const bool sync = qemu_map_flags & QEMU_MAP_SYNC; @@ -129,6 +131,11 @@ static void *mmap_activate(void *ptr, size_t size, int fd, int flags = MAP_FIXED; void *activated_ptr; + if (noreserve) { + error_report("Skipping reservation of swap space is not supported"); + return MAP_FAILED; + } + flags |= fd == -1 ? MAP_ANONYMOUS : 0; flags |= shared ? MAP_SHARED : MAP_PRIVATE; if (shared && sync) { diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 0dd7784a8893..e8bdb02e1d01 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -227,9 +227,11 @@ void *qemu_memalign(size_t alignment, size_t size) } /* alloc shared memory pages */ -void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared) +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; + 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); diff --git a/util/oslib-win32.c b/util/oslib-win32.c index ca99356fdfce..ee3a3692d828 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -38,6 +38,7 @@ #include "trace.h" #include "qemu/sockets.h" #include "qemu/cutils.h" +#include "qemu/error-report.h" #include /* this must come after including "trace.h" */ @@ -76,10 +77,20 @@ static int get_allocation_granularity(void) return system_info.dwAllocationGranularity; } -void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared) +void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared, + bool noreserve) { void *ptr; + if (noreserve) { + /* + * We need a MEM_COMMIT before accessing any memory in a MEM_RESERVE + * area; we cannot easily mimic POSIX MAP_NORESERVE semantics. + */ + error_report("Skipping reservation of swap space is not supported."); + return NULL; + } + ptr = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); trace_qemu_anon_ram_alloc(size, ptr); From d94e0bc9ef7848f69550a80e7be6d4de68856e46 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:22 +0200 Subject: [PATCH 26/45] util/mmap-alloc: Support RAM_NORESERVE via MAP_NORESERVE under Linux Let's support RAM_NORESERVE via MAP_NORESERVE on Linux. The flag has no effect on most shared mappings - except for hugetlbfs and anonymous memory. Linux man page: "MAP_NORESERVE: Do not reserve swap space for this mapping. When swap space is reserved, one has the guarantee that it is possible to modify the mapping. When swap space is not reserved one might get SIGSEGV upon a write if no physical memory is available. See also the discussion of the file /proc/sys/vm/overcommit_memory in proc(5). In kernels before 2.6, this flag had effect only for private writable mappings." Note that the "guarantee" part is wrong with memory overcommit in Linux. Also, in Linux hugetlbfs is treated differently - we configure reservation of huge pages from the pool, not reservation of swap space (huge pages cannot be swapped). The rough behavior is [1]: a) !Hugetlbfs: 1) Without MAP_NORESERVE *or* with memory overcommit under Linux disabled ("/proc/sys/vm/overcommit_memory == 2"), the following accounting/reservation happens: For a file backed map SHARED or READ-only - 0 cost (the file is the map not swap) PRIVATE WRITABLE - size of mapping per instance For an anonymous or /dev/zero map SHARED - size of mapping PRIVATE READ-only - 0 cost (but of little use) PRIVATE WRITABLE - size of mapping per instance 2) With MAP_NORESERVE, no accounting/reservation happens. b) Hugetlbfs: 1) Without MAP_NORESERVE, huge pages are reserved. 2) With MAP_NORESERVE, no huge pages are reserved. Note: With "/proc/sys/vm/overcommit_memory == 0", we were already able to configure it for !hugetlbfs globally; this toggle now allows configuring it more fine-grained, not for the whole system. The target use case is virtio-mem, which dynamically exposes memory inside a large, sparse memory area to the VM. [1] https://www.kernel.org/doc/Documentation/vm/overcommit-accounting Reviewed-by: Peter Xu Acked-by: Eduardo Habkost for memory backend and machine core Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-10-david@redhat.com> Signed-off-by: Paolo Bonzini --- include/qemu/osdep.h | 3 ++ softmmu/physmem.c | 1 + util/mmap-alloc.c | 69 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index c2c7fe5c4785..0a54bf7be86a 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -195,6 +195,9 @@ extern "C" { #ifndef MAP_FIXED_NOREPLACE #define MAP_FIXED_NOREPLACE 0 #endif +#ifndef MAP_NORESERVE +#define MAP_NORESERVE 0 +#endif #ifndef ENOMEDIUM #define ENOMEDIUM ENODEV #endif diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 11ea8e19a627..9b171c9dbe50 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -2251,6 +2251,7 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) flags = MAP_FIXED; flags |= block->flags & RAM_SHARED ? MAP_SHARED : MAP_PRIVATE; + flags |= block->flags & RAM_NORESERVE ? MAP_NORESERVE : 0; if (block->fd >= 0) { area = mmap(vaddr, length, PROT_READ | PROT_WRITE, flags, block->fd, offset); diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index d0cf4aaee533..838e286ce51d 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu/mmap-alloc.h" #include "qemu/host-utils.h" +#include "qemu/cutils.h" #include "qemu/error-report.h" #define HUGETLBFS_MAGIC 0x958458f6 @@ -83,6 +84,70 @@ size_t qemu_mempath_getpagesize(const char *mem_path) return qemu_real_host_page_size; } +#define OVERCOMMIT_MEMORY_PATH "/proc/sys/vm/overcommit_memory" +static bool map_noreserve_effective(int fd, uint32_t qemu_map_flags) +{ +#if defined(__linux__) + const bool readonly = qemu_map_flags & QEMU_MAP_READONLY; + const bool shared = qemu_map_flags & QEMU_MAP_SHARED; + gchar *content = NULL; + const char *endptr; + unsigned int tmp; + + /* + * hugeltb accounting is different than ordinary swap reservation: + * a) Hugetlb pages from the pool are reserved for both private and + * shared mappings. For shared mappings, all mappers have to specify + * MAP_NORESERVE. + * b) MAP_NORESERVE is not affected by /proc/sys/vm/overcommit_memory. + */ + if (qemu_fd_getpagesize(fd) != qemu_real_host_page_size) { + return true; + } + + /* + * Accountable mappings in the kernel that can be affected by MAP_NORESEVE + * are private writable mappings (see mm/mmap.c:accountable_mapping() in + * Linux). For all shared or readonly mappings, MAP_NORESERVE is always + * implicitly active -- no reservation; this includes shmem. The only + * exception is shared anonymous memory, it is accounted like private + * anonymous memory. + */ + if (readonly || (shared && fd >= 0)) { + return true; + } + + /* + * MAP_NORESERVE is globally ignored for applicable !hugetlb mappings when + * memory overcommit is set to "never". Sparse memory regions aren't really + * possible in this system configuration. + * + * Bail out now instead of silently committing way more memory than + * currently desired by the user. + */ + if (g_file_get_contents(OVERCOMMIT_MEMORY_PATH, &content, NULL, NULL) && + !qemu_strtoui(content, &endptr, 0, &tmp) && + (!endptr || *endptr == '\n')) { + if (tmp == 2) { + error_report("Skipping reservation of swap space is not supported:" + " \"" OVERCOMMIT_MEMORY_PATH "\" is \"2\""); + return false; + } + return true; + } + /* this interface has been around since Linux 2.6 */ + error_report("Skipping reservation of swap space is not supported:" + " Could not read: \"" OVERCOMMIT_MEMORY_PATH "\""); + return false; +#endif + /* + * E.g., FreeBSD used to define MAP_NORESERVE, never implemented it, + * and removed it a while ago. + */ + error_report("Skipping reservation of swap space is not supported"); + return false; +} + /* * Reserve a new memory region of the requested size to be used for mapping * from the given fd (if any). @@ -131,13 +196,13 @@ static void *mmap_activate(void *ptr, size_t size, int fd, int flags = MAP_FIXED; void *activated_ptr; - if (noreserve) { - error_report("Skipping reservation of swap space is not supported"); + if (noreserve && !map_noreserve_effective(fd, qemu_map_flags)) { return MAP_FAILED; } flags |= fd == -1 ? MAP_ANONYMOUS : 0; flags |= shared ? MAP_SHARED : MAP_PRIVATE; + flags |= noreserve ? MAP_NORESERVE : 0; if (shared && sync) { map_sync_flags = MAP_SYNC | MAP_SHARED_VALIDATE; } From 9181fb7043edcf096e0ae426cc3fb6f669c7fcb5 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:23 +0200 Subject: [PATCH 27/45] hostmem: Wire up RAM_NORESERVE via "reserve" property Let's provide a way to control the use of RAM_NORESERVE via memory backends using the "reserve" property which defaults to true (old behavior). Only Linux currently supports clearing the flag (and support is checked at runtime, depending on the setting of "/proc/sys/vm/overcommit_memory"). Windows and other POSIX systems will bail out with "reserve=false". The target use case is virtio-mem, which dynamically exposes memory inside a large, sparse memory area to the VM. This essentially allows avoiding to set "/proc/sys/vm/overcommit_memory == 0") when using virtio-mem and also supporting hugetlbfs in the future. As really only Linux implements RAM_NORESERVE right now, let's expose the property only with CONFIG_LINUX. Setting the property to "false" will then only fail in corner cases -- for example on very old kernels or when memory overcommit was completely disabled by the admin. Reviewed-by: Peter Xu Reviewed-by: Eduardo Habkost Reviewed-by: Markus Armbruster Acked-by: Eduardo Habkost for memory backend and machine core Cc: Markus Armbruster Cc: Eric Blake Cc: Igor Mammedov Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-11-david@redhat.com> Signed-off-by: Paolo Bonzini --- backends/hostmem-file.c | 11 ++++++----- backends/hostmem-memfd.c | 1 + backends/hostmem-ram.c | 1 + backends/hostmem.c | 36 ++++++++++++++++++++++++++++++++++++ include/sysemu/hostmem.h | 2 +- qapi/qom.json | 10 ++++++++++ 6 files changed, 55 insertions(+), 6 deletions(-) diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index 9b1b9f0a5604..cd038024fae3 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -39,6 +39,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) object_get_typename(OBJECT(backend))); #else HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); + uint32_t ram_flags; gchar *name; if (!backend->size) { @@ -51,11 +52,11 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) } name = host_memory_backend_get_name(backend); - memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), - name, - backend->size, fb->align, - (backend->share ? RAM_SHARED : 0) | - (fb->is_pmem ? RAM_PMEM : 0), + ram_flags = backend->share ? RAM_SHARED : 0; + ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; + ram_flags |= fb->is_pmem ? RAM_PMEM : 0; + memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), name, + backend->size, fb->align, ram_flags, fb->mem_path, fb->readonly, errp); g_free(name); #endif diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c index 3076da146d9a..3fc85c3db81b 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -54,6 +54,7 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) name = host_memory_backend_get_name(backend); ram_flags = backend->share ? RAM_SHARED : 0; + ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), name, backend->size, ram_flags, fd, 0, errp); g_free(name); diff --git a/backends/hostmem-ram.c b/backends/hostmem-ram.c index 741e70106289..b8e55cdbd0f8 100644 --- a/backends/hostmem-ram.c +++ b/backends/hostmem-ram.c @@ -29,6 +29,7 @@ ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) name = host_memory_backend_get_name(backend); ram_flags = backend->share ? RAM_SHARED : 0; + ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; memory_region_init_ram_flags_nomigrate(&backend->mr, OBJECT(backend), name, backend->size, ram_flags, errp); g_free(name); diff --git a/backends/hostmem.c b/backends/hostmem.c index aab3de84083e..4c05862ed5a8 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -216,6 +216,11 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value, Error *local_err = NULL; HostMemoryBackend *backend = MEMORY_BACKEND(obj); + if (!backend->reserve && value) { + error_setg(errp, "'prealloc=on' and 'reserve=off' are incompatible"); + return; + } + if (!host_memory_backend_mr_inited(backend)) { backend->prealloc = value; return; @@ -267,6 +272,7 @@ static void host_memory_backend_init(Object *obj) /* TODO: convert access to globals to compat properties */ backend->merge = machine_mem_merge(machine); backend->dump = machine_dump_guest_core(machine); + backend->reserve = true; backend->prealloc_threads = 1; } @@ -425,6 +431,30 @@ static void host_memory_backend_set_share(Object *o, bool value, Error **errp) backend->share = value; } +#ifdef CONFIG_LINUX +static bool host_memory_backend_get_reserve(Object *o, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(o); + + return backend->reserve; +} + +static void host_memory_backend_set_reserve(Object *o, bool value, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(o); + + if (host_memory_backend_mr_inited(backend)) { + error_setg(errp, "cannot change property value"); + return; + } + if (backend->prealloc && !value) { + error_setg(errp, "'prealloc=on' and 'reserve=off' are incompatible"); + return; + } + backend->reserve = value; +} +#endif /* CONFIG_LINUX */ + static bool host_memory_backend_get_use_canonical_path(Object *obj, Error **errp) { @@ -493,6 +523,12 @@ host_memory_backend_class_init(ObjectClass *oc, void *data) host_memory_backend_get_share, host_memory_backend_set_share); object_class_property_set_description(oc, "share", "Mark the memory as private to QEMU or shared"); +#ifdef CONFIG_LINUX + object_class_property_add_bool(oc, "reserve", + host_memory_backend_get_reserve, host_memory_backend_set_reserve); + object_class_property_set_description(oc, "reserve", + "Reserve swap space (or huge pages) if applicable"); +#endif /* CONFIG_LINUX */ /* * Do not delete/rename option. This option must be considered stable * (as if it didn't have the 'x-' prefix including deprecation period) as diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h index df5644723a39..9ff5c16963f0 100644 --- a/include/sysemu/hostmem.h +++ b/include/sysemu/hostmem.h @@ -64,7 +64,7 @@ struct HostMemoryBackend { /* protected */ uint64_t size; bool merge, dump, use_canonical_path; - bool prealloc, is_mapped, share; + bool prealloc, is_mapped, share, reserve; uint32_t prealloc_threads; DECLARE_BITMAP(host_nodes, MAX_NODES + 1); HostMemPolicy policy; diff --git a/qapi/qom.json b/qapi/qom.json index f7ef30f940c3..652be317b8b5 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -545,6 +545,9 @@ # @share: if false, the memory is private to QEMU; if true, it is shared # (default: false) # +# @reserve: if true, reserve swap space (or huge pages) if applicable +# (default: true) (since 6.1) +# # @size: size of the memory region in bytes # # @x-use-canonical-path-for-ramblock-id: if true, the canoncial path is used @@ -556,6 +559,12 @@ # false generally, but true for machine # types <= 4.0) # +# Note: prealloc=true and reserve=false cannot be set at the same time. With +# reserve=true, the behavior depends on the operating system: for example, +# Linux will not reserve swap space for shared file mappings -- +# "not applicable". In contrast, reserve=false will bail out if it cannot +# be configured accordingly. +# # Since: 2.1 ## { 'struct': 'MemoryBackendProperties', @@ -566,6 +575,7 @@ '*prealloc': 'bool', '*prealloc-threads': 'uint32', '*share': 'bool', + '*reserve': 'bool', 'size': 'size', '*x-use-canonical-path-for-ramblock-id': 'bool' } } From 157cfaf9b21c90a7c874ce80c4c1c9b1187ad244 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:24 +0200 Subject: [PATCH 28/45] qmp: Clarify memory backend properties returned via query-memdev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We return information on the currently configured memory backends and don't configure them, so decribe what the currently set properties express. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Eduardo Habkost Reviewed-by: Markus Armbruster Suggested-by: Markus Armbruster Acked-by: Eduardo Habkost for memory backend and machine core Cc: Eric Blake Cc: Markus Armbruster Cc: Igor Mammedov Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-12-david@redhat.com> Signed-off-by: Paolo Bonzini --- qapi/machine.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qapi/machine.json b/qapi/machine.json index 58a9c86b3600..eb1436481972 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -790,11 +790,11 @@ # # @size: memory backend size # -# @merge: enables or disables memory merge support +# @merge: whether memory merge support is enabled # -# @dump: includes memory backend's memory in a core dump or not +# @dump: whether memory backend's memory is included in a core dump # -# @prealloc: enables or disables memory preallocation +# @prealloc: whether memory was preallocated # # @host-nodes: host nodes for its memory policy # From d300fc54a48dcdbdd7c06873c3b9941f05c7c6ae Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:25 +0200 Subject: [PATCH 29/45] qmp: Include "share" property of memory backends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's include the property, which can be helpful when debugging, for example, to spot misuse of MAP_PRIVATE which can result in some ugly corner cases (e.g., double-memory consumption on shmem). Use the same description we also use for describing the property. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Eduardo Habkost Reviewed-by: Markus Armbruster Acked-by: Eduardo Habkost for memory backend and machine core Cc: Eric Blake Cc: Markus Armbruster Cc: Igor Mammedov Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-13-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/core/machine-qmp-cmds.c | 1 + qapi/machine.json | 3 +++ 2 files changed, 4 insertions(+) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index a36c96608f03..a36ceaf4f3b5 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -172,6 +172,7 @@ static int query_memdev(Object *obj, void *opaque) m->merge = object_property_get_bool(obj, "merge", &error_abort); m->dump = object_property_get_bool(obj, "dump", &error_abort); m->prealloc = object_property_get_bool(obj, "prealloc", &error_abort); + m->share = object_property_get_bool(obj, "share", &error_abort); m->policy = object_property_get_enum(obj, "policy", "HostMemPolicy", &error_abort); host_nodes = object_property_get_qobject(obj, diff --git a/qapi/machine.json b/qapi/machine.json index eb1436481972..1395742a4aba 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -796,6 +796,8 @@ # # @prealloc: whether memory was preallocated # +# @share: whether memory is private to QEMU or shared (since 6.1) +# # @host-nodes: host nodes for its memory policy # # @policy: memory policy of memory backend @@ -809,6 +811,7 @@ 'merge': 'bool', 'dump': 'bool', 'prealloc': 'bool', + 'share': 'bool', 'host-nodes': ['uint16'], 'policy': 'HostMemPolicy' }} From 7428e7ba15cbc2a1a801ea2697f5f47fe1153381 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:26 +0200 Subject: [PATCH 30/45] hmp: Print "share" property of memory backends with "info memdev" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's print the property. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Eduardo Habkost Reviewed-by: Markus Armbruster Acked-by: Eduardo Habkost for memory backend and machine core Cc: Markus Armbruster Cc: Eric Blake Cc: Igor Mammedov Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-14-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/core/machine-hmp-cmds.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 58248cffa372..004a92b3d682 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -110,6 +110,8 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict) m->value->dump ? "true" : "false"); monitor_printf(mon, " prealloc: %s\n", m->value->prealloc ? "true" : "false"); + monitor_printf(mon, " share: %s\n", + m->value->share ? "true" : "false"); monitor_printf(mon, " policy: %s\n", HostMemPolicy_str(m->value->policy)); visit_complete(v, &str); From 69647f9d51795ebea09eef05b5b2d14ffb835baf Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:27 +0200 Subject: [PATCH 31/45] qmp: Include "reserve" property of memory backends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's include the new property. Instead of relying on CONFIG_LINUX, let's try to unconditionally grab the property and treat errors as "does not exist". Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Eduardo Habkost Reviewed-by: Markus Armbruster Acked-by: Eduardo Habkost for memory backend and machine core Cc: Eric Blake Cc: Markus Armbruster Cc: Igor Mammedov Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-15-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/core/machine-qmp-cmds.c | 7 +++++++ qapi/machine.json | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index a36ceaf4f3b5..216fdfaf3a08 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -157,6 +157,7 @@ void qmp_set_numa_node(NumaOptions *cmd, Error **errp) static int query_memdev(Object *obj, void *opaque) { + Error *err = NULL; MemdevList **list = opaque; Memdev *m; QObject *host_nodes; @@ -173,6 +174,12 @@ static int query_memdev(Object *obj, void *opaque) m->dump = object_property_get_bool(obj, "dump", &error_abort); m->prealloc = object_property_get_bool(obj, "prealloc", &error_abort); m->share = object_property_get_bool(obj, "share", &error_abort); + m->reserve = object_property_get_bool(obj, "reserve", &err); + if (err) { + error_free_or_abort(&err); + } else { + m->has_reserve = true; + } m->policy = object_property_get_enum(obj, "policy", "HostMemPolicy", &error_abort); host_nodes = object_property_get_qobject(obj, diff --git a/qapi/machine.json b/qapi/machine.json index 1395742a4aba..e4d0f9b24f79 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -798,6 +798,12 @@ # # @share: whether memory is private to QEMU or shared (since 6.1) # +# @reserve: whether swap space (or huge pages) was reserved if applicable. +# This corresponds to the user configuration and not the actual +# behavior implemented in the OS to perform the reservation. +# For example, Linux will never reserve swap space for shared +# file mappings. (since 6.1) +# # @host-nodes: host nodes for its memory policy # # @policy: memory policy of memory backend @@ -812,6 +818,7 @@ 'dump': 'bool', 'prealloc': 'bool', 'share': 'bool', + '*reserve': 'bool', 'host-nodes': ['uint16'], 'policy': 'HostMemPolicy' }} From baa014e3b92a12a6037c7525ee1a169ab7ec0302 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 10 May 2021 13:43:28 +0200 Subject: [PATCH 32/45] hmp: Print "reserve" property of memory backends with "info memdev" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's print the new property. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Eduardo Habkost Reviewed-by: Markus Armbruster Acked-by: Eduardo Habkost for memory backend and machine core Cc: Markus Armbruster Cc: Eric Blake Cc: Igor Mammedov Signed-off-by: David Hildenbrand Message-Id: <20210510114328.21835-16-david@redhat.com> Signed-off-by: Paolo Bonzini --- hw/core/machine-hmp-cmds.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 004a92b3d682..76b22b00d6c6 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -112,6 +112,10 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict) m->value->prealloc ? "true" : "false"); monitor_printf(mon, " share: %s\n", m->value->share ? "true" : "false"); + if (m->value->has_reserve) { + monitor_printf(mon, " reserve: %s\n", + m->value->reserve ? "true" : "false"); + } monitor_printf(mon, " policy: %s\n", HostMemPolicy_str(m->value->policy)); visit_complete(v, &str); From f8bb7e1c25b3d9c55975ca0f428f03d1049f2b06 Mon Sep 17 00:00:00 2001 From: David Michael Date: Wed, 9 Jun 2021 08:28:39 -0400 Subject: [PATCH 33/45] configure: map x32 to cpu_family x86_64 for meson The meson.build file defines supported_cpus which does not contain x32, and x32 is not one of meson's stable built-in values: https://mesonbuild.com/Reference-tables.html#cpu-families Signed-off-by: David Michael Message-Id: <878s3jrzm0.fsf@gmail.com> Signed-off-by: Paolo Bonzini --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 8dcb9965b24e..4478f3889ac1 100755 --- a/configure +++ b/configure @@ -6384,7 +6384,7 @@ if test "$skip_meson" = no; then i386) echo "cpu_family = 'x86'" >> $cross ;; - x86_64) + x86_64|x32) echo "cpu_family = 'x86_64'" >> $cross ;; ppc64le) From 813c6459ee774ee48496653cd530658b733b79cd Mon Sep 17 00:00:00 2001 From: Lara Lazier Date: Wed, 16 Jun 2021 14:39:04 +0200 Subject: [PATCH 34/45] target/i386: Refactored intercept checks into cpu_svm_has_intercept Added cpu_svm_has_intercept to reduce duplication when checking the corresponding intercept bit outside of cpu_svm_check_intercept_param Signed-off-by: Lara Lazier Message-Id: <20210616123907.17765-2-laramglazier@gmail.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 4 ++ target/i386/tcg/sysemu/svm_helper.c | 105 +++++++++++++++------------- 2 files changed, 62 insertions(+), 47 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index ac3abea97c16..64b4e4673189 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2149,9 +2149,13 @@ static inline void cpu_svm_check_intercept_param(CPUX86State *env1, uint32_t type, uint64_t param, uintptr_t retaddr) { /* no-op */ } +static inline bool +cpu_svm_has_intercept(CPUX86State *env, uint32_t type) +{ return false; } #else void cpu_svm_check_intercept_param(CPUX86State *env1, uint32_t type, uint64_t param, uintptr_t retaddr); +bool cpu_svm_has_intercept(CPUX86State *env, uint32_t type); #endif /* apic.c */ diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/sysemu/svm_helper.c index 9d671297cf46..2f7606bebf4a 100644 --- a/target/i386/tcg/sysemu/svm_helper.c +++ b/target/i386/tcg/sysemu/svm_helper.c @@ -412,80 +412,91 @@ void helper_clgi(CPUX86State *env) env->hflags2 &= ~HF2_GIF_MASK; } -void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, - uint64_t param, uintptr_t retaddr) +bool cpu_svm_has_intercept(CPUX86State *env, uint32_t type) { - CPUState *cs = env_cpu(env); - - if (likely(!(env->hflags & HF_GUEST_MASK))) { - return; - } switch (type) { case SVM_EXIT_READ_CR0 ... SVM_EXIT_READ_CR0 + 8: if (env->intercept_cr_read & (1 << (type - SVM_EXIT_READ_CR0))) { - cpu_vmexit(env, type, param, retaddr); + return true; } break; case SVM_EXIT_WRITE_CR0 ... SVM_EXIT_WRITE_CR0 + 8: if (env->intercept_cr_write & (1 << (type - SVM_EXIT_WRITE_CR0))) { - cpu_vmexit(env, type, param, retaddr); + return true; } break; case SVM_EXIT_READ_DR0 ... SVM_EXIT_READ_DR0 + 7: if (env->intercept_dr_read & (1 << (type - SVM_EXIT_READ_DR0))) { - cpu_vmexit(env, type, param, retaddr); + return true; } break; case SVM_EXIT_WRITE_DR0 ... SVM_EXIT_WRITE_DR0 + 7: if (env->intercept_dr_write & (1 << (type - SVM_EXIT_WRITE_DR0))) { - cpu_vmexit(env, type, param, retaddr); + return true; } break; case SVM_EXIT_EXCP_BASE ... SVM_EXIT_EXCP_BASE + 31: if (env->intercept_exceptions & (1 << (type - SVM_EXIT_EXCP_BASE))) { - cpu_vmexit(env, type, param, retaddr); - } - break; - case SVM_EXIT_MSR: - if (env->intercept & (1ULL << (SVM_EXIT_MSR - SVM_EXIT_INTR))) { - /* FIXME: this should be read in at vmrun (faster this way?) */ - uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb + - offsetof(struct vmcb, - control.msrpm_base_pa)); - uint32_t t0, t1; - - switch ((uint32_t)env->regs[R_ECX]) { - case 0 ... 0x1fff: - t0 = (env->regs[R_ECX] * 2) % 8; - t1 = (env->regs[R_ECX] * 2) / 8; - break; - case 0xc0000000 ... 0xc0001fff: - t0 = (8192 + env->regs[R_ECX] - 0xc0000000) * 2; - t1 = (t0 / 8); - t0 %= 8; - break; - case 0xc0010000 ... 0xc0011fff: - t0 = (16384 + env->regs[R_ECX] - 0xc0010000) * 2; - t1 = (t0 / 8); - t0 %= 8; - break; - default: - cpu_vmexit(env, type, param, retaddr); - t0 = 0; - t1 = 0; - break; - } - if (x86_ldub_phys(cs, addr + t1) & ((1 << param) << t0)) { - cpu_vmexit(env, type, param, retaddr); - } + return true; } break; default: if (env->intercept & (1ULL << (type - SVM_EXIT_INTR))) { - cpu_vmexit(env, type, param, retaddr); + return true; } break; } + return false; +} + +void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, + uint64_t param, uintptr_t retaddr) +{ + CPUState *cs = env_cpu(env); + + if (likely(!(env->hflags & HF_GUEST_MASK))) { + return; + } + + if (!cpu_svm_has_intercept(env, type)) { + return; + } + + if (type == SVM_EXIT_MSR) { + /* FIXME: this should be read in at vmrun (faster this way?) */ + uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, + control.msrpm_base_pa)); + uint32_t t0, t1; + + switch ((uint32_t)env->regs[R_ECX]) { + case 0 ... 0x1fff: + t0 = (env->regs[R_ECX] * 2) % 8; + t1 = (env->regs[R_ECX] * 2) / 8; + break; + case 0xc0000000 ... 0xc0001fff: + t0 = (8192 + env->regs[R_ECX] - 0xc0000000) * 2; + t1 = (t0 / 8); + t0 %= 8; + break; + case 0xc0010000 ... 0xc0011fff: + t0 = (16384 + env->regs[R_ECX] - 0xc0010000) * 2; + t1 = (t0 / 8); + t0 %= 8; + break; + default: + cpu_vmexit(env, type, param, retaddr); + t0 = 0; + t1 = 0; + break; + } + if (x86_ldub_phys(cs, addr + t1) & ((1 << param) << t0)) { + cpu_vmexit(env, type, param, retaddr); + } + return; + } + + cpu_vmexit(env, type, param, retaddr); } void helper_svm_check_intercept(CPUX86State *env, uint32_t type) From 7eb54ca95d369135f2570c10daf1a41a1f8a6b9c Mon Sep 17 00:00:00 2001 From: Lara Lazier Date: Wed, 16 Jun 2021 14:39:05 +0200 Subject: [PATCH 35/45] target/i386: Added consistency checks for VMRUN intercept and ASID Zero VMRUN intercept and ASID should cause an immediate VMEXIT during the consistency checks performed by VMRUN. (AMD64 Architecture Programmer's Manual, V2, 15.5) Signed-off-by: Lara Lazier Message-Id: <20210616123907.17765-3-laramglazier@gmail.com> Signed-off-by: Paolo Bonzini --- target/i386/tcg/sysemu/svm_helper.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/sysemu/svm_helper.c index 2f7606bebf4a..902bf03fc376 100644 --- a/target/i386/tcg/sysemu/svm_helper.c +++ b/target/i386/tcg/sysemu/svm_helper.c @@ -72,6 +72,7 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) uint64_t nested_ctl; uint32_t event_inj; uint32_t int_ctl; + uint32_t asid; cpu_svm_check_intercept_param(env, SVM_EXIT_VMRUN, 0, GETPC()); @@ -154,9 +155,18 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) nested_ctl = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.nested_ctl)); + asid = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + control.asid)); env->nested_pg_mode = 0; + if (!cpu_svm_has_intercept(env, SVM_EXIT_VMRUN)) { + cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); + } + if (asid == 0) { + cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); + } + if (nested_ctl & SVM_NPT_ENABLED) { env->nested_cr3 = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, From 498df2a7470e09d6cb0204f45eeb30d7ae796465 Mon Sep 17 00:00:00 2001 From: Lara Lazier Date: Wed, 16 Jun 2021 14:39:06 +0200 Subject: [PATCH 36/45] target/i386: Added consistency checks for CR0 The combination of unset CD and set NW bit in CR0 is illegal. CR0[63:32] are also reserved and need to be zero. (AMD64 Architecture Programmer's Manual, V2, 15.5) Signed-off-by: Lara Lazier Message-Id: <20210616123907.17765-4-laramglazier@gmail.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 2 ++ target/i386/svm.h | 2 ++ target/i386/tcg/sysemu/svm_helper.c | 12 +++++++++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 64b4e4673189..1e11071d817b 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -224,6 +224,8 @@ typedef enum X86Seg { #define CR0_NE_MASK (1U << 5) #define CR0_WP_MASK (1U << 16) #define CR0_AM_MASK (1U << 18) +#define CR0_NW_MASK (1U << 29) +#define CR0_CD_MASK (1U << 30) #define CR0_PG_MASK (1U << 31) #define CR4_VME_MASK (1U << 0) diff --git a/target/i386/svm.h b/target/i386/svm.h index 87965e5bc26d..5098733053ab 100644 --- a/target/i386/svm.h +++ b/target/i386/svm.h @@ -135,6 +135,8 @@ #define SVM_NPTEXIT_GPA (1ULL << 32) #define SVM_NPTEXIT_GPT (1ULL << 33) +#define SVM_CR0_RESERVED_MASK 0xffffffff00000000U + struct QEMU_PACKED vmcb_control_area { uint16_t intercept_cr_read; uint16_t intercept_cr_write; diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/sysemu/svm_helper.c index 902bf03fc376..1c2dbc18621a 100644 --- a/target/i386/tcg/sysemu/svm_helper.c +++ b/target/i386/tcg/sysemu/svm_helper.c @@ -73,6 +73,7 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) uint32_t event_inj; uint32_t int_ctl; uint32_t asid; + uint64_t new_cr0; cpu_svm_check_intercept_param(env, SVM_EXIT_VMRUN, 0, GETPC()); @@ -192,13 +193,18 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->idt.limit = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit)); + new_cr0 = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cr0)); + if (new_cr0 & SVM_CR0_RESERVED_MASK) { + cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); + } + if ((new_cr0 & CR0_NW_MASK) && !(new_cr0 & CR0_CD_MASK)) { + cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); + } /* clear exit_info_2 so we behave like the real hardware */ x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), 0); - cpu_x86_update_cr0(env, x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, - save.cr0))); + cpu_x86_update_cr0(env, new_cr0); cpu_x86_update_cr4(env, x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cr4))); From e0375ec760d3c49163eb16f272349dc16f13e59c Mon Sep 17 00:00:00 2001 From: Lara Lazier Date: Wed, 16 Jun 2021 14:39:07 +0200 Subject: [PATCH 37/45] target/i386: Added Intercept CR0 writes check When the selective CR0 write intercept is set, all writes to bits in CR0 other than CR0.TS or CR0.MP cause a VMEXIT. Signed-off-by: Lara Lazier Message-Id: <20210616123907.17765-5-laramglazier@gmail.com> Signed-off-by: Paolo Bonzini --- target/i386/tcg/sysemu/misc_helper.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/i386/tcg/sysemu/misc_helper.c b/target/i386/tcg/sysemu/misc_helper.c index 0cef2f1a4c3b..db0d8a9d7956 100644 --- a/target/i386/tcg/sysemu/misc_helper.c +++ b/target/i386/tcg/sysemu/misc_helper.c @@ -84,6 +84,15 @@ void helper_write_crN(CPUX86State *env, int reg, target_ulong t0) { switch (reg) { case 0: + /* + * If we reach this point, the CR0 write intercept is disabled. + * But we could still exit if the hypervisor has requested the selective + * intercept for bits other than TS and MP + */ + if (cpu_svm_has_intercept(env, SVM_EXIT_CR0_SEL_WRITE) && + ((env->cr[0] ^ t0) & ~(CR0_TS_MASK | CR0_MP_MASK))) { + cpu_vmexit(env, SVM_EXIT_CR0_SEL_WRITE, 0, GETPC()); + } cpu_x86_update_cr0(env, t0); break; case 3: From 8a9d3d564093dbd5a7339085406e840893944d21 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Jun 2021 16:31:36 -0700 Subject: [PATCH 38/45] configure: Use -std=gnu11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the minimum gcc version is 7.5, we can use C11. This will allow lots of cleanups to the code, currently hidden behind macros in include/qemu/compiler.h. Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20210614233143.1221879-2-richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini --- configure | 4 ++-- meson.build | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configure b/configure index 4478f3889ac1..ebc016111a6f 100755 --- a/configure +++ b/configure @@ -159,7 +159,7 @@ update_cxxflags() { # options which some versions of GCC's C++ compiler complain about # because they only make sense for C programs. QEMU_CXXFLAGS="$QEMU_CXXFLAGS -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS" - CONFIGURE_CXXFLAGS=$(echo "$CONFIGURE_CFLAGS" | sed s/-std=gnu99/-std=gnu++11/) + CONFIGURE_CXXFLAGS=$(echo "$CONFIGURE_CFLAGS" | sed s/-std=gnu11/-std=gnu++11/) for arg in $QEMU_CFLAGS; do case $arg in -Wstrict-prototypes|-Wmissing-prototypes|-Wnested-externs|\ @@ -538,7 +538,7 @@ QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS" QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS" # Flags that are needed during configure but later taken care of by Meson -CONFIGURE_CFLAGS="-std=gnu99 -Wall" +CONFIGURE_CFLAGS="-std=gnu11 -Wall" CONFIGURE_LDFLAGS= diff --git a/meson.build b/meson.build index a2311eda6ec5..d8a92666fbcd 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('qemu', ['c'], meson_version: '>=0.55.0', - default_options: ['warning_level=1', 'c_std=gnu99', 'cpp_std=gnu++11', 'b_colorout=auto'] + + default_options: ['warning_level=1', 'c_std=gnu11', 'cpp_std=gnu++11', 'b_colorout=auto'] + (meson.version().version_compare('>=0.56.0') ? [ 'b_staticpic=false' ] : []), version: run_command('head', meson.source_root() / 'VERSION').stdout().strip()) From 52a80715799122c0a31e68c66a10ca901f6454ab Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Jun 2021 16:31:37 -0700 Subject: [PATCH 39/45] softfloat: Use _Generic instead of QEMU_GENERIC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Reviewed-by: Thomas Huth Message-Id: <20210614233143.1221879-3-richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini --- fpu/softfloat.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 4d0160fe9c2d..6e769f990c2b 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -686,11 +686,13 @@ static float128 float128_pack_raw(const FloatParts128 *p) #include "softfloat-specialize.c.inc" #define PARTS_GENERIC_64_128(NAME, P) \ - QEMU_GENERIC(P, (FloatParts128 *, parts128_##NAME), parts64_##NAME) + _Generic((P), FloatParts64 *: parts64_##NAME, \ + FloatParts128 *: parts128_##NAME) #define PARTS_GENERIC_64_128_256(NAME, P) \ - QEMU_GENERIC(P, (FloatParts256 *, parts256_##NAME), \ - (FloatParts128 *, parts128_##NAME), parts64_##NAME) + _Generic((P), FloatParts64 *: parts64_##NAME, \ + FloatParts128 *: parts128_##NAME, \ + FloatParts256 *: parts256_##NAME) #define parts_default_nan(P, S) PARTS_GENERIC_64_128(default_nan, P)(P, S) #define parts_silence_nan(P, S) PARTS_GENERIC_64_128(silence_nan, P)(P, S) @@ -892,11 +894,13 @@ static void parts128_log2(FloatParts128 *a, float_status *s, const FloatFmt *f); */ #define FRAC_GENERIC_64_128(NAME, P) \ - QEMU_GENERIC(P, (FloatParts128 *, frac128_##NAME), frac64_##NAME) + _Generic((P), FloatParts64 *: frac64_##NAME, \ + FloatParts128 *: frac128_##NAME) #define FRAC_GENERIC_64_128_256(NAME, P) \ - QEMU_GENERIC(P, (FloatParts256 *, frac256_##NAME), \ - (FloatParts128 *, frac128_##NAME), frac64_##NAME) + _Generic((P), FloatParts64 *: frac64_##NAME, \ + FloatParts128 *: frac128_##NAME, \ + FloatParts256 *: frac256_##NAME) static bool frac64_add(FloatParts64 *r, FloatParts64 *a, FloatParts64 *b) { From 5d63bd5aad0d80f9c3901c0948354ee3fb7da9bc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Jun 2021 16:31:38 -0700 Subject: [PATCH 40/45] util: Use real functions for thread-posix QemuRecMutex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the declarations from thread-win32.h into thread.h and remove the macro redirection from thread-posix.h. This will be required by following cleanups. Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Reviewed-by: Thomas Huth Message-Id: <20210614233143.1221879-4-richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini --- include/qemu/thread-posix.h | 4 ---- include/qemu/thread-win32.h | 6 ------ include/qemu/thread.h | 9 ++++++--- util/qemu-thread-posix.c | 20 ++++++++++++++++++++ 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h index c903525062e2..cf8bc904684d 100644 --- a/include/qemu/thread-posix.h +++ b/include/qemu/thread-posix.h @@ -5,10 +5,6 @@ #include typedef QemuMutex QemuRecMutex; -#define qemu_rec_mutex_destroy qemu_mutex_destroy -#define qemu_rec_mutex_lock_impl qemu_mutex_lock_impl -#define qemu_rec_mutex_trylock_impl qemu_mutex_trylock_impl -#define qemu_rec_mutex_unlock qemu_mutex_unlock struct QemuMutex { pthread_mutex_t lock; diff --git a/include/qemu/thread-win32.h b/include/qemu/thread-win32.h index d0a1a9597eb0..d95af4498fc9 100644 --- a/include/qemu/thread-win32.h +++ b/include/qemu/thread-win32.h @@ -18,12 +18,6 @@ struct QemuRecMutex { bool initialized; }; -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(QemuRecMutex *mutex); - struct QemuCond { CONDITION_VARIABLE var; bool initialized; diff --git a/include/qemu/thread.h b/include/qemu/thread.h index 54357631846f..2c0d85f3bc58 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -28,6 +28,12 @@ int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line); void qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, const int line); void 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(QemuRecMutex *mutex); + 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); @@ -129,9 +135,6 @@ static inline int (qemu_rec_mutex_trylock)(QemuRecMutex *mutex) return qemu_rec_mutex_trylock(mutex); } -/* Prototypes for other functions are in thread-posix.h/thread-win32.h. */ -void qemu_rec_mutex_init(QemuRecMutex *mutex); - void qemu_cond_init(QemuCond *cond); void qemu_cond_destroy(QemuCond *cond); diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index dcff5e7c5d67..8e2b6653f508 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -124,6 +124,26 @@ void qemu_rec_mutex_init(QemuRecMutex *mutex) mutex->initialized = true; } +void qemu_rec_mutex_destroy(QemuRecMutex *mutex) +{ + qemu_mutex_destroy(mutex); +} + +void qemu_rec_mutex_lock_impl(QemuRecMutex *mutex, const char *file, int line) +{ + qemu_mutex_lock_impl(mutex, file, line); +} + +int qemu_rec_mutex_trylock_impl(QemuRecMutex *mutex, const char *file, int line) +{ + return qemu_mutex_trylock_impl(mutex, file, line); +} + +void qemu_rec_mutex_unlock(QemuRecMutex *mutex) +{ + qemu_mutex_unlock(mutex); +} + void qemu_cond_init(QemuCond *cond) { int err; From d3192460bffdf31e830a9ef1261b587259e7ebd7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Jun 2021 16:31:39 -0700 Subject: [PATCH 41/45] util: Pass file+line to qemu_rec_mutex_unlock_impl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create macros for file+line expansion in qemu_rec_mutex_unlock like we have for qemu_mutex_unlock. Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210614233143.1221879-5-richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini --- include/qemu/thread.h | 10 +++++++++- util/qemu-thread-posix.c | 4 ++-- util/qemu-thread-win32.c | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/include/qemu/thread.h b/include/qemu/thread.h index 2c0d85f3bc58..460568d67d53 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -32,7 +32,7 @@ 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(QemuRecMutex *mutex); +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); @@ -110,6 +110,9 @@ extern QemuCondTimedWaitFunc qemu_cond_timedwait_func; #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); @@ -135,6 +138,11 @@ 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); diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index 8e2b6653f508..d990826ed8da 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -139,9 +139,9 @@ int qemu_rec_mutex_trylock_impl(QemuRecMutex *mutex, const char *file, int line) return qemu_mutex_trylock_impl(mutex, file, line); } -void qemu_rec_mutex_unlock(QemuRecMutex *mutex) +void qemu_rec_mutex_unlock_impl(QemuRecMutex *mutex, const char *file, int line) { - qemu_mutex_unlock(mutex); + qemu_mutex_unlock_impl(mutex, file, line); } void qemu_cond_init(QemuCond *cond) diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index cb5aa2018c3a..52eb19f3511a 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -105,7 +105,7 @@ int qemu_rec_mutex_trylock_impl(QemuRecMutex *mutex, const char *file, int line) return !TryEnterCriticalSection(&mutex->lock); } -void qemu_rec_mutex_unlock(QemuRecMutex *mutex) +void qemu_rec_mutex_unlock_impl(QemuRecMutex *mutex, const char *file, int line) { assert(mutex->initialized); LeaveCriticalSection(&mutex->lock); From dc41737844dbec4736855e128dce0da20c082f65 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Jun 2021 16:31:40 -0700 Subject: [PATCH 42/45] util: Use unique type for QemuRecMutex in thread-posix.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We will shortly convert lockable.h to _Generic, and we cannot have two compatible types in the same expansion. Wrap QemuMutex in a struct, and unwrap in qemu-thread-posix.c. Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Reviewed-by: Thomas Huth Message-Id: <20210614233143.1221879-6-richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini --- include/qemu/thread-posix.h | 10 ++++++++-- util/qemu-thread-posix.c | 12 ++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h index cf8bc904684d..b792e6ef377b 100644 --- a/include/qemu/thread-posix.h +++ b/include/qemu/thread-posix.h @@ -4,8 +4,6 @@ #include #include -typedef QemuMutex QemuRecMutex; - struct QemuMutex { pthread_mutex_t lock; #ifdef CONFIG_DEBUG_MUTEX @@ -15,6 +13,14 @@ struct QemuMutex { 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; diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index d990826ed8da..fd9d7140381a 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -116,32 +116,32 @@ void qemu_rec_mutex_init(QemuRecMutex *mutex) pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - err = pthread_mutex_init(&mutex->lock, &attr); + err = pthread_mutex_init(&mutex->m.lock, &attr); pthread_mutexattr_destroy(&attr); if (err) { error_exit(err, __func__); } - mutex->initialized = true; + mutex->m.initialized = true; } void qemu_rec_mutex_destroy(QemuRecMutex *mutex) { - qemu_mutex_destroy(mutex); + qemu_mutex_destroy(&mutex->m); } void qemu_rec_mutex_lock_impl(QemuRecMutex *mutex, const char *file, int line) { - qemu_mutex_lock_impl(mutex, file, 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, file, 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, file, line); + qemu_mutex_unlock_impl(&mutex->m, file, line); } void qemu_cond_init(QemuCond *cond) From 23c9b7e0f3ddc727fc664b616a3d73a24adef9d0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Jun 2021 16:31:41 -0700 Subject: [PATCH 43/45] include/qemu/lockable: Use _Generic instead of QEMU_GENERIC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is both more and less complicated than our expansion using __builtin_choose_expr and __builtin_types_compatible_p. The expansion through QEMU_MAKE_LOCKABLE_ doesn't work because we're not emumerating all of the types within the same _Generic, which results in errors about unhandled cases. We must also handle void* explicitly, so that the NULL constant can be used. Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-Id: <20210614233143.1221879-7-richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini --- include/qemu/lockable.h | 88 +++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 48 deletions(-) diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h index b62002314180..86db7cb04c9c 100644 --- a/include/qemu/lockable.h +++ b/include/qemu/lockable.h @@ -24,79 +24,71 @@ struct QemuLockable { QemuLockUnlockFunc *unlock; }; -/* This function gives an error if an invalid, non-NULL pointer type is passed - * to QEMU_MAKE_LOCKABLE. For optimized builds, we can rely on dead-code elimination - * from the compiler, and give the errors already at link time. - */ -#if defined(__OPTIMIZE__) && !defined(__SANITIZE_ADDRESS__) -void unknown_lock_type(void *); -#else -static inline void unknown_lock_type(void *unused) -{ - abort(); -} -#endif - static inline __attribute__((__always_inline__)) QemuLockable * qemu_make_lockable(void *x, QemuLockable *lockable) { - /* We cannot test this in a macro, otherwise we get compiler + /* + * 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; } -/* Auxiliary macros to simplify QEMU_MAKE_LOCABLE. */ -#define QEMU_LOCK_FUNC(x) ((QemuLockUnlockFunc *) \ - QEMU_GENERIC(x, \ - (QemuMutex *, qemu_mutex_lock), \ - (QemuRecMutex *, qemu_rec_mutex_lock), \ - (CoMutex *, qemu_co_mutex_lock), \ - (QemuSpin *, qemu_spin_lock), \ - unknown_lock_type)) - -#define QEMU_UNLOCK_FUNC(x) ((QemuLockUnlockFunc *) \ - QEMU_GENERIC(x, \ - (QemuMutex *, qemu_mutex_unlock), \ - (QemuRecMutex *, qemu_rec_mutex_unlock), \ - (CoMutex *, qemu_co_mutex_unlock), \ - (QemuSpin *, qemu_spin_unlock), \ - unknown_lock_type)) - -/* In C, compound literals have the lifetime of an automatic variable. +static inline __attribute__((__always_inline__)) QemuLockable * +qemu_null_lockable(void *x) +{ + if (x != NULL) { + qemu_build_not_reached(); + } + return NULL; +} + +/* + * 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 QEMU_MAKE_LOCKABLE_(x) (&(QemuLockable) { \ - .object = (x), \ - .lock = QEMU_LOCK_FUNC(x), \ - .unlock = QEMU_UNLOCK_FUNC(x), \ +#define QML_OBJ_(x, name) (&(QemuLockable) { \ + .object = (x), \ + .lock = (QemuLockUnlockFunc *) qemu_ ## name ## _lock, \ + .unlock = (QemuLockUnlockFunc *) qemu_ ## name ## _unlock \ }) -/* QEMU_MAKE_LOCKABLE - Make a polymorphic QemuLockable +/** + * QEMU_MAKE_LOCKABLE - Make a polymorphic QemuLockable * - * @x: a lock object (currently one of QemuMutex, QemuRecMutex, CoMutex, QemuSpin). + * @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) \ - QEMU_GENERIC(x, \ - (QemuLockable *, (x)), \ - qemu_make_lockable((x), QEMU_MAKE_LOCKABLE_(x))) +#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 +/** + * QEMU_MAKE_LOCKABLE_NONNULL - Make a polymorphic QemuLockable * - * @x: a lock object (currently one of QemuMutex, QemuRecMutex, CoMutex, QemuSpin). + * @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) \ - QEMU_GENERIC(x, \ - (QemuLockable *, (x)), \ - QEMU_MAKE_LOCKABLE_(x)) +#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) { From de51d8cbf0f9a9745ac02fb07e02063b7dfe35b9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Jun 2021 16:31:42 -0700 Subject: [PATCH 44/45] qemu/compiler: Remove QEMU_GENERIC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All previous users now use C11 _Generic. Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Message-Id: <20210614233143.1221879-8-richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini --- include/qemu/compiler.h | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 091c45248b04..5766d615890a 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -173,46 +173,6 @@ #define QEMU_ALWAYS_INLINE #endif -/* Implement C11 _Generic via GCC builtins. Example: - * - * QEMU_GENERIC(x, (float, sinf), (long double, sinl), sin) (x) - * - * The first argument is the discriminator. The last is the default value. - * The middle ones are tuples in "(type, expansion)" format. - */ - -/* First, find out the number of generic cases. */ -#define QEMU_GENERIC(x, ...) \ - QEMU_GENERIC_(typeof(x), __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) - -/* There will be extra arguments, but they are not used. */ -#define QEMU_GENERIC_(x, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, count, ...) \ - QEMU_GENERIC##count(x, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) - -/* Two more helper macros, this time to extract items from a parenthesized - * list. - */ -#define QEMU_FIRST_(a, b) a -#define QEMU_SECOND_(a, b) b - -/* ... and a final one for the common part of the "recursion". */ -#define QEMU_GENERIC_IF(x, type_then, else_) \ - __builtin_choose_expr(__builtin_types_compatible_p(x, \ - QEMU_FIRST_ type_then), \ - QEMU_SECOND_ type_then, else_) - -/* CPP poor man's "recursion". */ -#define QEMU_GENERIC1(x, a0, ...) (a0) -#define QEMU_GENERIC2(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC1(x, __VA_ARGS__)) -#define QEMU_GENERIC3(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC2(x, __VA_ARGS__)) -#define QEMU_GENERIC4(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC3(x, __VA_ARGS__)) -#define QEMU_GENERIC5(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC4(x, __VA_ARGS__)) -#define QEMU_GENERIC6(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC5(x, __VA_ARGS__)) -#define QEMU_GENERIC7(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC6(x, __VA_ARGS__)) -#define QEMU_GENERIC8(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC7(x, __VA_ARGS__)) -#define QEMU_GENERIC9(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC8(x, __VA_ARGS__)) -#define QEMU_GENERIC10(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC9(x, __VA_ARGS__)) - /** * qemu_build_not_reached() * From f51f8e3591393f7f274e1435ac22188e2dafdfe8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 14 Jun 2021 16:31:43 -0700 Subject: [PATCH 45/45] configure: Remove probe for _Static_assert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _Static_assert is part of C11, which is now required. Signed-off-by: Richard Henderson Reviewed-by: Alex Bennée Reviewed-by: Thomas Huth Message-Id: <20210614233143.1221879-9-richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini --- configure | 18 ------------------ include/qemu/compiler.h | 11 ----------- 2 files changed, 29 deletions(-) diff --git a/configure b/configure index ebc016111a6f..262ab71802eb 100755 --- a/configure +++ b/configure @@ -5090,20 +5090,6 @@ if compile_prog "" "" ; then have_sysmacros=yes fi -########################################## -# check for _Static_assert() - -have_static_assert=no -cat > $TMPC << EOF -_Static_assert(1, "success"); -int main(void) { - return 0; -} -EOF -if compile_prog "" "" ; then - have_static_assert=yes -fi - ########################################## # check for utmpx.h, it is missing e.g. on OpenBSD @@ -6035,10 +6021,6 @@ if test "$have_sysmacros" = "yes" ; then echo "CONFIG_SYSMACROS=y" >> $config_host_mak fi -if test "$have_static_assert" = "yes" ; then - echo "CONFIG_STATIC_ASSERT=y" >> $config_host_mak -fi - if test "$have_utmpx" = "yes" ; then echo "HAVE_UTMPX=y" >> $config_host_mak fi diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 5766d615890a..3baa5e3790f7 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -72,18 +72,7 @@ int:(x) ? -1 : 1; \ } -/* QEMU_BUILD_BUG_MSG() emits the message given if _Static_assert is - * supported; otherwise, it will be omitted from the compiler error - * message (but as it remains present in the source code, it can still - * be useful when debugging). */ -#if defined(CONFIG_STATIC_ASSERT) #define QEMU_BUILD_BUG_MSG(x, msg) _Static_assert(!(x), msg) -#elif defined(__COUNTER__) -#define QEMU_BUILD_BUG_MSG(x, msg) typedef QEMU_BUILD_BUG_ON_STRUCT(x) \ - glue(qemu_build_bug_on__, __COUNTER__) __attribute__((unused)) -#else -#define QEMU_BUILD_BUG_MSG(x, msg) -#endif #define QEMU_BUILD_BUG_ON(x) QEMU_BUILD_BUG_MSG(x, "not expecting: " #x)