[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PULL 33/42] esp: defer command completion interrupt on incoming data tr
From: |
Mark Cave-Ayland |
Subject: |
[PULL 33/42] esp: defer command completion interrupt on incoming data transfers |
Date: |
Sun, 7 Mar 2021 12:08:41 +0000 |
The MacOS toolbox ROM issues a command to the ESP controller as part of its
"FAST" SCSI routines and then proceeds to read the incoming data soon after
receiving the command completion interrupt.
Unfortunately due to SCSI block transfers being asynchronous the incoming data
may not yet be present causing an underflow error. Resolve this by waiting for
the SCSI subsystem transfer_data callback before raising the command completion
interrupt.
Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
Message-Id: <20210304221103.6369-34-mark.cave-ayland@ilande.co.uk>
---
hw/scsi/esp.c | 66 ++++++++++++++++++++++++++++++++++---------
include/hw/scsi/esp.h | 1 +
2 files changed, 54 insertions(+), 13 deletions(-)
diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 54d008c609..0eecc1d05c 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -183,6 +183,14 @@ static int esp_select(ESPState *s)
esp_raise_irq(s);
return -1;
}
+
+ /*
+ * 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
+ * IRQ mechanism in esp_transfer_data() for DATA IN transfers
+ */
+ s->rregs[ESP_RINTR] |= INTR_FC;
+ s->rregs[ESP_RSEQ] = SEQ_CD;
return 0;
}
@@ -237,18 +245,24 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf,
uint8_t busid)
s->ti_size = datalen;
if (datalen != 0) {
s->rregs[ESP_RSTAT] = STAT_TC;
+ s->rregs[ESP_RSEQ] = SEQ_CD;
esp_set_tc(s, 0);
if (datalen > 0) {
+ /*
+ * Switch to DATA IN phase but wait until initial data xfer is
+ * complete before raising the command completion interrupt
+ */
+ s->data_in_ready = false;
s->rregs[ESP_RSTAT] |= STAT_DI;
} else {
s->rregs[ESP_RSTAT] |= STAT_DO;
+ s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC;
+ esp_raise_irq(s);
+ esp_lower_drq(s);
}
scsi_req_continue(s->current_req);
+ return;
}
- s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC;
- s->rregs[ESP_RSEQ] = SEQ_CD;
- esp_raise_irq(s);
- esp_lower_drq(s);
}
static void do_cmd(ESPState *s)
@@ -439,17 +453,11 @@ static void do_dma_pdma_cb(ESPState *s)
} else {
if (s->async_len == 0) {
if (s->current_req) {
+ /* Defer until the scsi layer has completed */
scsi_req_continue(s->current_req);
+ s->data_in_ready = false;
}
-
- /*
- * If there is still data to be read from the device then
- * complete the DMA operation immediately. Otherwise defer
- * until the scsi layer has completed.
- */
- if (esp_get_tc(s) != 0 || s->ti_size == 0) {
- return;
- }
+ return;
}
if (esp_get_tc(s) != 0) {
@@ -602,12 +610,35 @@ void esp_command_complete(SCSIRequest *req, size_t resid)
void esp_transfer_data(SCSIRequest *req, uint32_t len)
{
ESPState *s = req->hba_private;
+ int to_device = ((s->rregs[ESP_RSTAT] & 7) == STAT_DO);
uint32_t dmalen = esp_get_tc(s);
assert(!s->do_cmd);
trace_esp_transfer_data(dmalen, s->ti_size);
s->async_len = len;
s->async_buf = scsi_req_get_buf(req);
+
+ if (!to_device && !s->data_in_ready) {
+ /*
+ * Initial incoming data xfer is complete so raise command
+ * completion interrupt
+ */
+ s->data_in_ready = true;
+ 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 (dmalen) {
esp_do_dma(s);
} else if (s->ti_size <= 0) {
@@ -869,6 +900,14 @@ static bool esp_is_before_version_5(void *opaque, int
version_id)
return version_id < 5;
}
+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;
+}
+
static int esp_pre_save(void *opaque)
{
ESPState *s = ESP(opaque);
@@ -913,6 +952,7 @@ const VMStateDescription vmstate_esp = {
VMSTATE_UINT32(cmdlen, ESPState),
VMSTATE_UINT32(do_cmd, ESPState),
VMSTATE_UINT32_TEST(mig_dma_left, ESPState, esp_is_before_version_5),
+ VMSTATE_BOOL_TEST(data_in_ready, ESPState, esp_is_version_5),
VMSTATE_END_OF_LIST()
},
};
diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h
index 91f8ffd6c8..61bc317a4c 100644
--- a/include/hw/scsi/esp.h
+++ b/include/hw/scsi/esp.h
@@ -41,6 +41,7 @@ struct ESPState {
uint32_t cmdlen;
uint32_t do_cmd;
+ bool data_in_ready;
int dma_enabled;
uint32_t async_len;
--
2.20.1
- [PULL 23/42] esp: use ti_wptr/ti_rptr to manage the current FIFO position for PDMA, (continued)
- [PULL 23/42] esp: use ti_wptr/ti_rptr to manage the current FIFO position for PDMA, Mark Cave-Ayland, 2021/03/07
- [PULL 24/42] esp: use in-built TC to determine PDMA transfer length, Mark Cave-Ayland, 2021/03/07
- [PULL 25/42] esp: remove CMD pdma_origin, Mark Cave-Ayland, 2021/03/07
- [PULL 26/42] esp: rename get_cmd_cb() to esp_select(), Mark Cave-Ayland, 2021/03/07
- [PULL 28/42] esp: use FIFO for PDMA transfers between initiator and device, Mark Cave-Ayland, 2021/03/07
- [PULL 27/42] esp: fix PDMA target selection, Mark Cave-Ayland, 2021/03/07
- [PULL 29/42] esp: remove pdma_origin from ESPState, Mark Cave-Ayland, 2021/03/07
- [PULL 30/42] esp: add 4 byte PDMA read and write transfers, Mark Cave-Ayland, 2021/03/07
- [PULL 31/42] esp: implement FIFO flush command, Mark Cave-Ayland, 2021/03/07
- [PULL 32/42] esp: latch individual bits in ESP_RINTR register, Mark Cave-Ayland, 2021/03/07
- [PULL 33/42] esp: defer command completion interrupt on incoming data transfers,
Mark Cave-Ayland <=
- [PULL 34/42] esp: remove old deferred command completion mechanism, Mark Cave-Ayland, 2021/03/07
- [PULL 35/42] esp: raise interrupt after every non-DMA byte transferred to the FIFO, Mark Cave-Ayland, 2021/03/07
- [PULL 36/42] esp: add maxlen parameter to get_cmd(), Mark Cave-Ayland, 2021/03/07
- [PULL 37/42] esp: transition to message out phase after SATN and stop command, Mark Cave-Ayland, 2021/03/07
- [PULL 38/42] esp: convert ti_buf from array to Fifo8, Mark Cave-Ayland, 2021/03/07
- [PULL 39/42] esp: convert cmdbuf from array to Fifo8, Mark Cave-Ayland, 2021/03/07
- [PULL 40/42] esp: add trivial implementation of the ESP_RFLAGS register, Mark Cave-Ayland, 2021/03/07
- [PULL 41/42] esp: implement non-DMA transfers in PDMA mode, Mark Cave-Ayland, 2021/03/07
- [PULL 42/42] esp: add support for unaligned accesses, Mark Cave-Ayland, 2021/03/07
- Re: [PULL 00/42] qemu-sparc queue 20210307, Peter Maydell, 2021/03/09