diff mbox

[03/28] ide-test: add test for werror=stop

Message ID 1404757089-4836-4-git-send-email-jsnow@redhat.com
State New
Headers show

Commit Message

John Snow July 7, 2014, 6:17 p.m. UTC
From: Paolo Bonzini <pbonzini@redhat.com>

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: John Snow <jsnow@redhat.com>
---
 tests/ide-test.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 74 insertions(+)

Comments

Stefan Hajnoczi July 31, 2014, 10:58 a.m. UTC | #1
On Mon, Jul 07, 2014 at 02:17:44PM -0400, John Snow wrote:
> @@ -489,6 +490,72 @@ static void test_flush(void)
>      ide_test_quit();
>  }
>  
> +static void prepare_blkdebug_script(const char *debug_path, const char *event)
> +{
> +    FILE *debug_file = fopen(debug_path, "w");

Please avoid shadowing the global debug_path variable.  Perhaps simply
call the argument 'filename'.

> +static void test_retry_flush(void)
> +{
> +    uint8_t data;
> +    const char *s;
> +
> +    prepare_blkdebug_script(debug_path, "flush_to_disk");
> +
> +    ide_test_start(
> +        "-vnc none "
> +        "-drive file=blkdebug:%s:%s,if=ide,cache=writeback,rerror=stop,werror=stop",
> +        debug_path, tmp_path);
> +
> +    /* FLUSH CACHE command on device 0*/
> +    outb(IDE_BASE + reg_device, 0);
> +    outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
> +
> +    /* Check status while request is in flight*/
> +    data = inb(IDE_BASE + reg_status);
> +    assert_bit_set(data, BSY | DRDY);
> +    assert_bit_clear(data, DF | ERR | DRQ);
> +
> +    sleep(1);                    /* HACK: wait for event */
> +
> +    /* Complete the command */
> +    s = "{'execute':'cont' }";
> +    while (!qmp(s)) {
> +        s = "";
> +        sleep(1);
> +    }

I guess we're supposed to wait for the block I/O error event when the
machine stops.  Please implement that and replace this polling loop.

See the STOP event in docs/qmp/qmp-events.txt.

> @@ -501,6 +568,11 @@ int main(int argc, char **argv)
>          return 0;
>      }
>  
> +    /* Create temporary blkdebug instructions */
> +    fd = mkstemp(debug_path);
> +    g_assert(fd >= 0);
> +    close(fd);
> +

debug_path must be unlinked at the end of main().  Tests should not
clutter up the /tmp directory, all resources must be freed.
John Snow July 31, 2014, 10:06 p.m. UTC | #2
On 07/31/2014 06:58 AM, Stefan Hajnoczi wrote:
> On Mon, Jul 07, 2014 at 02:17:44PM -0400, John Snow wrote:
>> +static void test_retry_flush(void)
>> +{
>> +    uint8_t data;
>> +    const char *s;
>> +
>> +    prepare_blkdebug_script(debug_path, "flush_to_disk");
>> +
>> +    ide_test_start(
>> +        "-vnc none "
>> +        "-drive file=blkdebug:%s:%s,if=ide,cache=writeback,rerror=stop,werror=stop",
>> +        debug_path, tmp_path);
>> +
>> +    /* FLUSH CACHE command on device 0*/
>> +    outb(IDE_BASE + reg_device, 0);
>> +    outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
>> +
>> +    /* Check status while request is in flight*/
>> +    data = inb(IDE_BASE + reg_status);
>> +    assert_bit_set(data, BSY | DRDY);
>> +    assert_bit_clear(data, DF | ERR | DRQ);
>> +
>> +    sleep(1);                    /* HACK: wait for event */
>> +
>> +    /* Complete the command */
>> +    s = "{'execute':'cont' }";
>> +    while (!qmp(s)) {
>> +        s = "";
>> +        sleep(1);
>> +    }
> I guess we're supposed to wait for the block I/O error event when the
> machine stops.  Please implement that and replace this polling loop.
>
> See the STOP event in docs/qmp/qmp-events.txt.
>
Paolo: I edited in a bit to check for the STOP event instead of sleeping 
in V2, but what's the point of setting s = "" and sleeping and resending 
a blank string? Can't we just g_assert(qmp(s)) the first go around, 
provided the STOP event has already occurred?

It works in practice, but I am curious.

--J
Markus Armbruster Aug. 1, 2014, 7:13 a.m. UTC | #3
John Snow <jsnow@redhat.com> writes:

> On 07/31/2014 06:58 AM, Stefan Hajnoczi wrote:
>> On Mon, Jul 07, 2014 at 02:17:44PM -0400, John Snow wrote:
>>> +static void test_retry_flush(void)
>>> +{
>>> +    uint8_t data;
>>> +    const char *s;
>>> +
>>> +    prepare_blkdebug_script(debug_path, "flush_to_disk");
>>> +
>>> +    ide_test_start(
>>> +        "-vnc none "
>>> + "-drive
>>> file=blkdebug:%s:%s,if=ide,cache=writeback,rerror=stop,werror=stop",
>>> +        debug_path, tmp_path);
>>> +
>>> +    /* FLUSH CACHE command on device 0*/
>>> +    outb(IDE_BASE + reg_device, 0);
>>> +    outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
>>> +
>>> +    /* Check status while request is in flight*/
>>> +    data = inb(IDE_BASE + reg_status);
>>> +    assert_bit_set(data, BSY | DRDY);
>>> +    assert_bit_clear(data, DF | ERR | DRQ);
>>> +
>>> +    sleep(1);                    /* HACK: wait for event */
>>> +
>>> +    /* Complete the command */
>>> +    s = "{'execute':'cont' }";
>>> +    while (!qmp(s)) {
>>> +        s = "";
>>> +        sleep(1);
>>> +    }
>> I guess we're supposed to wait for the block I/O error event when the
>> machine stops.  Please implement that and replace this polling loop.
>>
>> See the STOP event in docs/qmp/qmp-events.txt.
>>
> Paolo: I edited in a bit to check for the STOP event instead of
> sleeping in V2, but what's the point of setting s = "" and sleeping
> and resending a blank string? Can't we just g_assert(qmp(s)) the first
> go around, provided the STOP event has already occurred?
>
> It works in practice, but I am curious.

I don't understand the need for sleep.  qmp() & friends wait for a
reply.

Sending "", i.e. nothing, is a crude way to just receive a reply.

Example: boot-order-test.c waits for an event without examining it:

    qmp_discard_response("");   /* HACK: wait for event */

Your loop seems to try to consume replies (events?) until you get NULL,
which I guess happens when the other end closes the connection.

By the way, your loop leaks the responses.  Pretty harmless, but you may
want to QDECREF() just to be orderly.

Proper support for events in libqtest would be nice.
diff mbox

Patch

diff --git a/tests/ide-test.c b/tests/ide-test.c
index 4a0d97f..03af481 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -106,6 +106,7 @@  static QPCIBus *pcibus = NULL;
 static QGuestAllocator *guest_malloc;
 
 static char tmp_path[] = "/tmp/qtest.XXXXXX";
+static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX";
 
 static void ide_test_start(const char *cmdline_fmt, ...)
 {
@@ -489,6 +490,72 @@  static void test_flush(void)
     ide_test_quit();
 }
 
+static void prepare_blkdebug_script(const char *debug_path, const char *event)
+{
+    FILE *debug_file = fopen(debug_path, "w");
+    int ret;
+
+    fprintf(debug_file, "[inject-error]\n");
+    fprintf(debug_file, "event = \"%s\"\n", event);
+    fprintf(debug_file, "errno = \"5\"\n");
+    fprintf(debug_file, "state = \"1\"\n");
+    fprintf(debug_file, "immediately = \"off\"\n");
+    fprintf(debug_file, "once = \"on\"\n");
+
+    fprintf(debug_file, "[set-state]\n");
+    fprintf(debug_file, "event = \"%s\"\n", event);
+    fprintf(debug_file, "new_state = \"2\"\n");
+    fflush(debug_file);
+    g_assert(!ferror(debug_file));
+
+    ret = fclose(debug_file);
+    g_assert(ret == 0);
+}
+
+static void test_retry_flush(void)
+{
+    uint8_t data;
+    const char *s;
+
+    prepare_blkdebug_script(debug_path, "flush_to_disk");
+
+    ide_test_start(
+        "-vnc none "
+        "-drive file=blkdebug:%s:%s,if=ide,cache=writeback,rerror=stop,werror=stop",
+        debug_path, tmp_path);
+
+    /* FLUSH CACHE command on device 0*/
+    outb(IDE_BASE + reg_device, 0);
+    outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
+
+    /* Check status while request is in flight*/
+    data = inb(IDE_BASE + reg_status);
+    assert_bit_set(data, BSY | DRDY);
+    assert_bit_clear(data, DF | ERR | DRQ);
+
+    sleep(1);                    /* HACK: wait for event */
+
+    /* Complete the command */
+    s = "{'execute':'cont' }";
+    while (!qmp(s)) {
+        s = "";
+        sleep(1);
+    }
+
+    /* Check registers */
+    data = inb(IDE_BASE + reg_device);
+    g_assert_cmpint(data & DEV, ==, 0);
+
+    do {
+        data = inb(IDE_BASE + reg_status);
+    } while (data & BSY);
+
+    assert_bit_set(data, DRDY);
+    assert_bit_clear(data, BSY | DF | ERR | DRQ);
+
+    ide_test_quit();
+}
+
 int main(int argc, char **argv)
 {
     const char *arch = qtest_get_arch();
@@ -501,6 +568,11 @@  int main(int argc, char **argv)
         return 0;
     }
 
+    /* Create temporary blkdebug instructions */
+    fd = mkstemp(debug_path);
+    g_assert(fd >= 0);
+    close(fd);
+
     /* Create a temporary raw image */
     fd = mkstemp(tmp_path);
     g_assert(fd >= 0);
@@ -522,6 +594,8 @@  int main(int argc, char **argv)
 
     qtest_add_func("/ide/flush", test_flush);
 
+    qtest_add_func("/ide/retry/flush", test_retry_flush);
+
     ret = g_test_run();
 
     /* Cleanup */