qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: John Snow <jsnow@redhat.com>
To: qemu-block@nongnu.org
Cc: kwolf@redhat.com, pbonzini@redhat.com,
	John Snow <jsnow@redhat.com>,
	qemu-devel@nongnu.org, stefanha@redhat.com
Subject: [Qemu-devel] [PATCH 01/16] ide: add limit to .prepare_buf()
Date: Mon, 22 Jun 2015 20:21:00 -0400	[thread overview]
Message-ID: <1435018875-22527-2-git-send-email-jsnow@redhat.com> (raw)
In-Reply-To: <1435018875-22527-1-git-send-email-jsnow@redhat.com>

prepare_buf should not always grab as many descriptors
as it can, sometimes it should self-limit.

For example, an NCQ transfer of 1 sector with a PRDT that
describes 4GiB of data should not copy 4GiB of data, it
should just transfer that first 512 bytes.

PIO is not affected, because the dma_buf_rw dma helpers
already have a byte limit built-in to them, but DMA/NCQ
will exhaust the entire list regardless of requested size.

AHCI 1.3 specifies in section 6.1.6 Command List Underflow that
NCQ is not required to detect underflow conditions. Non-NCQ
pathways signal underflow by writing to the PRDBC field, which
will already occur by writing the actual transferred byte count
to the PRDBC, signaling the underflow.

Our NCQ pathways aren't required to detect underflow, but since our DMA
backend uses the size of the PRDT to determine the size of the transer,
if our PRDT is bigger than the transaction (the underflow condition) it
doesn't cost us anything to detect it and truncate the PRDT.

This is a recoverable error and is not signaled to the guest, in either
NCQ or normal DMA cases.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 hw/ide/ahci.c     | 27 ++++++++++++++-------------
 hw/ide/core.c     |  5 +++--
 hw/ide/internal.h |  2 +-
 hw/ide/macio.c    |  2 +-
 hw/ide/pci.c      |  5 ++++-
 5 files changed, 23 insertions(+), 18 deletions(-)

diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 95d228f..f873ab1 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -49,7 +49,7 @@ static int handle_cmd(AHCIState *s,int port,int slot);
 static void ahci_reset_port(AHCIState *s, int port);
 static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis);
 static void ahci_init_d2h(AHCIDevice *ad);
-static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write);
+static int ahci_dma_prepare_buf(IDEDMA *dma, int64_t limit, int is_write);
 static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes);
 static bool ahci_map_clb_address(AHCIDevice *ad);
 static bool ahci_map_fis_address(AHCIDevice *ad);
@@ -827,11 +827,12 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
 
 static int prdt_tbl_entry_size(const AHCI_SG *tbl)
 {
+    /* flags_size is zero-based */
     return (le32_to_cpu(tbl->flags_size) & AHCI_PRDT_SIZE_MASK) + 1;
 }
 
 static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
-                                int32_t offset)
+                                int64_t limit, int32_t offset)
 {
     AHCICmdHdr *cmd = ad->cur_cmd;
     uint16_t opts = le16_to_cpu(cmd->opts);
@@ -881,9 +882,8 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
         AHCI_SG *tbl = (AHCI_SG *)prdt;
         sum = 0;
         for (i = 0; i < prdtl; i++) {
-            /* flags_size is zero-based */
             tbl_entry_size = prdt_tbl_entry_size(&tbl[i]);
-            if (offset <= (sum + tbl_entry_size)) {
+            if (offset < (sum + tbl_entry_size)) {
                 off_idx = i;
                 off_pos = offset - sum;
                 break;
@@ -901,12 +901,13 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
         qemu_sglist_init(sglist, qbus->parent, (prdtl - off_idx),
                          ad->hba->as);
         qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr) + off_pos,
-                        prdt_tbl_entry_size(&tbl[off_idx]) - off_pos);
+                        MIN(prdt_tbl_entry_size(&tbl[off_idx]) - off_pos,
+                            limit));
 
-        for (i = off_idx + 1; i < prdtl; i++) {
-            /* flags_size is zero-based */
+        for (i = off_idx + 1; i < prdtl && sglist->size < limit; i++) {
             qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr),
-                            prdt_tbl_entry_size(&tbl[i]));
+                            MIN(prdt_tbl_entry_size(&tbl[i]),
+                                limit - sglist->size));
             if (sglist->size > INT32_MAX) {
                 error_report("AHCI Physical Region Descriptor Table describes "
                              "more than 2 GiB.\n");
@@ -1024,8 +1025,8 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
 
     ncq_tfs->sector_count = ((uint16_t)ncq_fis->sector_count_high << 8) |
                                 ncq_fis->sector_count_low;
-    ahci_populate_sglist(ad, &ncq_tfs->sglist, 0);
     size = ncq_tfs->sector_count * 512;
+    ahci_populate_sglist(ad, &ncq_tfs->sglist, size, 0);
 
     if (ncq_tfs->sglist.size < size) {
         error_report("ahci: PRDT length for NCQ command (0x%zx) "
@@ -1262,7 +1263,7 @@ static void ahci_start_transfer(IDEDMA *dma)
         goto out;
     }
 
-    if (ahci_dma_prepare_buf(dma, is_write)) {
+    if (ahci_dma_prepare_buf(dma, size, is_write)) {
         has_sglist = 1;
     }
 
@@ -1312,12 +1313,12 @@ static void ahci_restart_dma(IDEDMA *dma)
  * Not currently invoked by PIO R/W chains,
  * which invoke ahci_populate_sglist via ahci_start_transfer.
  */
-static int32_t ahci_dma_prepare_buf(IDEDMA *dma, int is_write)
+static int32_t ahci_dma_prepare_buf(IDEDMA *dma, int64_t limit, int is_write)
 {
     AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
     IDEState *s = &ad->port.ifs[0];
 
-    if (ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset) == -1) {
+    if (ahci_populate_sglist(ad, &s->sg, limit, s->io_buffer_offset) == -1) {
         DPRINTF(ad->port_no, "ahci_dma_prepare_buf failed.\n");
         return -1;
     }
@@ -1352,7 +1353,7 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
     uint8_t *p = s->io_buffer + s->io_buffer_index;
     int l = s->io_buffer_size - s->io_buffer_index;
 
-    if (ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset)) {
+    if (ahci_populate_sglist(ad, &s->sg, l, s->io_buffer_offset)) {
         return 0;
     }
 
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 1efd98a..6eefb30 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -734,7 +734,8 @@ static void ide_dma_cb(void *opaque, int ret)
     n = s->nsector;
     s->io_buffer_index = 0;
     s->io_buffer_size = n * 512;
-    if (s->bus->dma->ops->prepare_buf(s->bus->dma, ide_cmd_is_read(s)) < 512) {
+    if (s->bus->dma->ops->prepare_buf(s->bus->dma, s->io_buffer_size,
+                                      ide_cmd_is_read(s)) < 512) {
         /* The PRDs were too short. Reset the Active bit, but don't raise an
          * interrupt. */
         s->status = READY_STAT | SEEK_STAT;
@@ -2326,7 +2327,7 @@ static void ide_nop(IDEDMA *dma)
 {
 }
 
-static int32_t ide_nop_int32(IDEDMA *dma, int x)
+static int32_t ide_nop_int32(IDEDMA *dma, int64_t l, int x)
 {
     return 0;
 }
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index 965cc55..7a4a86d 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -324,7 +324,7 @@ typedef void EndTransferFunc(IDEState *);
 typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockCompletionFunc *);
 typedef void DMAVoidFunc(IDEDMA *);
 typedef int DMAIntFunc(IDEDMA *, int);
-typedef int32_t DMAInt32Func(IDEDMA *, int);
+typedef int32_t DMAInt32Func(IDEDMA *, int64_t len, int is_write);
 typedef void DMAu32Func(IDEDMA *, uint32_t);
 typedef void DMAStopFunc(IDEDMA *, bool);
 typedef void DMARestartFunc(void *, int, RunState);
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
index dd52d50..1bd1580 100644
--- a/hw/ide/macio.c
+++ b/hw/ide/macio.c
@@ -499,7 +499,7 @@ static int ide_nop_int(IDEDMA *dma, int x)
     return 0;
 }
 
-static int32_t ide_nop_int32(IDEDMA *dma, int x)
+static int32_t ide_nop_int32(IDEDMA *dma, int64_t l, int x)
 {
     return 0;
 }
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index 4afd0cf..a295baa 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -55,8 +55,11 @@ static void bmdma_start_dma(IDEDMA *dma, IDEState *s,
 /**
  * Return the number of bytes successfully prepared.
  * -1 on error.
+ * BUG?: Does not currently heed the 'limit' parameter because
+ *       it is not clear what the correct behavior here is,
+ *       see tests/ide-test.c
  */
-static int32_t bmdma_prepare_buf(IDEDMA *dma, int is_write)
+static int32_t bmdma_prepare_buf(IDEDMA *dma, int64_t limit, int is_write)
 {
     BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
     IDEState *s = bmdma_active_if(bm);
-- 
2.1.0

  reply	other threads:[~2015-06-23  0:21 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-06-23  0:20 [Qemu-devel] [PATCH 00/16] ahci: ncq cleanup, part 2 John Snow
2015-06-23  0:21 ` John Snow [this message]
2015-06-26 14:32   ` [Qemu-devel] [PATCH 01/16] ide: add limit to .prepare_buf() Stefan Hajnoczi
2015-06-26 18:16     ` John Snow
2015-06-29 13:34       ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2015-06-29 18:52         ` John Snow
2015-06-23  0:21 ` [Qemu-devel] [PATCH 02/16] ahci: stash ncq command John Snow
2015-06-23  0:21 ` [Qemu-devel] [PATCH 03/16] ahci: assert is_ncq for process_ncq John Snow
2015-06-23  0:21 ` [Qemu-devel] [PATCH 04/16] ahci: refactor process_ncq_command John Snow
2015-06-23  0:21 ` [Qemu-devel] [PATCH 05/16] ahci: factor ncq_finish out of ncq_cb John Snow
2015-06-23  0:21 ` [Qemu-devel] [PATCH 06/16] ahci: record ncq failures John Snow
2015-06-26 15:35   ` Stefan Hajnoczi
2015-06-26 18:27     ` John Snow
2015-06-29 14:10       ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2015-06-29 14:24       ` Stefan Hajnoczi
2015-06-29 15:42         ` John Snow
2015-06-29 15:47           ` John Snow
2015-06-30 13:56             ` Stefan Hajnoczi
2015-06-23  0:21 ` [Qemu-devel] [PATCH 07/16] ahci: kick NCQ queue John Snow
2015-06-23  0:21 ` [Qemu-devel] [PATCH 08/16] ahci: correct types in NCQTransferState John Snow
2015-06-23  0:21 ` [Qemu-devel] [PATCH 09/16] ahci: correct ncq sector count John Snow
2015-06-23  0:21 ` [Qemu-devel] [PATCH 10/16] qtest/ahci: halted NCQ test John Snow
2015-06-23  0:21 ` [Qemu-devel] [PATCH 11/16] ahci: add cmd header to ncq transfer state John Snow
2015-06-23  0:21 ` [Qemu-devel] [PATCH 12/16] ahci: ncq migration John Snow
2015-06-26 15:48   ` Stefan Hajnoczi
2015-06-26 16:46     ` John Snow
2015-06-29 14:25       ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2015-06-23  0:21 ` [Qemu-devel] [PATCH 13/16] ahci: add get_cmd_header helper John Snow
2015-06-26 15:51   ` Stefan Hajnoczi
2015-06-26 18:32     ` John Snow
2015-06-23  0:21 ` [Qemu-devel] [PATCH 14/16] ahci: Do not map cmd_fis to generate response John Snow
2015-06-26 15:59   ` Stefan Hajnoczi
2015-06-26 17:31     ` John Snow
2015-06-29 14:51       ` Stefan Hajnoczi
2015-06-29 15:07         ` John Snow
2015-06-30 14:50           ` Stefan Hajnoczi
2015-06-23  0:21 ` [Qemu-devel] [PATCH 15/16] qtest/ahci: halted ncq migration test John Snow
2015-06-23  0:21 ` [Qemu-devel] [PATCH 16/16] ahci: fix sdb fis semantics John Snow
2015-06-26 16:11   ` Stefan Hajnoczi
2015-06-26 17:36     ` John Snow
2015-06-29 14:52       ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2015-06-26 16:11 ` [Qemu-devel] [PATCH 00/16] ahci: ncq cleanup, part 2 Stefan Hajnoczi
2015-06-26 19:27   ` John Snow

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=1435018875-22527-2-git-send-email-jsnow@redhat.com \
    --to=jsnow@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@redhat.com \
    /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).