From: Michael Tokarev <mjt@tls.msk.ru>
To: qemu-devel@nongnu.org
Cc: qemu-stable@nongnu.org, Niklas Cassel <niklas.cassel@wdc.com>,
John Snow <jsnow@redhat.com>, Michael Tokarev <mjt@tls.msk.ru>
Subject: [Stable-7.2.6 27/37] hw/ide/ahci: simplify and document PxCI handling
Date: Sat, 9 Sep 2023 16:04:57 +0300 [thread overview]
Message-ID: <20230909130511.354171-27-mjt@tls.msk.ru> (raw)
In-Reply-To: <qemu-stable-7.2.6-20230909160328@cover.tls.msk.ru>
From: Niklas Cassel <niklas.cassel@wdc.com>
The AHCI spec states that:
For NCQ, PxCI is cleared on command queued successfully.
For non-NCQ, PxCI is cleared on command completed successfully.
(A non-NCQ command that completes with error does not clear PxCI.)
The current QEMU implementation either clears PxCI in check_cmd(),
or in ahci_cmd_done().
check_cmd() will clear PxCI for a command if handle_cmd() returns 0.
handle_cmd() will return -1 if BUSY or DRQ is set.
The QEMU implementation for NCQ commands will currently not set BUSY
or DRQ, so they will always have PxCI cleared by handle_cmd().
ahci_cmd_done() will never even get called for NCQ commands.
Non-NCQ commands are executed by ide_bus_exec_cmd().
Non-NCQ commands in QEMU are implemented either in a sync or in an async
way.
For non-NCQ commands implemented in a sync way, the command handler will
return true, and when ide_bus_exec_cmd() sees that a command handler
returns true, it will call ide_cmd_done() (which will call
ahci_cmd_done()). For a command implemented in a sync way,
ahci_cmd_done() will do nothing (since busy_slot is not set). Instead,
after ide_bus_exec_cmd() has finished, check_cmd() will clear PxCI for
these commands.
For non-NCQ commands implemented in an async way (using either aiocb or
pio_aiocb), the command handler will return false, ide_bus_exec_cmd()
will not call ide_cmd_done(), instead it is expected that the async
callback function will call ide_cmd_done() once the async command is
done. handle_cmd() will set busy_slot, if and only if BUSY or DRQ is
set, and this is checked _after_ ide_bus_exec_cmd() has returned.
handle_cmd() will return -1, so check_cmd() will not clear PxCI.
When the async callback calls ide_cmd_done() (which will call
ahci_cmd_done()), it will see that busy_slot is set, and
ahci_cmd_done() will clear PxCI.
This seems racy, since busy_slot is set _after_ ide_bus_exec_cmd() has
returned. The callback might come before busy_slot gets set. And it is
quite confusing that ahci_cmd_done() will be called for all non-NCQ
commands when the command is done, but will only clear PxCI in certain
cases, even though it will always write a D2H FIS and raise an IRQ.
Even worse, in the case where ahci_cmd_done() does not clear PxCI, it
still raises an IRQ. Host software might thus read an old PxCI value,
since PxCI is cleared (by check_cmd()) after the IRQ has been raised.
Try to simplify this by always setting busy_slot for non-NCQ commands,
such that ahci_cmd_done() will always be responsible for clearing PxCI
for non-NCQ commands.
For NCQ commands, clear PxCI when we receive the D2H FIS, but before
raising the IRQ, see AHCI 1.3.1, section 5.3.8, states RegFIS:Entry and
RegFIS:ClearCI.
Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
Message-id: 20230609140844.202795-5-nks@flawful.org
Signed-off-by: John Snow <jsnow@redhat.com>
(cherry picked from commit e2a5d9b3d9c3d311618160603cc9bc04fbd98796)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 54b741c7b3..24ee3cee21 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -40,9 +40,10 @@
#include "trace.h"
static void check_cmd(AHCIState *s, int port);
-static int handle_cmd(AHCIState *s, int port, uint8_t slot);
+static void handle_cmd(AHCIState *s, int port, uint8_t slot);
static void ahci_reset_port(AHCIState *s, int port);
static bool ahci_write_fis_d2h(AHCIDevice *ad, bool d2h_fis_i);
+static void ahci_clear_cmd_issue(AHCIDevice *ad, uint8_t slot);
static void ahci_init_d2h(AHCIDevice *ad);
static int ahci_dma_prepare_buf(const IDEDMA *dma, int32_t limit);
static bool ahci_map_clb_address(AHCIDevice *ad);
@@ -590,9 +591,8 @@ static void check_cmd(AHCIState *s, int port)
if ((pr->cmd & PORT_CMD_START) && pr->cmd_issue) {
for (slot = 0; (slot < 32) && pr->cmd_issue; slot++) {
- if ((pr->cmd_issue & (1U << slot)) &&
- !handle_cmd(s, port, slot)) {
- pr->cmd_issue &= ~(1U << slot);
+ if (pr->cmd_issue & (1U << slot)) {
+ handle_cmd(s, port, slot);
}
}
}
@@ -1122,6 +1122,22 @@ static void process_ncq_command(AHCIState *s, int port, const uint8_t *cmd_fis,
return;
}
+ /*
+ * A NCQ command clears the bit in PxCI after the command has been QUEUED
+ * successfully (ERROR not set, BUSY and DRQ cleared).
+ *
+ * For NCQ commands, PxCI will always be cleared here.
+ *
+ * (Once the NCQ command is COMPLETED, the device will send a SDB FIS with
+ * the interrupt bit set, which will clear PxSACT and raise an interrupt.)
+ */
+ ahci_clear_cmd_issue(ad, slot);
+
+ /*
+ * In reality, for NCQ commands, PxCI is cleared after receiving a D2H FIS
+ * without the interrupt bit set, but since ahci_write_fis_d2h() can raise
+ * an IRQ on error, we need to call them in reverse order.
+ */
ahci_write_fis_d2h(ad, false);
ncq_tfs->used = 1;
@@ -1196,6 +1212,7 @@ static void handle_reg_h2d_fis(AHCIState *s, int port,
{
IDEState *ide_state = &s->dev[port].port.ifs[0];
AHCICmdHdr *cmd = get_cmd_header(s, port, slot);
+ AHCIDevice *ad = &s->dev[port];
uint16_t opts = le16_to_cpu(cmd->opts);
if (cmd_fis[1] & 0x0F) {
@@ -1272,11 +1289,19 @@ static void handle_reg_h2d_fis(AHCIState *s, int port,
/* Reset transferred byte counter */
cmd->status = 0;
+ /*
+ * A non-NCQ command clears the bit in PxCI after the command has COMPLETED
+ * successfully (ERROR not set, BUSY and DRQ cleared).
+ *
+ * For non-NCQ commands, PxCI will always be cleared by ahci_cmd_done().
+ */
+ ad->busy_slot = slot;
+
/* We're ready to process the command in FIS byte 2. */
ide_exec_cmd(&s->dev[port].port, cmd_fis[2]);
}
-static int handle_cmd(AHCIState *s, int port, uint8_t slot)
+static void handle_cmd(AHCIState *s, int port, uint8_t slot)
{
IDEState *ide_state;
uint64_t tbl_addr;
@@ -1287,12 +1312,12 @@ static int handle_cmd(AHCIState *s, int port, uint8_t slot)
if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) {
/* Engine currently busy, try again later */
trace_handle_cmd_busy(s, port);
- return -1;
+ return;
}
if (!s->dev[port].lst) {
trace_handle_cmd_nolist(s, port);
- return -1;
+ return;
}
cmd = get_cmd_header(s, port, slot);
/* remember current slot handle for later */
@@ -1302,7 +1327,7 @@ static int handle_cmd(AHCIState *s, int port, uint8_t slot)
ide_state = &s->dev[port].port.ifs[0];
if (!ide_state->blk) {
trace_handle_cmd_badport(s, port);
- return -1;
+ return;
}
tbl_addr = le64_to_cpu(cmd->tbl_addr);
@@ -1311,7 +1336,7 @@ static int handle_cmd(AHCIState *s, int port, uint8_t slot)
DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED);
if (!cmd_fis) {
trace_handle_cmd_badfis(s, port);
- return -1;
+ return;
} else if (cmd_len != 0x80) {
ahci_trigger_irq(s, &s->dev[port], AHCI_PORT_IRQ_BIT_HBFS);
trace_handle_cmd_badmap(s, port, cmd_len);
@@ -1335,15 +1360,6 @@ static int handle_cmd(AHCIState *s, int port, uint8_t slot)
out:
dma_memory_unmap(s->as, cmd_fis, cmd_len, DMA_DIRECTION_TO_DEVICE,
cmd_len);
-
- if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) {
- /* async command, complete later */
- s->dev[port].busy_slot = slot;
- return -1;
- }
-
- /* done handling the command */
- return 0;
}
/* Transfer PIO data between RAM and device */
@@ -1497,6 +1513,16 @@ static int ahci_dma_rw_buf(const IDEDMA *dma, bool is_write)
return 1;
}
+static void ahci_clear_cmd_issue(AHCIDevice *ad, uint8_t slot)
+{
+ IDEState *ide_state = &ad->port.ifs[0];
+
+ if (!(ide_state->status & (BUSY_STAT | DRQ_STAT))) {
+ ad->port_regs.cmd_issue &= ~(1 << slot);
+ }
+}
+
+/* Non-NCQ command is done - This function is never called for NCQ commands. */
static void ahci_cmd_done(const IDEDMA *dma)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
@@ -1505,11 +1531,15 @@ static void ahci_cmd_done(const IDEDMA *dma)
/* no longer busy */
if (ad->busy_slot != -1) {
- ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot);
+ ahci_clear_cmd_issue(ad, ad->busy_slot);
ad->busy_slot = -1;
}
- /* update d2h status */
+ /*
+ * In reality, for non-NCQ commands, PxCI is cleared after receiving a D2H
+ * FIS with the interrupt bit set, but since ahci_write_fis_d2h() will raise
+ * an IRQ, we need to call them in reverse order.
+ */
ahci_write_fis_d2h(ad, true);
if (ad->port_regs.cmd_issue && !ad->check_bh) {
--
2.39.2
next prev parent reply other threads:[~2023-09-09 13:08 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-09-09 13:04 [Stable-7.2.6 00/37] Patch Round-up for stable 7.2.6, freeze on 2023-09-19 Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 01/37] machine: Add helpers to get cores/threads per socket Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 02/37] hw/smbios: Fix smbios_smp_sockets caculation Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 03/37] hw/smbios: Fix thread count in type4 Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 04/37] hw/smbios: Fix core " Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 05/37] dump: kdump-zlib data pages not dumped with pvtime/aarch64 Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 06/37] hw/nvme: fix CRC64 for guard tag Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 07/37] linux-user/elfload: Set V in ELF_HWCAP for RISC-V Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 08/37] include/exec/user: Set ABI_LLONG_ALIGNMENT to 4 for microblaze Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 09/37] include/exec/user: Set ABI_LLONG_ALIGNMENT to 4 for nios2 Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 10/37] Fixed incorrect LLONG alignment for openrisc and cris Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 11/37] target/s390x: Fix the "ignored match" case in VSTRS Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 12/37] target/s390x: Use a 16-bit immediate in VREP Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 13/37] target/s390x: Fix VSTL with a large length Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 14/37] target/s390x: Check reserved bits of VFMIN/VFMAX's M5 Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 15/37] include/hw/virtio/virtio-gpu: Fix virtio-gpu with blob on big endian hosts Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 16/37] kvm: Introduce kvm_arch_get_default_type hook Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 17/37] accel/kvm: Specify default IPA size for arm64 Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 18/37] target/arm: Fix SME ST1Q Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 19/37] target/arm: Fix 64-bit SSRA Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 20/37] docs/about/license: Update LICENSE URL Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 21/37] block-migration: Ensure we don't crash during migration cleanup Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 22/37] hw/ppc/e500: fix broken snapshot replay Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 23/37] ppc/vof: Fix missed fields in VOF cleanup Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 24/37] target/ppc: Flush inputs to zero with NJ in ppc_store_vscr Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 25/37] hw/ide/core: set ERR_STAT in unsupported command completion Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 26/37] hw/ide/ahci: write D2H FIS when processing NCQ command Michael Tokarev
2023-09-09 13:04 ` Michael Tokarev [this message]
2023-09-09 13:04 ` [Stable-7.2.6 28/37] hw/ide/ahci: PxSACT and PxCI is cleared when PxCMD.ST is cleared Michael Tokarev
2023-09-09 13:04 ` [Stable-7.2.6 29/37] hw/ide/ahci: PxCI should not get cleared when ERR_STAT is set Michael Tokarev
2023-09-10 21:07 ` Michael Tokarev
2023-09-11 13:34 ` Niklas Cassel
2023-09-09 13:05 ` [Stable-7.2.6 30/37] hw/ide/ahci: fix ahci_write_fis_sdb() Michael Tokarev
2023-09-09 13:05 ` [Stable-7.2.6 31/37] hw/ide/ahci: fix broken SError handling Michael Tokarev
2023-09-09 13:05 ` [Stable-7.2.6 32/37] hw/i2c/aspeed: Fix Tx count and Rx size error in buffer pool mode Michael Tokarev
2023-09-09 13:05 ` [Stable-7.2.6 33/37] hw/i2c/aspeed: Fix TXBUF transmission start position error Michael Tokarev
2023-09-09 13:05 ` [Stable-7.2.6 34/37] qemu-options.hx: Rephrase the descriptions of the -hd* and -cdrom options Michael Tokarev
2023-09-09 13:05 ` [Stable-7.2.6 35/37] docs tests: Fix use of migrate_set_parameter Michael Tokarev
2023-09-09 13:05 ` [Stable-7.2.6 36/37] hw/net/vmxnet3: Fix guest-triggerable assert() Michael Tokarev
2023-09-09 13:05 ` [Stable-7.2.6 37/37] qxl: don't assert() if device isn't yet initialized Michael Tokarev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230909130511.354171-27-mjt@tls.msk.ru \
--to=mjt@tls.msk.ru \
--cc=jsnow@redhat.com \
--cc=niklas.cassel@wdc.com \
--cc=qemu-devel@nongnu.org \
--cc=qemu-stable@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).