From patchwork Wed Oct 1 22:55:47 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 395703 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 1FC9814016B for ; Thu, 2 Oct 2014 08:56:59 +1000 (EST) Received: from localhost ([::1]:59145 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XZSpM-00086A-Rk for incoming@patchwork.ozlabs.org; Wed, 01 Oct 2014 18:56:56 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:51623) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XZSof-0006lR-0l for qemu-devel@nongnu.org; Wed, 01 Oct 2014 18:56:19 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1XZSoT-00019y-Rk for qemu-devel@nongnu.org; Wed, 01 Oct 2014 18:56:12 -0400 Received: from mx1.redhat.com ([209.132.183.28]:50456) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XZSoT-00019t-KS for qemu-devel@nongnu.org; Wed, 01 Oct 2014 18:56:01 -0400 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s91Mu0mg019587 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL) for ; Wed, 1 Oct 2014 18:56:01 -0400 Received: from dhcp-17-12.bos.redhat.com ([10.18.17.12]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s91Mtww2004189; Wed, 1 Oct 2014 18:56:00 -0400 From: John Snow To: qemu-devel@nongnu.org Date: Wed, 1 Oct 2014 18:55:47 -0400 Message-Id: <1412204151-18117-3-git-send-email-jsnow@redhat.com> In-Reply-To: <1412204151-18117-1-git-send-email-jsnow@redhat.com> References: <1412204151-18117-1-git-send-email-jsnow@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.27 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: kwolf@redhat.com, mst@redhat.com, armbru@redhat.com, stefanha@redhat.com, pbonzini@redhat.com, John Snow Subject: [Qemu-devel] [PATCH 2/6] ahci: Update byte count after DMA completion X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Currently, DMA read/write operations neglect to update the byte count after a successful transfer like ATAPI DMA read or PIO read/write operations do. We correct this oversight by adding another callback into the IDEDMAOps structure. The commit callback is called whenever we are cleaning up a scatter-gather list. AHCI can register this callback in order to update post- transfer information such as byte count updates. We use this callback in AHCI to consolidate where we delete the SGlist as generated from the PRDT, as well as update the byte count after the transfer is complete. The QEMUSGList structure has an init flag added to it in order to make qemu_sglist_destroy a nop if it is called when there is no sglist, which simplifies cleanup and error paths. This patch fixes several AHCI problems, notably Non-NCQ modes of operation for Windows 7 as well as Hibernate support for Windows 7. Signed-off-by: John Snow --- hw/ide/ahci.c | 41 +++++++++++++++++++++++++++++++---------- hw/ide/core.c | 11 +++++++---- hw/ide/internal.h | 2 ++ 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 0529d68..6966a94 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -48,6 +48,9 @@ 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 void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes); + static uint32_t ahci_port_read(AHCIState *s, int port, int offset) { @@ -1104,16 +1107,12 @@ static void ahci_start_transfer(IDEDMA *dma) } } - /* update number of transferred bytes */ - ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + size); - out: /* declare that we processed everything */ s->data_ptr = s->data_end; - if (has_sglist) { - qemu_sglist_destroy(&s->sg); - } + /* Update number of transferred bytes, destroy sglist */ + ahci_commit_buf(dma, size); s->end_transfer_func(s); @@ -1134,6 +1133,11 @@ static void ahci_start_dma(IDEDMA *dma, IDEState *s, dma_cb(s, 0); } +/** + * Called in DMA R/W chains to read the PRDT, utilizing ahci_populate_sglist. + * Not currently invoked by PIO R/W chains, + * which invoke ahci_populate_sglist via ahci_start_transfer. + */ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); @@ -1146,6 +1150,24 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) return s->io_buffer_size != 0; } +/** + * Destroys the scatter-gather list, + * and updates the command header with a bytes-read value. + * called explicitly via ahci_dma_rw_buf (ATAPI DMA), + * and ahci_start_transfer (PIO R/W), + * and called via callback from ide_dma_cb for DMA R/W paths. + */ +static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes) +{ + AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); + IDEState *s = &ad->port.ifs[0]; + + tx_bytes += le32_to_cpu(ad->cur_cmd->status); + ad->cur_cmd->status = cpu_to_le32(tx_bytes); + + qemu_sglist_destroy(&s->sg); +} + static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); @@ -1163,11 +1185,9 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) dma_buf_write(p, l, &s->sg); } - /* free sglist that was created in ahci_populate_sglist() */ - qemu_sglist_destroy(&s->sg); + /* free sglist, update byte count */ + ahci_commit_buf(dma, l); - /* update number of transferred bytes */ - ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + l); s->io_buffer_index += l; s->io_buffer_offset += l; @@ -1210,6 +1230,7 @@ static const IDEDMAOps ahci_dma_ops = { .start_dma = ahci_start_dma, .start_transfer = ahci_start_transfer, .prepare_buf = ahci_dma_prepare_buf, + .commit_buf = ahci_commit_buf, .rw_buf = ahci_dma_rw_buf, .set_unit = ahci_dma_set_unit, .cmd_done = ahci_cmd_done, diff --git a/hw/ide/core.c b/hw/ide/core.c index 190700a..f15b174 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -631,8 +631,11 @@ void ide_sector_read(IDEState *s) ide_sector_read_cb, s); } -static void dma_buf_commit(IDEState *s) +static void dma_buf_commit(IDEState *s, uint32_t tx_bytes) { + if (s->bus->dma->ops->commit_buf) { + s->bus->dma->ops->commit_buf(s->bus->dma, tx_bytes); + } qemu_sglist_destroy(&s->sg); } @@ -647,6 +650,7 @@ void ide_set_inactive(IDEState *s, bool more) void ide_dma_error(IDEState *s) { + dma_buf_commit(s, 0); ide_abort_command(s); ide_set_inactive(s, false); ide_set_irq(s->bus); @@ -662,7 +666,6 @@ static int ide_handle_rw_error(IDEState *s, int error, int op) s->bus->error_status = op; } else if (action == BLOCK_ERROR_ACTION_REPORT) { if (op & IDE_RETRY_DMA) { - dma_buf_commit(s); ide_dma_error(s); } else { ide_rw_error(s); @@ -706,7 +709,8 @@ void ide_dma_cb(void *opaque, int ret) sector_num = ide_get_sector(s); if (n > 0) { - dma_buf_commit(s); + assert(s->io_buffer_size == s->sg.size); + dma_buf_commit(s, s->io_buffer_size); sector_num += n; ide_set_sector(s, sector_num); s->nsector -= n; @@ -737,7 +741,6 @@ void ide_dma_cb(void *opaque, int ret) if ((s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) && !ide_sect_range_ok(s, sector_num, n)) { - dma_buf_commit(s); ide_dma_error(s); return; } diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 5c19f79..033329d 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -322,6 +322,7 @@ typedef void EndTransferFunc(IDEState *); typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockDriverCompletionFunc *); typedef void DMAVoidFunc(IDEDMA *); typedef int DMAIntFunc(IDEDMA *, int); +typedef void DMAu32Func(IDEDMA *, uint32_t); typedef void DMAStopFunc(IDEDMA *, bool); typedef void DMARestartFunc(void *, int, RunState); @@ -430,6 +431,7 @@ struct IDEDMAOps { DMAStartFunc *start_dma; DMAVoidFunc *start_transfer; DMAIntFunc *prepare_buf; + DMAu32Func *commit_buf; DMAIntFunc *rw_buf; DMAIntFunc *set_unit; DMAStopFunc *set_inactive;