slirp: Factor out internal state structure
[qemu] / qemu-io.c
index 405b6c5..566ca79 100644 (file)
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -54,20 +54,20 @@ static void qemu_io_free(void *p)
 }
 
 static void
-dump_buffer(char *buffer, int64_t offset, int len)
+dump_buffer(const void *buffer, int64_t offset, int len)
 {
        int i, j;
-       char *p;
+       const uint8_t *p;
 
        for (i = 0, p = buffer; i < len; i += 16) {
-               char    *s = p;
+               const uint8_t *s = p;
 
                printf("%08llx:  ", (unsigned long long)offset + i);
                for (j = 0; j < 16 && i + j < len; j++, p++)
                        printf("%02x ", *p);
                printf(" ");
                for (j = 0; j < 16 && i + j < len; j++, s++) {
-                       if (isalnum((int)*s))
+                       if (isalnum(*s))
                                printf("%c", *s);
                        else
                                printf(".");
@@ -191,11 +191,13 @@ read_help(void)
 "\n"
 " Reads a segment of the currently open file, optionally dumping it to the\n"
 " standard output stream (with -v option) for subsequent inspection.\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -l, -- length for pattern verification (only with -P)\n"
 " -p, -- use bdrv_pread to read the file\n"
 " -P, -- use a pattern to verify read data\n"
-" -C, -- report statistics in a machine parsable format\n"
-" -v, -- dump buffer to standard output\n"
 " -q, -- quite mode, do not show I/O statistics\n"
+" -s, -- start offset for pattern verification (only with -P)\n"
+" -v, -- dump buffer to standard output\n"
 "\n");
 }
 
@@ -204,18 +206,28 @@ read_f(int argc, char **argv)
 {
        struct timeval t1, t2;
        int Cflag = 0, pflag = 0, qflag = 0, vflag = 0;
+       int Pflag = 0, sflag = 0, lflag = 0;
        int c, cnt;
        char *buf;
        int64_t offset;
-       int count, total;
-       int pattern = 0;
-       int Pflag = 0;
+       int count;
+        /* Some compilers get confused and warn if this is not initialized.  */
+        int total = 0;
+       int pattern = 0, pattern_offset = 0, pattern_count = 0;
 
-       while ((c = getopt(argc, argv, "CpP:qv")) != EOF) {
+       while ((c = getopt(argc, argv, "Cl:pP:qs:v")) != EOF) {
                switch (c) {
                case 'C':
                        Cflag = 1;
                        break;
+               case 'l':
+                       lflag = 1;
+                       pattern_count = cvtnum(optarg);
+                       if (pattern_count < 0) {
+                               printf("non-numeric length argument -- %s\n", optarg);
+                               return 0;
+                       }
+                       break;
                case 'p':
                        pflag = 1;
                        break;
@@ -226,6 +238,14 @@ read_f(int argc, char **argv)
                case 'q':
                        qflag = 1;
                        break;
+               case 's':
+                       sflag = 1;
+                       pattern_offset = cvtnum(optarg);
+                       if (pattern_offset < 0) {
+                               printf("non-numeric length argument -- %s\n", optarg);
+                               return 0;
+                       }
+                       break;
                case 'v':
                        vflag = 1;
                        break;
@@ -250,6 +270,19 @@ read_f(int argc, char **argv)
                return 0;
        }
 
+    if (!Pflag && (lflag || sflag)) {
+        return command_usage(&read_cmd);
+    }
+
+    if (!lflag) {
+        pattern_count = count - pattern_offset;
+    }
+
+    if ((pattern_count < 0) || (pattern_count + pattern_offset > count))  {
+        printf("pattern verfication range exceeds end of read data\n");
+        return 0;
+    }
+
        if (!pflag)
                if (offset & 0x1ff) {
                        printf("offset %lld is not sector aligned\n",
@@ -278,12 +311,12 @@ read_f(int argc, char **argv)
        }
 
        if (Pflag) {
-               void* cmp_buf = malloc(count);
-               memset(cmp_buf, pattern, count);
-               if (memcmp(buf, cmp_buf, count)) {
+               void* cmp_buf = malloc(pattern_count);
+               memset(cmp_buf, pattern, pattern_count);
+               if (memcmp(buf + pattern_offset, cmp_buf, pattern_count)) {
                        printf("Pattern verification failed at offset %lld, "
                                "%d bytes\n",
-                               (long long) offset, count);
+                               (long long) offset + pattern_offset, pattern_count);
                }
                free(cmp_buf);
        }
@@ -309,7 +342,7 @@ static const cmdinfo_t read_cmd = {
        .cfunc          = read_f,
        .argmin         = 2,
        .argmax         = -1,
-       .args           = "[-aCpqv] [-P pattern ] off len",
+       .args           = "[-aCpqv] [-P pattern [-s off] [-l len]] off len",
        .oneline        = "reads a number of bytes at a specified offset",
        .help           = read_help,
 };
@@ -496,7 +529,9 @@ write_f(int argc, char **argv)
        int c, cnt;
        char *buf;
        int64_t offset;
-       int count, total;
+       int count;
+        /* Some compilers get confused and warn if this is not initialized.  */
+        int total = 0;
        int pattern = 0xcd;
 
        while ((c = getopt(argc, argv, "CpP:q")) != EOF) {
@@ -717,6 +752,351 @@ static const cmdinfo_t writev_cmd = {
        .help           = writev_help,
 };
 
+struct aio_ctx {
+       QEMUIOVector qiov;
+       int64_t offset;
+       char *buf;
+       int qflag;
+       int vflag;
+       int Cflag;
+       int Pflag;
+       int pattern;
+       struct timeval t1;
+};
+
+static void
+aio_write_done(void *opaque, int ret)
+{
+       struct aio_ctx *ctx = opaque;
+       struct timeval t2;
+       int total;
+       int cnt = 1;
+
+       gettimeofday(&t2, NULL);
+
+       total = ctx->qiov.size;
+
+       if (ret < 0) {
+               printf("aio_write failed: %s\n", strerror(-ret));
+               return;
+       }
+
+       if (ctx->qflag)
+               return;
+
+       /* Finally, report back -- -C gives a parsable format */
+       t2 = tsub(t2, ctx->t1);
+       print_report("wrote", &t2, ctx->offset, ctx->qiov.size, total, cnt,
+                    ctx->Cflag);
+
+       qemu_io_free(ctx->buf);
+       free(ctx);
+}
+
+static const cmdinfo_t aio_read_cmd;
+
+static void
+aio_read_done(void *opaque, int ret)
+{
+       struct aio_ctx *ctx = opaque;
+       struct timeval t2;
+       int total;
+       int cnt = 1;
+
+       gettimeofday(&t2, NULL);
+
+       total = ctx->qiov.size;
+
+       if (ret < 0) {
+               printf("readv failed: %s\n", strerror(-ret));
+               return;
+       }
+
+       if (ctx->Pflag) {
+               void *cmp_buf = malloc(total);
+
+               memset(cmp_buf, ctx->pattern, total);
+               if (memcmp(ctx->buf, cmp_buf, total)) {
+                       printf("Pattern verification failed at offset %lld, "
+                               "%d bytes\n",
+                               (long long) ctx->offset, total);
+               }
+               free(cmp_buf);
+       }
+
+       if (ctx->qflag)
+               return;
+
+        if (ctx->vflag)
+               dump_buffer(ctx->buf, ctx->offset, total);
+
+       /* Finally, report back -- -C gives a parsable format */
+       t2 = tsub(t2, ctx->t1);
+       print_report("read", &t2, ctx->offset, ctx->qiov.size, total, cnt,
+                    ctx->Cflag);
+
+       qemu_io_free(ctx->buf);
+       free(ctx);
+
+}
+
+static void
+aio_read_help(void)
+{
+       printf(
+"\n"
+" asynchronously reads a range of bytes from the given offset\n"
+"\n"
+" Example:\n"
+" 'aio_read -v 512 1k 1k ' - dumps 2 kilobytes read from 512 bytes into the file\n"
+"\n"
+" Reads a segment of the currently open file, optionally dumping it to the\n"
+" standard output stream (with -v option) for subsequent inspection.\n"
+" The read is performed asynchronously and should the aio_flush command \n"
+" should be used to ensure all outstanding aio requests have been completed\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -P, -- use a pattern to verify read data\n"
+" -v, -- dump buffer to standard output\n"
+" -q, -- quite mode, do not show I/O statistics\n"
+"\n");
+}
+
+static int
+aio_read_f(int argc, char **argv)
+{
+       char *p;
+       int count = 0;
+       int nr_iov, i, c;
+       struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx));
+       BlockDriverAIOCB *acb;
+
+       ctx->pattern = 0xcd;
+
+       while ((c = getopt(argc, argv, "CP:qv")) != EOF) {
+               switch (c) {
+               case 'C':
+                       ctx->Cflag = 1;
+                       break;
+               case 'P':
+                       ctx->Pflag = 1;
+                       ctx->pattern = atoi(optarg);
+                       break;
+               case 'q':
+                       ctx->qflag = 1;
+                       break;
+               case 'v':
+                       ctx->vflag = 1;
+                       break;
+               default:
+                       return command_usage(&aio_read_cmd);
+               }
+       }
+
+       if (optind > argc - 2)
+               return command_usage(&aio_read_cmd);
+
+
+       ctx->offset = cvtnum(argv[optind]);
+       if (ctx->offset < 0) {
+               printf("non-numeric length argument -- %s\n", argv[optind]);
+               return 0;
+       }
+       optind++;
+
+       if (ctx->offset & 0x1ff) {
+               printf("offset %lld is not sector aligned\n",
+                       (long long)ctx->offset);
+               return 0;
+       }
+
+       if (count & 0x1ff) {
+               printf("count %d is not sector aligned\n",
+                       count);
+               return 0;
+       }
+
+       for (i = optind; i < argc; i++) {
+               size_t len;
+
+               len = cvtnum(argv[i]);
+               if (len < 0) {
+                       printf("non-numeric length argument -- %s\n", argv[i]);
+                       return 0;
+               }
+               count += len;
+       }
+
+       nr_iov = argc - optind;
+       qemu_iovec_init(&ctx->qiov, nr_iov);
+       ctx->buf = p = qemu_io_alloc(count, 0xab);
+       for (i = 0; i < nr_iov; i++) {
+               size_t len;
+
+               len = cvtnum(argv[optind]);
+               if (len < 0) {
+                       printf("non-numeric length argument -- %s\n",
+                               argv[optind]);
+                       return 0;
+               }
+
+               qemu_iovec_add(&ctx->qiov, p, len);
+               p += len;
+               optind++;
+       }
+
+       gettimeofday(&ctx->t1, NULL);
+       acb = bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov,
+                             ctx->qiov.size >> 9, aio_read_done, ctx);
+       if (!acb)
+               return -EIO;
+
+       return 0;
+}
+
+static const cmdinfo_t aio_read_cmd = {
+       .name           = "aio_read",
+       .cfunc          = aio_read_f,
+       .argmin         = 2,
+       .argmax         = -1,
+       .args           = "[-Cqv] [-P pattern ] off len [len..]",
+       .oneline        = "asynchronously reads a number of bytes",
+       .help           = aio_read_help,
+};
+
+static const cmdinfo_t aio_write_cmd;
+
+static void
+aio_write_help(void)
+{
+       printf(
+"\n"
+" asynchronously writes a range of bytes from the given offset source \n"
+" from multiple buffers\n"
+"\n"
+" Example:\n"
+" 'aio_write 512 1k 1k' - writes 2 kilobytes at 512 bytes into the open file\n"
+"\n"
+" Writes into a segment of the currently open file, using a buffer\n"
+" filled with a set pattern (0xcdcdcdcd).\n"
+" The write is performed asynchronously and should the aio_flush command \n"
+" should be used to ensure all outstanding aio requests have been completed\n"
+" -P, -- use different pattern to fill file\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -q, -- quite mode, do not show I/O statistics\n"
+"\n");
+}
+
+
+static int
+aio_write_f(int argc, char **argv)
+{
+       char *p;
+       int count = 0;
+       int nr_iov, i, c;
+       int pattern = 0xcd;
+       struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx));
+       BlockDriverAIOCB *acb;
+
+       while ((c = getopt(argc, argv, "CqP:")) != EOF) {
+               switch (c) {
+               case 'C':
+                       ctx->Cflag = 1;
+                       break;
+               case 'q':
+                       ctx->qflag = 1;
+                       break;
+               case 'P':
+                       pattern = atoi(optarg);
+                       break;
+               default:
+                       return command_usage(&aio_write_cmd);
+               }
+       }
+
+       if (optind > argc - 2)
+               return command_usage(&aio_write_cmd);
+
+       ctx->offset = cvtnum(argv[optind]);
+       if (ctx->offset < 0) {
+               printf("non-numeric length argument -- %s\n", argv[optind]);
+               return 0;
+       }
+       optind++;
+
+       if (ctx->offset & 0x1ff) {
+               printf("offset %lld is not sector aligned\n",
+                       (long long)ctx->offset);
+               return 0;
+       }
+
+       if (count & 0x1ff) {
+               printf("count %d is not sector aligned\n",
+                       count);
+               return 0;
+       }
+
+
+       for (i = optind; i < argc; i++) {
+               size_t len;
+
+               len = cvtnum(argv[optind]);
+               if (len < 0) {
+                       printf("non-numeric length argument -- %s\n", argv[i]);
+                       return 0;
+               }
+               count += len;
+       }
+
+       nr_iov = argc - optind;
+       qemu_iovec_init(&ctx->qiov, nr_iov);
+       ctx->buf = p = qemu_io_alloc(count, pattern);
+       for (i = 0; i < nr_iov; i++) {
+               size_t len;
+
+               len = cvtnum(argv[optind]);
+               if (len < 0) {
+                       printf("non-numeric length argument -- %s\n",
+                               argv[optind]);
+                       return 0;
+               }
+
+               qemu_iovec_add(&ctx->qiov, p, len);
+               p += len;
+               optind++;
+       }
+
+       gettimeofday(&ctx->t1, NULL);
+       acb = bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov,
+                             ctx->qiov.size >> 9, aio_write_done, ctx);
+       if (!acb)
+               return -EIO;
+
+       return 0;
+}
+
+static const cmdinfo_t aio_write_cmd = {
+       .name           = "aio_write",
+       .cfunc          = aio_write_f,
+       .argmin         = 2,
+       .argmax         = -1,
+       .args           = "[-Cq] [-P pattern ] off len [len..]",
+       .oneline        = "asynchronously writes a number of bytes",
+       .help           = aio_write_help,
+};
+
+static int
+aio_flush_f(int argc, char **argv)
+{
+       qemu_aio_flush();
+       return 0;
+}
+
+static const cmdinfo_t aio_flush_cmd = {
+       .name           = "aio_flush",
+       .cfunc          = aio_flush_f,
+       .oneline        = "completes all outstanding aio requets"
+};
+
 static int
 flush_f(int argc, char **argv)
 {
@@ -998,7 +1378,7 @@ static void usage(const char *name)
 {
        printf(
 "Usage: %s [-h] [-V] [-Crsnm] [-c cmd] ... [file]\n"
-"QEMU Disk excerciser\n"
+"QEMU Disk exerciser\n"
 "\n"
 "  -C, --create         create new file if it doesn't exist\n"
 "  -c, --cmd            command to execute\n"
@@ -1083,6 +1463,9 @@ int main(int argc, char **argv)
        add_command(&readv_cmd);
        add_command(&write_cmd);
        add_command(&writev_cmd);
+       add_command(&aio_read_cmd);
+       add_command(&aio_write_cmd);
+       add_command(&aio_flush_cmd);
        add_command(&flush_cmd);
        add_command(&truncate_cmd);
        add_command(&length_cmd);
@@ -1102,6 +1485,11 @@ int main(int argc, char **argv)
                openfile(argv[optind], flags);
        command_loop();
 
+       /*
+        * Make sure all outstanding requests get flushed the program exits.
+        */
+       qemu_aio_flush();
+
        if (bs)
                bdrv_close(bs);
        return 0;