Patchwork [1/2,v5-test] add function DMA_set_return and DMA_set_channel_async in dma.c

login
register
mail settings
Submitter Zhi Hui Li
Date April 19, 2012, 2:07 p.m.
Message ID <1334844460-17315-1-git-send-email-zhihuili@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/153778/
State New
Headers show

Comments

Zhi Hui Li - April 19, 2012, 2:07 p.m.
To avoid re-enter function fdctrl_transfer_handler, according paolo's suggestion, the patch is used to test.

Signed-off-by: Li Zhi Hui <zhihuili@linux.vnet.ibm.com>
---
 hw/dma.c |   48 ++++++++++++++++++++++++++++++++++++++++++++++--
 hw/isa.h |    2 ++
 2 files changed, 48 insertions(+), 2 deletions(-)
Paolo Bonzini - April 19, 2012, 2:21 p.m.
Il 19/04/2012 16:07, Li Zhi Hui ha scritto:
> To avoid re-enter function fdctrl_transfer_handler, according paolo's suggestion, the patch is used to test.
> 
> Signed-off-by: Li Zhi Hui <zhihuili@linux.vnet.ibm.com>
> ---
>  hw/dma.c |   48 ++++++++++++++++++++++++++++++++++++++++++++++--
>  hw/isa.h |    2 ++
>  2 files changed, 48 insertions(+), 2 deletions(-)
> 
> diff --git a/hw/dma.c b/hw/dma.c
> index 0a9322d..0a59164 100644
> --- a/hw/dma.c
> +++ b/hw/dma.c
> @@ -45,6 +45,8 @@ struct dma_regs {
>      uint8_t eop;
>      DMA_transfer_handler transfer_handler;
>      void *opaque;
> +    bool channel_running;
> +    bool channel_is_asynchronous;
>  };
>  
>  #define ADDR 0
> @@ -138,6 +140,8 @@ static inline void init_chan (struct dma_cont *d, int ichan)
>      r = d->regs + ichan;
>      r->now[ADDR] = r->base[ADDR] << d->dshift;
>      r->now[COUNT] = 0;
> +    r->channel_running = false;
> +    r->channel_is_asynchronous = false;
>  }
>  
>  static inline int getff (struct dma_cont *d)
> @@ -375,9 +379,20 @@ static void DMA_run (void)
>  
>              mask = 1 << ichan;
>  
> -            if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) {
> -                channel_run (icont, ichan);
> +            while ((0 == (d->mask & mask)) &&
> +                   (0 != (d->status & (mask << 4)))) {
> +                struct dma_regs *r = &dma_controllers[icont].regs[ichan];
> +                if (r->channel_running) {
> +                    assert(r->channel_is_asynchronous);
> +                    break;
> +                }
> +                r->channel_running = true;
> +                channel_run(icont, ichan);
>                  rearm = 1;
> +                if (r->channel_is_asynchronous) {
> +                    break;
> +                }
> +                r->channel_running = false;
>              }
>          }
>      }
> @@ -388,6 +403,35 @@ out:
>          qemu_bh_schedule_idle(dma_bh);
>  }
>  
> +void DMA_set_channel_async(int nchan, bool val)
> +{
> +    int icont, ichan;
> +    struct dma_regs *r;
> +
> +    icont = nchan > 3;
> +    ichan = nchan & 3;
> +    r = &dma_controllers[icont].regs[ichan];
> +    r->channel_is_asynchronous = val;
> +}
> +
> +void DMA_set_return(int nret, int nchan)
> +{
> +    struct dma_regs *r;
> +    struct dma_cont *d;
> +    int icont, ichan;
> +
> +    icont = nchan > 3;
> +    ichan = nchan & 3;
> +    d = dma_controllers;
> +    r = &d[icont].regs[ichan];
> +    r->now[COUNT] = nret;
> +    assert(r->channel_is_asynchronous);
> +    assert(r->channel_running);

Thanks, this is very much like what I had in mind, except that here I
would have called DMA_run.  Also you can then remove the bottom half
(and rearm logic) completely.

If calling DMA_run is not working, perhaps it is because you didn't
remove the bottom half.

I would hope that you can also change the "if (running) goto out;" to an
"assert(!running)", but I'm not so sure.

Paolo
Zhi Hui Li - April 20, 2012, 2:36 a.m.
>> +
>> +void DMA_set_return(int nret, int nchan)
>> +{
>> +    struct dma_regs *r;
>> +    struct dma_cont *d;
>> +    int icont, ichan;
>> +
>> +    icont = nchan>  3;
>> +    ichan = nchan&  3;
>> +    d = dma_controllers;
>> +    r =&d[icont].regs[ichan];
>> +    r->now[COUNT] = nret;
>> +    assert(r->channel_is_asynchronous);
>> +    assert(r->channel_running);
>
> Thanks, this is very much like what I had in mind, except that here I
> would have called DMA_run.  Also you can then remove the bottom half
> (and rearm logic) completely.
>
> If calling DMA_run is not working, perhaps it is because you didn't
> remove the bottom half.

You have written to me :

    assert(channel_is_asynchronous[ichan]);
    assert(channel_running[ichan] == 1);
    channel_running[ichan]--;
    if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) {
        channel_run (icont, ichan);
    }

If I add the code :
    if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) {
        channel_run (icont, ichan);
    }
Because function DMA_set_return is called in fdctrl_read_DMA_cb and 
fdctrl_write_DMA_cb, the fdctrl_stop_transfer will release the channel,
but fdctrl_stop_transfer is after the DMA_set_return, so the channel_run 
will run again, so here I delete the above code.

>
> I would hope that you can also change the "if (running) goto out;" to an
> "assert(!running)", but I'm not so sure.
>

sorry, here I don't understand well,or you can explain in detail.  :)


Thank you very much for your feedback!   :)
Paolo Bonzini - April 20, 2012, 1:07 p.m.
Il 20/04/2012 04:36, Zhi Hui Li ha scritto:
> If I add the code :
>    if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) {
>        channel_run (icont, ichan);
>    }
> Because function DMA_set_return is called in fdctrl_read_DMA_cb and
> fdctrl_write_DMA_cb, the fdctrl_stop_transfer will release the channel,
> but fdctrl_stop_transfer is after the DMA_set_return, so the channel_run
> will run again, so here I delete the above code.

I looked again at the code, and man it is painful! :)  (Both the
synchronous and asynchronous versions).

I think the best bet is to do the transfer one sector at a time even in
the asynchronous code, and reuse the fifo buffer for this).

Remember that your final objective is to execute exactly the same code
that is in QEMU right now, only scattered across multiple function calls.

Some other notes:

- you don't need the opaque_cb.  Just pass the fdctrl, the dma_len can
be stored in there and the channel is already available

- the scan support is completely broken in QEMU already.  It always
returns SEH (scan equal hit), probably because of misplaced gotos.

Paolo

Patch

diff --git a/hw/dma.c b/hw/dma.c
index 0a9322d..0a59164 100644
--- a/hw/dma.c
+++ b/hw/dma.c
@@ -45,6 +45,8 @@  struct dma_regs {
     uint8_t eop;
     DMA_transfer_handler transfer_handler;
     void *opaque;
+    bool channel_running;
+    bool channel_is_asynchronous;
 };
 
 #define ADDR 0
@@ -138,6 +140,8 @@  static inline void init_chan (struct dma_cont *d, int ichan)
     r = d->regs + ichan;
     r->now[ADDR] = r->base[ADDR] << d->dshift;
     r->now[COUNT] = 0;
+    r->channel_running = false;
+    r->channel_is_asynchronous = false;
 }
 
 static inline int getff (struct dma_cont *d)
@@ -375,9 +379,20 @@  static void DMA_run (void)
 
             mask = 1 << ichan;
 
-            if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) {
-                channel_run (icont, ichan);
+            while ((0 == (d->mask & mask)) &&
+                   (0 != (d->status & (mask << 4)))) {
+                struct dma_regs *r = &dma_controllers[icont].regs[ichan];
+                if (r->channel_running) {
+                    assert(r->channel_is_asynchronous);
+                    break;
+                }
+                r->channel_running = true;
+                channel_run(icont, ichan);
                 rearm = 1;
+                if (r->channel_is_asynchronous) {
+                    break;
+                }
+                r->channel_running = false;
             }
         }
     }
@@ -388,6 +403,35 @@  out:
         qemu_bh_schedule_idle(dma_bh);
 }
 
+void DMA_set_channel_async(int nchan, bool val)
+{
+    int icont, ichan;
+    struct dma_regs *r;
+
+    icont = nchan > 3;
+    ichan = nchan & 3;
+    r = &dma_controllers[icont].regs[ichan];
+    r->channel_is_asynchronous = val;
+}
+
+void DMA_set_return(int nret, int nchan)
+{
+    struct dma_regs *r;
+    struct dma_cont *d;
+    int icont, ichan;
+
+    icont = nchan > 3;
+    ichan = nchan & 3;
+    d = dma_controllers;
+    r = &d[icont].regs[ichan];
+    r->now[COUNT] = nret;
+    assert(r->channel_is_asynchronous);
+    assert(r->channel_running);
+    r->channel_running = false;
+    r->channel_is_asynchronous = false;
+}
+
+
 static void DMA_run_bh(void *unused)
 {
     DMA_run();
diff --git a/hw/isa.h b/hw/isa.h
index 40373fb..07293d0 100644
--- a/hw/isa.h
+++ b/hw/isa.h
@@ -92,4 +92,6 @@  void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit);
 void DMA_register_channel (int nchan,
                            DMA_transfer_handler transfer_handler,
                            void *opaque);
+void DMA_set_channel_async(int nchan, bool val);
+void DMA_set_return(int nret, int nchan);
 #endif