All of lore.kernel.org
 help / color / mirror / Atom feed
From: Paolo Bonzini <pbonzini@redhat.com>
To: John Snow <jsnow@redhat.com>, qemu-devel@nongnu.org
Cc: stefanha@redhat.com, mst@redhat.com
Subject: Re: [Qemu-devel] [RFC 02/10] AHCI: Update byte count after DMA completion
Date: Sat, 13 Sep 2014 15:21:22 +0200	[thread overview]
Message-ID: <541444D2.6090709@redhat.com> (raw)
In-Reply-To: <1410582855-21870-3-git-send-email-jsnow@redhat.com>

Il 13/09/2014 06:34, John Snow ha scritto:
> 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.

I think this patch is touching the internals more than it should.
There's obviously some good stuff here, but the abstractions needed to
fix the problem are already there.

> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
>  dma-helpers.c        |  5 +++++
>  hw/ide/ahci.c        | 47 +++++++++++++++++++++++++++++++++++++----------
>  hw/ide/core.c        | 14 +++++++++-----
>  hw/ide/internal.h    |  1 +
>  include/sysemu/dma.h |  1 +
>  5 files changed, 53 insertions(+), 15 deletions(-)
> 
> diff --git a/dma-helpers.c b/dma-helpers.c
> index 499b52b..ba965a3 100644
> --- a/dma-helpers.c
> +++ b/dma-helpers.c
> @@ -45,6 +45,7 @@ void qemu_sglist_init(QEMUSGList *qsg, DeviceState *dev, int alloc_hint,
>      qsg->as = as;
>      qsg->dev = dev;
>      object_ref(OBJECT(dev));
> +    qsg->init = true;
>  }
>  
>  void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len)
> @@ -61,6 +62,10 @@ void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len)
>  
>  void qemu_sglist_destroy(QEMUSGList *qsg)
>  {
> +    if (!qsg->init) {
> +        return;
> +    }
> +
>      object_unref(OBJECT(qsg->dev));
>      g_free(qsg->sg);
>      memset(qsg, 0, sizeof(*qsg));

Why do you need this?  qemu_sglist_destroy is idempotent (neither free
nor unref of NULL cause problems).

> diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
> index 0ee713b..d3ece78 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 int ahci_commit_buf(IDEDMA *dma, int xfer_ok);
> +
>  
>  static uint32_t  ahci_port_read(AHCIState *s, int port, int offset)
>  {
> @@ -1084,16 +1087,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, true);
>  
>      s->end_transfer_func(s);
>  
> @@ -1114,6 +1113,11 @@ static void ahci_start_dma(IDEDMA *dma, IDEState *s,
>      dma_cb(s, 0);
>  }
>  
> +/**
> + * Called in DMA R/W chains to read the PRDTL, 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);
> @@ -1126,6 +1130,30 @@ 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 int ahci_commit_buf(IDEDMA *dma, int xfer_ok)
> +{
> +    AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
> +    IDEState *s = &ad->port.ifs[0];
> +    uint32_t byte_count;
> +
> +    if (xfer_ok) {
> +        byte_count = le32_to_cpu(ad->cur_cmd->status);
> +        byte_count += s->sg.size;
> +        ad->cur_cmd->status = cpu_to_le32(byte_count);
> +    }

If you leave qemu_sglist_destroy in the caller, you do not need to call
ahci_commit_buf in the error cases.  Maybe I'm wrong, but I have a
feeling that it would be much simpler to me if your commit hook is simply

       byte_count += le32_to_cpu(ad->cur_cmd->status);
       ad->cur_cmd->status = cpu_to_le32(byte_count);

where byte_count is the second argument to the hook.  The callers can
pass 0 in the error case, s->sg.size otherwise.

> +    qemu_sglist_destroy(&s->sg);
> +
> +    return s->sg.size != 0;

Also, there's no reason to keep the return value (which is always false).

> +}
> +
>  static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
>  {
>      AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
> @@ -1143,11 +1171,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, true);

Perhaps you should make dma_buf_commit public (and add the call to the
hook), so that ahci_commit_buf does not have to mess with &s->sg.

> -    /* 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;
>  
> @@ -1190,6 +1216,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 3d682e2..b2980e9 100644
> --- a/hw/ide/core.c
> +++ b/hw/ide/core.c
> @@ -629,9 +629,13 @@ 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, int xfer_ok)
>  {
> -    qemu_sglist_destroy(&s->sg);
> +    if (s->bus->dma->ops->commit_buf) {
> +        s->bus->dma->ops->commit_buf(s->bus->dma, xfer_ok);
> +    } else {

The "else" is not necessary, I think.

> +        qemu_sglist_destroy(&s->sg);
> +    }
>  }
>  
>  void ide_set_inactive(IDEState *s, bool more)
> @@ -660,7 +664,7 @@ 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);
> +            dma_buf_commit(s, false);

For the two error cases, it makes sense to move the call to
dma_buf_commit from the callers to ide_dma_error.

>              ide_dma_error(s);
>          } else {
>              ide_rw_error(s);
> @@ -701,7 +705,7 @@ void ide_dma_cb(void *opaque, int ret)
>  
>      sector_num = ide_get_sector(s);
>      if (n > 0) {
> -        dma_buf_commit(s);
> +        dma_buf_commit(s, true);
>          sector_num += n;
>          ide_set_sector(s, sector_num);
>          s->nsector -= n;
> @@ -732,7 +736,7 @@ 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);
> +        dma_buf_commit(s, false);
>          ide_dma_error(s);
>          return;
>      }
> diff --git a/hw/ide/internal.h b/hw/ide/internal.h
> index 72d0147..f190e8c 100644
> --- a/hw/ide/internal.h
> +++ b/hw/ide/internal.h
> @@ -432,6 +432,7 @@ struct IDEDMAOps {
>      DMAStartFunc *start_dma;
>      DMAVoidFunc *start_transfer;
>      DMAIntFunc *prepare_buf;
> +    DMAIntFunc *commit_buf;
>      DMAIntFunc *rw_buf;
>      DMAIntFunc *set_unit;
>      DMAStopFunc *set_inactive;
> diff --git a/include/sysemu/dma.h b/include/sysemu/dma.h
> index 00f21f3..8d2c4d6 100644
> --- a/include/sysemu/dma.h
> +++ b/include/sysemu/dma.h
> @@ -31,6 +31,7 @@ struct QEMUSGList {
>      size_t size;
>      DeviceState *dev;
>      AddressSpace *as;
> +    bool init;
>  };
>  
>  #ifndef CONFIG_USER_ONLY
> 

  reply	other threads:[~2014-09-13 13:21 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-09-13  4:34 [Qemu-devel] [RFC 00/10] AHCI Device improvements John Snow
2014-09-13  4:34 ` [Qemu-devel] [RFC 01/10] ide: add is_write() macro for semantic consistency John Snow
2014-09-13 12:54   ` Paolo Bonzini
2014-09-13 17:01     ` John Snow
2014-09-13  4:34 ` [Qemu-devel] [RFC 02/10] AHCI: Update byte count after DMA completion John Snow
2014-09-13 13:21   ` Paolo Bonzini [this message]
2014-09-15 20:07     ` John Snow
2014-09-16  7:54       ` Paolo Bonzini
2014-09-13  4:34 ` [Qemu-devel] [RFC 03/10] AHCI: Add PRD interrupt John Snow
2014-09-13 13:26   ` Paolo Bonzini
2014-09-13 19:50     ` Paolo Bonzini
2014-09-15 16:31       ` John Snow
2014-09-16  7:44         ` Paolo Bonzini
2014-09-15 16:13     ` John Snow
2014-09-13  4:34 ` [Qemu-devel] [RFC 04/10] ide: Correct handling of malformed/short PRDTs John Snow
2014-09-13 13:23   ` Paolo Bonzini
2014-09-13  4:34 ` [Qemu-devel] [RFC 05/10] AHCI: Rename NCQFIS structure fields John Snow
2014-09-13  4:34 ` [Qemu-devel] [RFC 06/10] AHCI: Fix FIS decomposition John Snow
2014-09-13  4:34 ` [Qemu-devel] [RFC 07/10] ide/ahci: Reorder error cases in handle_cmd John Snow
2014-09-13 13:27   ` Paolo Bonzini
2014-09-13  4:34 ` [Qemu-devel] [RFC 08/10] ahci: Check cmd_fis[1] more explicitly John Snow
2014-09-13 13:26   ` Paolo Bonzini
2014-09-13  4:34 ` [Qemu-devel] [RFC 09/10] ahci: factor out FIS decomposition John Snow
2014-09-13 13:27   ` Paolo Bonzini
2014-09-13  4:34 ` [Qemu-devel] [RFC 10/10] AHCI: Fix SDB FIS Construction 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=541444D2.6090709@redhat.com \
    --to=pbonzini@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=mst@redhat.com \
    --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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.