[Libguestfs] [PATCH libnbd v2 5/6] examples: copy-libev.c: Add request state

Nir Soffer nirsof at gmail.com
Thu Apr 22 20:56:25 UTC 2021


Replace request waiting flag with a state enum. This makes it easier to
follow the logs, including the type of the request in the completion log:

$ COPY_LIBEV_DEBUG=1 ./copy-libev $SRC $DST
...
copy-libev: r0: start read offset=0 len=65536
...
copy-libev: r0: read completed offset=0 len=65536
copy-libev: r0: start write offset=0 len=65536
...
copy-libev: r0: write completed offset=0 len=65536, time=0.007836

It is also helpful when debugging. Looking in the request array, we can
understand the state of every request:

(gdb) p requests[0]
$9 = {watcher = {active = 0, pending = 0, priority = 0, data = 0x0, cb =
    0x4020e0 <start_request_cb>, at = -1.1344964150339365e-05, repeat = 0},
    offset = 0, length = 65536, zero = false, data = 0x7ffff7373010 "\353c\220",
    index = 0, started = 1615154809.1588342, state = WRITE}
(gdb) p requests[1]
$10 = {watcher = {active = 0, pending = 0, priority = 0, data = 0x0, cb =
    0x4020e0 <start_request_cb>, at = -1.1344964150339365e-05, repeat = 0},
    offset = 12845056, length = 131072, zero = true, data = 0x7ffff7272010 "",
    index = 1, started = 1615154809.1588342, state = ZERO}

I think this will useful for more fine grain sparsifying; when a partial
zero or write request completes, we can restart it to consume the rest
of the payload.

Signed-off-by: Nir Soffer <nsoffer at redhat.com>
---
 examples/copy-libev.c | 51 +++++++++++++++++++++++++++++++++----------
 1 file changed, 39 insertions(+), 12 deletions(-)

diff --git a/examples/copy-libev.c b/examples/copy-libev.c
index d9c8560..6e6cbcb 100644
--- a/examples/copy-libev.c
+++ b/examples/copy-libev.c
@@ -66,6 +66,19 @@ struct connection {
     bool can_extents;
 };
 
+enum request_state {
+    IDLE,       /* Not used yet. */
+    EXTENTS,    /* Getting extents from source. */
+    READ,       /* Read from source. */
+    WRITE,      /* Write to destiation. */
+    ZERO,       /* Write zeroes to destiation. */
+    SLEEP       /* Waiting for extents completion. */
+};
+
+static const char *state_names[] = {
+    "idle", "extents", "read", "write", "zero", "sleep"
+};
+
 struct request {
     ev_timer watcher;       /* For starting on next loop iteration. */
     int64_t offset;
@@ -74,7 +87,7 @@ struct request {
     unsigned char *data;
     size_t index;
     ev_tstamp started;
-    bool waiting;           /* Waiting for extents completion. */
+    enum request_state state;
 };
 
 static struct ev_loop *loop;
@@ -135,6 +148,12 @@ is_zero (const unsigned char *data, size_t len)
   return memcmp (data, p, len) == 0;
 }
 
+static inline const char *
+request_state (struct request *r)
+{
+    return state_names[r->state];
+}
+
 static inline int
 get_fd(struct connection *c)
 {
@@ -198,15 +217,16 @@ extents_completed (void *user_data, int *error)
         src.can_extents = false;
     }
 
-    /* Start requests waiting for extents completion on the next loop
-     * iteration, to avoid deadlock if we need to start a read.
+    /* Start the request to process recvievd extents. This must be done on the
+     * next loop iteration, to avoid deadlock if we need to start a read.
      */
+    start_request_soon(r);
+
+    /* Wake up requests waiting for extents completion */
     for (i = 0; i < MAX_REQUESTS; i++) {
         struct request *r = &requests[i];
-        if (r->waiting) {
-            r->waiting = false;
+        if (r->state == SLEEP)
             start_request_soon (r);
-       }
     }
 
     return 1;
@@ -219,7 +239,8 @@ start_extents (struct request *r)
     int64_t cookie;
 
     if (extents_in_progress) {
-        r->waiting = true;
+        /* Sleep until extents request completes. */
+        r->state = SLEEP;
         return true;
     }
 
@@ -238,7 +259,7 @@ start_extents (struct request *r)
         return false;
     }
 
-    r->waiting = true;
+    r->state = EXTENTS;
     extents_in_progress = true;
 
     return true;
@@ -315,6 +336,7 @@ next_extent (struct request *r)
 static inline void
 start_request_soon (struct request *r)
 {
+    r->state = IDLE;
     ev_timer_init (&r->watcher, start_request_cb, 0, 0);
     ev_timer_start (loop, &r->watcher);
 }
@@ -340,8 +362,6 @@ start_request(struct request *r)
     if (src.can_extents && extents == NULL && start_extents (r))
         return;
 
-    DEBUG ("r%d: start request offset=%ld", r->index, offset);
-
     if (src.can_extents) {
         /* Handle the next extent. */
         next_extent (r);
@@ -369,6 +389,8 @@ start_read(struct request *r)
 {
     int64_t cookie;
 
+    r->state = READ;
+
     DEBUG ("r%d: start read offset=%ld len=%ld",
            r->index, r->offset, r->length);
 
@@ -402,6 +424,8 @@ start_write(struct request *r)
 {
     int64_t cookie;
 
+    r->state = WRITE;
+
     DEBUG ("r%d: start write offset=%ld len=%ld",
            r->index, r->offset, r->length);
 
@@ -419,6 +443,8 @@ start_zero(struct request *r)
 {
     int64_t cookie;
 
+    r->state = ZERO;
+
     DEBUG ("r%d: start zero offset=%ld len=%ld",
            r->index, r->offset, r->length);
 
@@ -439,8 +465,9 @@ request_completed (void *user_data, int *error)
 
     written += r->length;
 
-    DEBUG ("r%d: request completed offset=%ld len=%ld time=%.6f",
-           r->index, r->offset, r->length, ev_now (loop) - r->started);
+    DEBUG ("r%d: %s completed offset=%ld len=%ld, time=%.6f",
+           r->index, request_state (r), r->offset, r->length,
+           ev_now (loop) - r->started);
 
     if (written == size) {
         /* The last write completed. Stop all watchers and break out
-- 
2.26.3




More information about the Libguestfs mailing list