qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] [RFC] Add delay option to blkdebug
@ 2013-06-29 18:02 Alex Bligh
  2013-06-29 18:02 ` [Qemu-devel] [PATCH] " Alex Bligh
  0 siblings, 1 reply; 4+ messages in thread
From: Alex Bligh @ 2013-06-29 18:02 UTC (permalink / raw)
  To: qemu-devel

This is an RFC for a very lightly tested patch.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [Qemu-devel] [PATCH] Add delay option to blkdebug
  2013-06-29 18:02 [Qemu-devel] [PATCH] [RFC] Add delay option to blkdebug Alex Bligh
@ 2013-06-29 18:02 ` Alex Bligh
  2013-07-01 10:23   ` Kevin Wolf
  0 siblings, 1 reply; 4+ messages in thread
From: Alex Bligh @ 2013-06-29 18:02 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bligh

Add a delay option to blkdebug, allowing operations to be delayed by
a specifiable number of microseconds. Example configuration:

[inject-error]
event = "read_aio"
delay = "200000"

Signed-off-by: Alex Bligh <alex@alex.org.uk>
---
 block/blkdebug.c |   83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/block/blkdebug.c b/block/blkdebug.c
index ccb627a..dafb805 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -40,6 +40,9 @@ typedef struct BlkdebugAIOCB {
     BlockDriverAIOCB common;
     QEMUBH *bh;
     int ret;
+    bool *finished;
+    int64_t delay;
+    int64_t starttime;
 } BlkdebugAIOCB;
 
 typedef struct BlkdebugSuspendedReq {
@@ -71,6 +74,7 @@ typedef struct BlkdebugRule {
             int immediately;
             int once;
             int64_t sector;
+            int64_t delay;
         } inject;
         struct {
             int new_state;
@@ -111,6 +115,10 @@ static QemuOptsList inject_error_opts = {
             .name = "immediately",
             .type = QEMU_OPT_BOOL,
         },
+        {
+            .name = "delay", /* delay in microseconds */
+            .type = QEMU_OPT_NUMBER,
+        },
         { /* end of list */ }
     },
 };
@@ -236,6 +244,10 @@ static int add_rule(QemuOpts *opts, void *opaque)
         rule->options.inject.immediately =
             qemu_opt_get_bool(opts, "immediately", 0);
         rule->options.inject.sector = qemu_opt_get_number(opts, "sector", -1);
+        rule->options.inject.delay = qemu_opt_get_number(opts, "delay", 0);
+        if (rule->options.inject.delay) {
+            rule->options.inject.error = 0;
+        }
         break;
 
     case ACTION_SET_STATE:
@@ -399,6 +411,9 @@ fail:
 static void error_callback_bh(void *opaque)
 {
     struct BlkdebugAIOCB *acb = opaque;
+    if (acb->finished) {
+        *acb->finished = true;
+    }
     qemu_bh_delete(acb->bh);
     acb->common.cb(acb->common.opaque, acb->ret);
     qemu_aio_release(acb);
@@ -407,6 +422,13 @@ static void error_callback_bh(void *opaque)
 static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb)
 {
     BlkdebugAIOCB *acb = container_of(blockacb, BlkdebugAIOCB, common);
+    bool finished = false;
+
+    /* Wait until request completes, invokes its callback, and frees itself */
+    acb->finished = &finished;
+    while (!finished) {
+        qemu_aio_wait();
+    }
     qemu_aio_release(acb);
 }
 
@@ -427,6 +449,7 @@ static BlockDriverAIOCB *inject_error(BlockDriverState *bs,
     }
 
     acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque);
+    acb->finished = NULL;
     acb->ret = -error;
 
     bh = qemu_bh_new(error_callback_bh, acb);
@@ -436,6 +459,50 @@ static BlockDriverAIOCB *inject_error(BlockDriverState *bs,
     return &acb->common;
 }
 
+static BlkdebugAIOCB *blkdebug_aio_get(BlockDriverState *bs, int64_t delay,
+                                       BlockDriverCompletionFunc *cb,
+                                       void *opaque)
+{
+    BlkdebugAIOCB *acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque);
+    
+    acb->bh = NULL;
+    acb->delay = delay;
+    acb->finished = NULL;
+    return acb;
+}
+
+static void blkdebug_aio_delay_bh(void *opaque)
+{
+    int64_t currenttime;
+    BlkdebugAIOCB *acb = opaque;
+
+    /* Unfortunately we cannot use a timer as under qemu-img for instance
+     * the timer loop is not run.
+     */
+    currenttime = qemu_get_clock_ns(rt_clock);
+    if ((currenttime - acb->starttime) < (acb->delay * 1000)) {
+        qemu_bh_schedule_idle(acb->bh);
+        return;
+    }
+
+    qemu_bh_delete(acb->bh);
+
+    acb->common.cb(acb->common.opaque, acb->ret);
+    if (acb->finished) {
+        *acb->finished = true;
+    }
+    qemu_aio_release(acb);
+}
+
+static void blkdebug_aio_delay_cb(void *opaque, int ret)
+{
+    BlkdebugAIOCB *acb = opaque;
+
+    acb->starttime = qemu_get_clock_ns(rt_clock);
+    acb->bh = qemu_bh_new(blkdebug_aio_delay_bh, acb);
+    qemu_bh_schedule_idle(acb->bh);
+}
+
 static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
     BlockDriverCompletionFunc *cb, void *opaque)
@@ -455,6 +522,14 @@ static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
         return inject_error(bs, cb, opaque, rule);
     }
 
+    if (rule && rule->options.inject.delay) {
+        BlkdebugAIOCB *acb = blkdebug_aio_get(bs, rule->options.inject.delay, cb, opaque);
+        
+        bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors,
+                       blkdebug_aio_delay_cb, acb);
+        return &acb->common;
+    }
+
     return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
 }
 
@@ -477,6 +552,14 @@ static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
         return inject_error(bs, cb, opaque, rule);
     }
 
+    if (rule && rule->options.inject.delay) {
+        BlkdebugAIOCB *acb = blkdebug_aio_get(bs, rule->options.inject.delay, cb, opaque);
+        
+        bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
+                        blkdebug_aio_delay_cb, acb);
+        return &acb->common;
+    }
+
     return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
 }
 
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH] Add delay option to blkdebug
  2013-06-29 18:02 ` [Qemu-devel] [PATCH] " Alex Bligh
@ 2013-07-01 10:23   ` Kevin Wolf
  2013-07-01 11:15     ` Alex Bligh
  0 siblings, 1 reply; 4+ messages in thread
From: Kevin Wolf @ 2013-07-01 10:23 UTC (permalink / raw)
  To: Alex Bligh; +Cc: qemu-devel, stefanha

Am 29.06.2013 um 20:02 hat Alex Bligh geschrieben:
> Add a delay option to blkdebug, allowing operations to be delayed by
> a specifiable number of microseconds. Example configuration:
> 
> [inject-error]
> event = "read_aio"
> delay = "200000"
> 
> Signed-off-by: Alex Bligh <alex@alex.org.uk>

"inject-error" doesn't really describe this well. Shouldn't we rather
introduce a new section "[delay]" or something like that?

I remember that I once tried something similar, but never submitted it.
I think the reason was that using timers inside requests doesn't really
work. It works as long as everything is indeed asynchronous, but
bdrv_drain_all() or a loop waiting for a single request that tries to
make progress with qemu_aio_wait() will simply hang because timers
aren't processed in these nested event loops.

Kevin

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH] Add delay option to blkdebug
  2013-07-01 10:23   ` Kevin Wolf
@ 2013-07-01 11:15     ` Alex Bligh
  0 siblings, 0 replies; 4+ messages in thread
From: Alex Bligh @ 2013-07-01 11:15 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: Alex Bligh, qemu-devel, stefanha

Kevin,

--On 1 July 2013 12:23:53 +0200 Kevin Wolf <kwolf@redhat.com> wrote:

> "inject-error" doesn't really describe this well. Shouldn't we rather
> introduce a new section "[delay]" or something like that?

That's how I started off. Then I realised you might want to make
the delay dependent on the sector or state, or the operation
concerned. Changing the name of the section to '[inject]' would
probably be best.

> I remember that I once tried something similar, but never submitted it.
> I think the reason was that using timers inside requests doesn't really
> work. It works as long as everything is indeed asynchronous, but
> bdrv_drain_all() or a loop waiting for a single request that tries to
> make progress with qemu_aio_wait() will simply hang because timers
> aren't processed in these nested event loops.

Yes I discovered that the hard way. In particular, timers are not
run at all in (e.g.) qemu-img. I think they are not run at all outside
qemu itself.

This is why I don't use timers.

What it currently does is schedule a bh which looks at the time
(as time still moves forwards) - using idle scheduling, and if it's
not time to run the bh, the bh reschedules itself (using idle scheduling
again). Because of the use of idle scheduling it isn't busy-waiting,
but it is hardly ideal.

I /think/ this is safe because if bh's aren't running, lots of
drivers (e.g. blkverify) would not work. And as far as I can tell,
idle bh's are scheduled whenever non-idle ones are. However, I am
newbie as far as the async code and the block code are concerned.

-- 
Alex Bligh

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2013-07-01 11:15 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-06-29 18:02 [Qemu-devel] [PATCH] [RFC] Add delay option to blkdebug Alex Bligh
2013-06-29 18:02 ` [Qemu-devel] [PATCH] " Alex Bligh
2013-07-01 10:23   ` Kevin Wolf
2013-07-01 11:15     ` Alex Bligh

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).