qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Anthony Liguori <anthony@codemonkey.ws>
To: Chunqiang Tang <ctang@us.ibm.com>
Cc: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH 2/3] FVD: Added the simulated 'blksim' driver
Date: Fri, 21 Jan 2011 16:49:00 -0600	[thread overview]
Message-ID: <4D3A0D5C.3080603@codemonkey.ws> (raw)
In-Reply-To: <1295648355-17359-2-git-send-email-ctang@us.ibm.com>

On 01/21/2011 04:19 PM, Chunqiang Tang wrote:
> This patch is part of the Fast Virtual Disk (FVD) proposal. See the related
> discussions at
> http://lists.gnu.org/archive/html/qemu-devel/2011-01/msg00426.html.
>
> This patch adds the 'blksim' block device driver, which is a tool to
> facilitate testing and debugging. blksim operates on a RAW image, but it uses
> neither AIO nor posix threads to perform I/Os. Instead, blksim functions like
> an event-driven disk simulator, and allows a block device driver developer to
> fully control the order of disk I/Os, the order of callbacks, and the return
> code of every I/O operation. The purpose is to comprehensively test a block
> device driver under failures and race conditions. Bugs found by blksim under
> rare race conditions are guaranteed to be precisely reproducible, with no
> dependency on thread timing etc., which makes debugging much easier.
>
> Signed-off-by: Chunqiang Tang<ctang@us.ibm.com>
> ---
>   Makefile.objs  |    1 +
>   block/blksim.c |  752 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   block/blksim.h |   35 +++
>   3 files changed, 788 insertions(+), 0 deletions(-)
>   create mode 100644 block/blksim.c
>   create mode 100644 block/blksim.h
>
> diff --git a/Makefile.objs b/Makefile.objs
> index c3e52c5..ce5cc8d 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -23,6 +23,7 @@ block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o
>   block-nested-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
>   block-nested-y += qed-check.o
>   block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o
> +block-nested-y += blksim.o
>   block-nested-$(CONFIG_WIN32) += raw-win32.o
>   block-nested-$(CONFIG_POSIX) += raw-posix.o
>   block-nested-$(CONFIG_CURL) += curl.o
> diff --git a/block/blksim.c b/block/blksim.c
> new file mode 100644
> index 0000000..a92ba11
> --- /dev/null
> +++ b/block/blksim.c
> @@ -0,0 +1,752 @@
> +/*
> + * Copyright (c) 2010-2011 IBM
> + *
> + * Authors:
> + *         Chunqiang Tang<ctang@us.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +/*=============================================================================
> + *  A short description: this module implements a simulated block device
> + *  driver "blksim". It works with qemu-io and qemu-test to perform testing,
> + *  allowing changing the  order of disk I/O and callback activities to test
> + *  rare race conditions. See qemu-test.c, qemu-io.c, and qemu-io-sim.c.
> + *============================================================================*/
> +
> +#include<sys/vfs.h>
> +#include<sys/mman.h>
> +#include<pthread.h>
> +#include<execinfo.h>
> +#include<stdlib.h>
> +#include<sys/ioctl.h>
> +#include<stdint.h>
> +#include<stdio.h>
> +#include<inttypes.h>
>    

QEMU code shouldn't include headers like this.  It almost guarantees 
breaking portability.

> +#include "block_int.h"
> +#include "osdep.h"
> +#include "qemu-option.h"
> +#include "qemu-timer.h"
> +#include "block.h"
> +#include "qemu-queue.h"
> +#include "qemu-common.h"
> +#include "block/blksim.h"
> +
> +#ifndef TRUE
> +# define TRUE 1
> +#endif
> +
> +#ifndef FALSE
> +# define FALSE 0
> +#endif
>    

C99 introduces stdbool.h.  That's the appropriate defines to use for 
booleans.

> +
> +#if 0
> +# define QDEBUG printf
> +#else
> +# define QDEBUG(format,...) do {} while (0)
> +#endif
> +
> +typedef enum {
> +    SIM_NULL,
> +    SIM_READ,
> +    SIM_WRITE,
> +    SIM_FLUSH,
> +    SIM_READ_CALLBACK,
> +    SIM_WRITE_CALLBACK,
> +    SIM_FLUSH_CALLBACK,
> +    SIM_TIMER
> +} sim_op_t;
>    

Breaks coding style (and the C standard).

> +static void sim_aio_cancel (BlockDriverAIOCB * acb);
> +static int64_t sim_uuid = 0;
> +static int64_t current_time = 0;
> +static int64_t rand_time = 0;
> +static int interactive_print = TRUE;
> +static int blksim_invoked = FALSE;
> +static int instant_qemubh = TRUE;
> +struct SimAIOCB;
> +
> +/*
> + * Note: disk_io_return_code, set_disk_io_return_code(), and insert_task() work
> + * together to ensure that multiple subrequests triggered by the same
> + * outtermost request either succeed together or fail together. This behavior
> + * is required by qemu-test.  Here is one example of problems caused by
> + * departuring from this behavior.  Consider a write request that generates
> + * two subrequests, w1 and w2. If w1 succeeds but w2 fails, the data will not
> + * be written into qemu-test's "truth image" but the part of the data handled
> + * by w1 will be written into qemu-test's "test image". As a result, their
> + * contents diverge can automated testing cannot continue.
> + */
> +static int disk_io_return_code = 0;
> +
> +typedef struct BDRVSimState {
> +    int fd;
> +} BDRVSimState;
> +
> +typedef struct SimAIOCB {
> +    BlockDriverAIOCB common;
> +    int64_t uuid;
> +    sim_op_t op;
> +    int64_t sector_num;
> +    QEMUIOVector *qiov;
> +    int nb_sectors;
> +    int ret;
> +    int64_t time;
> +    struct SimAIOCB *next;
> +    struct SimAIOCB *prev;
>    

Use qemu-queue instead of open coding data structures.

> +} SimAIOCB;
> +
> +static AIOPool sim_aio_pool = {
> +    .aiocb_size = sizeof (SimAIOCB),
> +    .cancel = sim_aio_cancel,
> +};
> +
> +static SimAIOCB head = {
> +    .uuid = -1,
> +    .time = (int64_t) (9223372036854775807ULL),
>    

This number has to mean something but I'll be damned if I know what it 
means.

> +    .op = SIM_NULL,
> +    .next =&head,
> +    .prev =&head,
> +};
> +
> +/* Debug a specific task.*/
> +#if 1
> +# define CHECK_TASK(acb) do { } while (0)
> +#else
> +static inline void CHECK_TASK (int64_t uuid)
> +{
> +    if (uuid == 19LL) {
> +        printf ("CHECK_TASK pause for task %" PRId64 "\n", uuid);
> +    }
> +}
> +#endif
>    

19LL?  Why is 19 significant?

> +/* do_io() should never fail. A failure indicates a bug in the upper layer
> + * block device driver, or failure in the real hardware. */
> +static int do_io (BlockDriverState * bs, int64_t sector_num, uint8_t * buf,
> +                  int nb_sectors, int do_read)
> +{
> +    BDRVSimState *s = bs->opaque;
> +    size_t size = nb_sectors * 512;
> +    int ret;
> +
> +    if (lseek (s->fd, sector_num * 512, SEEK_SET)<  0) {
> +        fprintf (stderr, "Error: lseek %s sector_num=%" PRId64 ". "
> +                 "Pause process %d for debugging...\n",
> +                 bs->filename, sector_num, getpid ());
> +        fgetc (stdin);
> +    }
> +
> +    while (size>  0) {
> +
> +        if (do_read) {
> +            ret = read (s->fd, buf, size);
> +            if (ret == 0) {
> +                fprintf (stderr,
> +                         "Error: read beyond the size of %s sector_num=%" PRId64
> +                         " nb_sectors=%d. Pause process %d for debugging...\n",
> +                         bs->filename, sector_num, nb_sectors, getpid ());
> +                fgetc (stdin);
> +            }
> +        } else {
> +            ret = write (s->fd, buf, size);
> +        }
> +
> +        if (ret>= 0) {
> +            size -= ret;
> +            buf += ret;
>    

You'll hit an infinite loop on EOF.

> +        } else if (errno != EINTR) {
> +            fprintf (stderr, "Error: %s %s sector_num=%" PRId64
> +                     " nb_sectors=%d. Pause process %d for debugging...\n",
> +                     do_read ? "READ" : "WRITE", bs->filename, sector_num,
> +                     nb_sectors, getpid ());
> +            fgetc (stdin);
> +            return -errno;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int blksim_read (BlockDriverState * bs, int64_t sector_num,
> +                        uint8_t * buf, int nb_sectors)
> +{
> +    return do_io (bs, sector_num, buf, nb_sectors, TRUE);
> +}
> +
> +static int blksim_write (BlockDriverState * bs, int64_t sector_num,
> +                      const uint8_t * buf, int nb_sectors)
> +{
> +    return do_io (bs, sector_num, (uint8_t *) buf, nb_sectors, FALSE);
> +}
> +
> +static void insert_in_list (SimAIOCB * acb)
> +{
> +    int64_t new_id = sim_uuid++;
> +    CHECK_TASK (new_id);
> +    acb->uuid = new_id;
> +
> +    if (rand_time<= 0) {
> +        /* Working with qemu-io.c and not doing delay randomization.
> +         * Insert it to the tail. */
> +        acb->time = 0;
> +        acb->prev = head.prev;
> +        acb->next =&head;
> +        head.prev->next = acb;
> +        head.prev = acb;
> +        return;
> +    }
> +
> +    SimAIOCB *p = head.next;
> +
> +    if (acb->time>= 0) {
> +        /* Introduce a random delay to better trigger rare race conditions. */
> +        acb->time += random () % rand_time;
> +
> +        /* Find the position to insert. The list is sorted in ascending time. */
> +        while (1) {
> +            if (p->time>  acb->time) {
> +                break;
> +            }
> +            if (p->time == acb->time&&  (random () % 2 == 0)) {
> +                break;
> +            }
> +            p = p->next;
> +        }
> +    }
> +
> +    /* Insert acb before p. */
> +    acb->next = p;
> +    acb->prev = p->prev;
> +    p->prev->next = acb;
> +    p->prev = acb;
> +}
> +
> +/* Debug problems related to reusing task objects. Problem already solved.*/
> +#if 1
> +# define my_qemu_aio_get qemu_aio_get
> +# define my_qemu_aio_release qemu_aio_release
> +
> +#else
> +static SimAIOCB *search_task_list (SimAIOCB * acb)
> +{
> +    SimAIOCB *p;
> +    for (p = head.next; p !=&head; p = p->next) {
> +        if (p == acb) {
> +            return p;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
> +static inline void *my_qemu_aio_get (AIOPool * pool, BlockDriverState * bs,
> +                                     BlockDriverCompletionFunc * cb,
> +                                     void *opaque)
> +{
> +    SimAIOCB *acb = (SimAIOCB *) qemu_aio_get (&sim_aio_pool, bs, cb, opaque);
> +    QDEBUG ("SIM: qemu_aio_get reuse old task%" PRId64 "\n", acb->uuid);
> +    ASSERT (!search_task_list (acb));
> +    return acb;
> +}
> +
> +static inline void my_qemu_aio_release (SimAIOCB * acb)
> +{
> +    QDEBUG ("SIM: qemu_aio_release task%" PRId64 "\n", acb->uuid);
> +    qemu_aio_release (acb);
> +}
> +#endif
> +
> +static BlockDriverAIOCB *insert_task (int op, BlockDriverState * bs,
> +                                      int64_t sector_num, QEMUIOVector * qiov,
> +                                      int nb_sectors,
> +                                      BlockDriverCompletionFunc * cb,
> +                                      void *opaque)
> +{
> +    SimAIOCB *acb = my_qemu_aio_get (&sim_aio_pool, bs, cb, opaque);
> +    if (!acb) {
> +        return NULL;
> +    }
> +
> +    acb->op = op;
> +    acb->sector_num = sector_num;
> +    acb->qiov = qiov;
> +    acb->nb_sectors = nb_sectors;
> +    acb->ret = disk_io_return_code;
> +    acb->time = current_time;
> +    insert_in_list (acb);
> +
> +    if (interactive_print) {
> +        if (op == SIM_READ) {
> +            printf ("Added READ uuid=%" PRId64 "  filename=%s  sector_num=%"
> +                    PRId64 "  nb_sectors=%d\n", acb->uuid,
> +                    acb->common.bs->filename, acb->sector_num, acb->nb_sectors);
> +        } else if (op == SIM_WRITE) {
> +            printf ("Added WRITE uuid=%" PRId64 "  filename=%s  sector_num=%"
> +                    PRId64 "  nb_sectors=%d\n", acb->uuid,
> +                    acb->common.bs->filename, acb->sector_num, acb->nb_sectors);
> +        } else {
> +            fprintf (stderr, "Unknown op %d\n", op);
> +            exit (1);
> +        }
> +    }
> +
> +    return&acb->common;
> +}
> +
> +static void insert_aio_callback (SimAIOCB * acb)
> +{
> +    acb->time = current_time;
> +    insert_in_list (acb);
> +
> +    if (acb->op == SIM_FLUSH) {
> +        acb->op = SIM_FLUSH_CALLBACK;
> +        if (interactive_print) {
> +            printf ("Added FLUSH_CALLBACK uuid=%" PRId64 "  filename=%s\n",
> +                    acb->uuid, acb->common.bs->filename);
> +        }
> +    } else if (acb->op == SIM_READ) {
> +        acb->op = SIM_READ_CALLBACK;
> +        if (interactive_print) {
> +            printf ("Added READ_CALLBACK uuid=%" PRId64
> +                    "  filename=%s  sector_num=%" PRId64 "  nb_sectors=%d\n",
> +                    acb->uuid, acb->common.bs->filename, acb->sector_num,
> +                    acb->nb_sectors);
> +        }
> +    } else if (acb->op == SIM_WRITE) {
> +        acb->op = SIM_WRITE_CALLBACK;
> +        if (interactive_print) {
> +            printf ("Added WRITE_CALLBACK uuid=%" PRId64
> +                    "  filename=%s  sector_num=%" PRId64 "  nb_sectors=%d\n",
> +                    acb->uuid, acb->common.bs->filename, acb->sector_num,
> +                    acb->nb_sectors);
> +        }
> +    } else {
> +        fprintf (stderr, "Wrong op %d\n", acb->op);
> +        exit (1);
> +    }
> +}
> +
> +void blksim_list_tasks (void)
> +{
> +    SimAIOCB *acb;
> +
> +    for (acb = head.next; acb !=&head; acb = acb->next) {
> +        if (acb->op == SIM_READ) {
> +            printf ("uuid=%" PRId64 "  READ           file=%s  sector_num=%"
> +                    PRIu64 "  nb_sectors=%d\n", acb->uuid,
> +                    acb->common.bs->filename, acb->sector_num, acb->nb_sectors);
> +        } else if (acb->op == SIM_WRITE) {
> +            printf ("uuid=%" PRId64 "  WRITE          file=%s  sector_num=%"
> +                    PRIu64 "  nb_sectors=%d\n", acb->uuid,
> +                    acb->common.bs->filename, acb->sector_num, acb->nb_sectors);
> +        } else if (acb->op == SIM_READ_CALLBACK) {
> +            printf ("uuid=%" PRId64 "  CALLBACK READ  file=%s  sector_num=%"
> +                    PRIu64 "  nb_sectors=%d\n", acb->uuid,
> +                    acb->common.bs->filename, acb->sector_num, acb->nb_sectors);
> +        } else if (acb->op == SIM_WRITE_CALLBACK) {
> +            printf ("uuid=%" PRId64 "  CALLBACK WRITE file=%s  sector_num=%"
> +                    PRIu64 "  nb_sectors=%d\n", acb->uuid,
> +                    acb->common.bs->filename, acb->sector_num, acb->nb_sectors);
> +        } else {
> +            fprintf (stderr, "Wrong OP %d\n", acb->op);
> +            exit (1);
> +        }
> +    }
> +}
> +
> +static inline void sim_callback (SimAIOCB * acb)
> +{
> +    acb->common.cb (acb->common.opaque, acb->ret);
> +}
> +
> +int64_t blksim_get_time (void)
> +{
> +    return current_time;
> +}
> +
> +void *blksim_new_timer (void *cb, void *opaque)
> +{
> +    SimAIOCB *acb = my_qemu_aio_get (&sim_aio_pool, NULL, cb, opaque);
> +    acb->op = SIM_TIMER;
> +    acb->prev = NULL;
> +    return acb;
> +}
> +
> +void blksim_mod_timer (void *ts, int64_t expire_time)
> +{
> +    SimAIOCB *acb = ts;
> +
> +    if (acb->prev) {
> +        /* Remove it first. */
> +        acb->next->prev = acb->prev;
> +        acb->prev->next = acb->next;
> +    }
> +    acb->time = expire_time;
> +    insert_in_list (acb);
> +
> +    if (interactive_print) {
> +        printf ("Added TIMER uuid=%" PRId64 "  expire_time=%"PRId64
> +                " current_time=%"PRId64"\n",
> +                acb->uuid, expire_time, current_time);
> +    }
> +}
> +
> +void blksim_free_timer (void *ts)
> +{
> +    SimAIOCB *acb = ts;
> +    CHECK_TASK (acb->uuid);
> +    my_qemu_aio_release (acb);
> +}
> +
> +void blksim_del_timer (void *ts)
> +{
> +    SimAIOCB *acb = ts;
> +
> +    CHECK_TASK (acb->uuid);
> +    if (acb->prev) {
> +        /* Remove it from the list. */
> +        acb->next->prev = acb->prev;
> +        acb->prev->next = acb->next;
> +
> +        /* Mark it as not in list. */
> +        acb->prev = NULL;
> +    }
> +}
> +
> +void blksim_bh_schedule (void *bh)
> +{
> +    if (instant_qemubh) {
> +        blksim_mod_timer (bh, -1);
> +    } else {
> +        blksim_mod_timer (bh, current_time);
> +    }
> +}
> +
> +void blksim_set_instant_qemubh (int instant)
> +{
> +    instant_qemubh = instant;
> +}
> +
> +void blksim_set_disk_io_return_code (int ret)
> +{
> +    disk_io_return_code = ret;
> +}
> +
> +static void run_task_by_acb (SimAIOCB * acb)
> +{
> +    CHECK_TASK (acb->uuid);
> +
> +    /* Remove it from the list. */
> +    acb->next->prev = acb->prev;
> +    acb->prev->next = acb->next;
> +    acb->prev = NULL;        /* Indicate that it is no longer in the list. */
> +
> +    if (acb->time>  current_time) {
> +        current_time = acb->time;
> +    }
> +
> +    if (acb->op == SIM_TIMER) {
> +        QDEBUG ("SIM: execute task%" PRId64 " time=%" PRId64 " TIMER \n",
> +                acb->uuid, acb->time);
> +
> +        ((QEMUTimerCB *) acb->common.cb) (acb->common.opaque);
> +        return;
> +    }
> +
> +    BlockDriverState *bs = acb->common.bs;
> +
> +    if (acb->op == SIM_READ) {
> +        QDEBUG ("SIM: execute task%" PRId64 " time=%" PRId64
> +                " READ %s sector_num=%" PRId64 " nb_sectors=%d\n",
> +                acb->uuid, acb->time, bs->filename, acb->sector_num,
> +                acb->nb_sectors);
> +
> +        if (acb->ret == 0) {
> +            if (acb->qiov->niov == 1) {
> +                if (blksim_read
> +                    (bs, acb->sector_num, acb->qiov->iov->iov_base,
> +                     acb->nb_sectors) != 0) {
> +                    fprintf (stderr, "Error in reading %s sector_num=%lld "
> +                             "nb_sectors=%d\n", acb->common.bs->filename,
> +                             acb->sector_num, acb->nb_sectors);
> +                    exit (1);
> +                }
> +            } else {
> +                uint8_t *buf=qemu_blockalign (acb->common.bs, acb->qiov->size);
> +                if (blksim_read (bs, acb->sector_num, buf,
> +                                 acb->nb_sectors) != 0) {
> +                    fprintf (stderr, "Error in reading %s sector_num=%lld "
> +                             "nb_sectors=%d\n", acb->common.bs->filename,
> +                             acb->sector_num, acb->nb_sectors);
> +                    exit (1);
> +                }
> +                qemu_iovec_from_buffer (acb->qiov, buf, acb->qiov->size);
> +                qemu_vfree (buf);
> +            }
> +        }
> +
> +        insert_aio_callback (acb);
> +    } else if (acb->op == SIM_WRITE) {
> +        QDEBUG ("SIM: execute task%" PRId64 " time=%" PRId64
> +                " WRITE %s sector_num=%" PRId64 " nb_sectors=%d\n",
> +                acb->uuid, acb->time, bs->filename,
> +                acb->sector_num, acb->nb_sectors);
> +
> +        if (acb->ret == 0) {
> +            if (acb->qiov->niov == 1) {
> +                if (blksim_write (bs, acb->sector_num, acb->qiov->iov->iov_base,
> +                                  acb->nb_sectors) != 0) {
> +                    fprintf (stderr, "Error in writing %s sector_num=%lld "
> +                             "nb_sectors=%d\n", acb->common.bs->filename,
> +                             acb->sector_num, acb->nb_sectors);
> +                    exit (1);
> +                }
> +            } else {
> +                uint8_t *buf = qemu_blockalign (acb->common.bs,
> +                                                acb->qiov->size);
> +                qemu_iovec_to_buffer (acb->qiov, buf);
> +                if (blksim_write (bs, acb->sector_num, buf,
> +                                  acb->nb_sectors)!= 0) {
> +                    fprintf (stderr, "Error in writing %s sector_num=%lld "
> +                             "nb_sectors=%d\n", acb->common.bs->filename,
> +                             acb->sector_num, acb->nb_sectors);
> +                    exit (1);
> +                }
> +                qemu_vfree (buf);
> +            }
> +        }
> +
> +        insert_aio_callback (acb);
> +    } else if (acb->op == SIM_FLUSH) {
> +        QDEBUG ("SIM: execute task%" PRId64 " time=%" PRId64 " FLUSH %s\n",
> +                acb->uuid, acb->time, bs->filename);
> +        /* Skip real flushing to speed up simulation:
> +         *         if (ret == 0) { * fdatasync (s->fd); } */
> +        insert_aio_callback (acb);
> +    } else if (acb->op == SIM_WRITE_CALLBACK || acb->op == SIM_READ_CALLBACK
> +               || acb->op == SIM_FLUSH_CALLBACK) {
> +        QDEBUG ("SIM: execute task%" PRId64 " time=%" PRId64 " CALLBACK\n",
> +                acb->uuid, acb->time);
> +        sim_callback (acb);
> +        CHECK_TASK (acb->uuid);
> +        my_qemu_aio_release (acb);
> +    } else {
> +        fprintf (stderr, "Unknown op %d\n", acb->op);
> +        exit (1);
> +    }
> +}
> +
> +int blksim_run_task_by_uuid (int64_t uuid)
> +{
> +    SimAIOCB *acb;
> +
> +    for (acb = head.next; acb !=&head; acb = acb->next) {
> +        if (acb->uuid == uuid) {
> +            run_task_by_acb (acb);
> +            return 0;
> +        }
> +    }
> +
> +    return -1;
> +}
> +
> +int blksim_run_all_tasks (void)
> +{
> +    int n = 0;
> +
> +    while (1) {
> +        SimAIOCB *acb = head.next;
> +        if (acb ==&head) {
> +            return n; /* No more tasks.*/
> +        }
> +
> +        run_task_by_acb (acb);
> +        n++;
> +    }
> +}
> +
> +static BlockDriverAIOCB *blksim_aio_readv (BlockDriverState * bs,
> +                                        int64_t sector_num,
> +                                        QEMUIOVector * qiov,
> +                                        int nb_sectors,
> +                                        BlockDriverCompletionFunc * cb,
> +                                        void *opaque)
> +{
> +    return insert_task (SIM_READ, bs, sector_num, qiov, nb_sectors, cb, opaque);
> +}
> +
> +static BlockDriverAIOCB *blksim_aio_writev (BlockDriverState * bs,
> +                                         int64_t sector_num,
> +                                         QEMUIOVector * qiov,
> +                                         int nb_sectors,
> +                                         BlockDriverCompletionFunc * cb,
> +                                         void *opaque)
> +{
> +    return insert_task (SIM_WRITE, bs, sector_num, qiov, nb_sectors, cb,
> +                        opaque);
> +}
> +
> +static BlockDriverAIOCB *blksim_aio_flush (BlockDriverState * bs,
> +                                        BlockDriverCompletionFunc * cb,
> +                                        void *opaque)
> +{
> +    return insert_task (SIM_FLUSH, bs, 0, NULL, 0, cb, opaque);
> +}
> +
> +static void sim_aio_cancel (BlockDriverAIOCB * blockacb)
> +{
> +    SimAIOCB *acb = container_of (blockacb, SimAIOCB, common);
> +
> +    CHECK_TASK (acb->uuid);
> +    QDEBUG ("SIM: cancel task%" PRId64 "\n", acb->uuid);
> +
> +    if (acb->prev) {
> +        acb->next->prev = acb->prev;
> +        acb->prev->next = acb->next;
> +        acb->prev = NULL;
> +        my_qemu_aio_release (acb);
> +    } else {
> +        fprintf (stderr, "Error: cancel a blksim task that does not exist: "
> +                 "uuid=%"PRId64". Halt process %d for debugging...\n",
> +                 acb->uuid, getpid());
> +        fgetc (stdin);
> +        exit (1);
> +    }
> +}
> +
> +static int blksim_open (BlockDriverState * bs, const char *filename,
> +                     int bdrv_flags)
> +{
> +    BDRVSimState *s = bs->opaque;
> +    int open_flags = O_BINARY | O_LARGEFILE;
> +
> +    blksim_invoked  = TRUE;
> +
> +    if ((bdrv_flags&  BDRV_O_RDWR)) {
> +        open_flags |= O_RDWR;
> +    } else {
> +        open_flags |= O_RDONLY;
> +    }
> +
> +    if ((bdrv_flags&  BDRV_O_NOCACHE)) {
> +        open_flags |= O_DIRECT;
> +    } else if (!(bdrv_flags&  BDRV_O_CACHE_WB)) {
> +        open_flags |= O_DSYNC;
> +    }
> +
> +    /* Parse the "blksim:" prefix */
> +    if (!strncmp(filename, "blksim:", strlen("blksim:"))) {
> +        filename += strlen("blksim:");
> +    }
> +
> +    s->fd = open (filename, open_flags);
> +    if (s->fd<  0)
> +        return -1;
> +
> +    int64_t len = lseek (s->fd, 0, SEEK_END);
> +    if (len>= 0) {
> +        bs->total_sectors = len / 512;
> +    } else {
> +        bs->total_sectors = 0;
> +    }
> +
> +    bs->growable = 1;
> +    return 0;
> +}
> +
> +static void blksim_close (BlockDriverState * bs)
> +{
> +    BDRVSimState *s = bs->opaque;
> +    close (s->fd);
> +}
> +
> +static int blksim_flush (BlockDriverState * bs)
> +{
> +    /*
> +     * Skip real flushing to speed up simulation.
> +         * BDRVSimState *s = bs->opaque;
> +         * fdatasync (s->fd);
> +     */
> +    return 0;
> +}
> +
> +static int blksim_has_zero_init (BlockDriverState * bs)
> +{
> +    struct stat buf;
> +
> +    if (stat (bs->filename,&buf) != 0) {
> +        fprintf (stderr, "Failed to stat() %s\n", bs->filename);
> +        exit (1);
> +    }
> +
> +    if (S_ISBLK (buf.st_mode) || S_ISCHR (buf.st_mode)) {
> +        return 0;
> +    }
> +
> +    return 1;
> +}
> +
> +static int blksim_truncate (BlockDriverState * bs, int64_t offset)
> +{
> +    BDRVSimState *s = bs->opaque;
> +    return ftruncate (s->fd, offset);
> +}
> +
> +static BlockDriver bdrv_blksim = {
> +    .format_name = "blksim",
> +    .protocol_name = "blksim",
> +    .instance_size = sizeof (BDRVSimState),
> +    .bdrv_file_open = blksim_open,
> +    .bdrv_close = blksim_close,
> +    .bdrv_flush = blksim_flush,
> +    .bdrv_read = blksim_read,
> +    .bdrv_write = blksim_write,
> +    .bdrv_aio_readv = blksim_aio_readv,
> +    .bdrv_aio_writev = blksim_aio_writev,
> +    .bdrv_aio_flush = blksim_aio_flush,
> +    .bdrv_has_zero_init = blksim_has_zero_init,
> +    .bdrv_truncate = blksim_truncate,
> +};
> +
> +static void bdrv_blksim_init(void)
> +{
> +    bdrv_register(&bdrv_blksim);
> +}
> +block_init(bdrv_blksim_init);
> +
> +void init_blksim (int print, int64_t _rand_time)
> +{
> +    interactive_print = print;
> +    rand_time = _rand_time;
> +}
> +
> +/*
> + * To work properly in the simulation mode, block device drivers that
> + * explicitly invoke qemu_aio_wait() should invoke blksim_qemu_aio_wait() if
> + * the block device is openned using blksim. Most block device drivers do not
> + * invoke qemu_aio_wait() and hence should not be concerned about this.
> + */
> +int blksim_qemu_aio_wait (void)
> +{
> +    SimAIOCB *acb = head.next;
> +    if (acb ==&head) {
> +        return 0;
> +    }
> +    else {
> +        run_task_by_acb (acb);
> +        return 1;
> +    }
> +}
> +
> +int blksim_has_task (void)
> +{
> +    return head.next !=&head;
> +}
> +
> +int using_blksim (void)
> +{
> +    return blksim_invoked;
> +}
> diff --git a/block/blksim.h b/block/blksim.h
> new file mode 100644
> index 0000000..fa1e20d
> --- /dev/null
> +++ b/block/blksim.h
> @@ -0,0 +1,35 @@
> +/*
> + * Copyright (c) 2010-2011 IBM
> + *
> + * Authors:
> + *         Chunqiang Tang<ctang@us.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +/*=============================================================================
> + *  A short description: this is the header of the simulated block device
> + *  driver "blksim".
> + *============================================================================*/
> +
> +#ifndef __block_sim_h__
> +#define __block_sim_h__
>    

Coding style.

In general, I like the idea of the simulator but the coding style is off 
quite a bit.

Regards,

Anthony Liguori

> +void init_blksim (int print, int64_t _rand_time);
> +int using_blksim (void);
> +int blksim_has_task (void);
> +void blksim_list_tasks (void);
> +int blksim_run_task_by_uuid (int64_t uuid);
> +int blksim_run_all_tasks (void);
> +int64_t blksim_get_time (void);
> +void *blksim_new_timer (void *cb, void *opaque);
> +void blksim_mod_timer (void *ts, int64_t expire_time);
> +void blksim_free_timer (void *ts);
> +void blksim_del_timer (void *ts);
> +void blksim_bh_schedule (void *bh);
> +void blksim_set_disk_io_return_code (int ret);
> +int blksim_qemu_aio_wait(void);
> +void blksim_set_instant_qemubh (int instant /* TRUE or FALSE */);
> +
> +#endif
>    

  reply	other threads:[~2011-01-21 22:49 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-01-21 22:19 [Qemu-devel] [PATCH 1/3] FVD: Added support for 'qemu-img update' Chunqiang Tang
2011-01-21 22:19 ` [Qemu-devel] [PATCH 2/3] FVD: Added the simulated 'blksim' driver Chunqiang Tang
2011-01-21 22:49   ` Anthony Liguori [this message]
2011-01-22  3:09     ` Chunqiang Tang
2011-01-23 23:26       ` Anthony Liguori
2011-01-24 15:07         ` Chunqiang Tang
2011-01-23 15:26   ` Andreas Färber
2011-01-25 16:54     ` Chunqiang Tang
2011-01-21 22:19 ` [Qemu-devel] [PATCH 3/3] FVD: Made qemu-io working with simulation (blksim) Chunqiang Tang
2011-01-28  9:57 ` [Qemu-devel] [PATCH 1/3] FVD: Added support for 'qemu-img update' Stefan Hajnoczi
2011-01-28 14:51   ` Chunqiang Tang
2011-01-28 16:16     ` Stefan Hajnoczi
2011-01-28 21:26       ` Chunqiang Tang
2011-01-29 10:05         ` Stefan Hajnoczi
2011-01-31 14:49           ` Chunqiang Tang
2011-02-01 13:53             ` Stefan Hajnoczi

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=4D3A0D5C.3080603@codemonkey.ws \
    --to=anthony@codemonkey.ws \
    --cc=ctang@us.ibm.com \
    --cc=qemu-devel@nongnu.org \
    /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).