- Add FAIL macro to simplfy error handling. - Support simple sparsification using full requests. Changes since v1: - Keep example standalone by not using izzero.h (Rich) - Use nbd_zero only if destination supports zero (Eric) - Fix whitespace in one DEBUG call Nir Soffer (2): examples: copy-libev: Add FAIL macro examples: copy-libev.c: Simple sparsifying examples/copy-libev.c | 175 +++++++++++++++++++++++++++--------------- 1 file changed, 115 insertions(+), 60 deletions(-) -- 2.26.2
Nir Soffer
2021-Mar-05 22:33 UTC
[Libguestfs] [PATCH libnbd v2 1/2] examples: copy-libev: Add FAIL macro
Stop repeating fprintf (stderr, "..."); exit (EXIT_FAILURE). Signed-off-by: Nir Soffer <nsoffer at redhat.com> --- examples/copy-libev.c | 81 +++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 46 deletions(-) diff --git a/examples/copy-libev.c b/examples/copy-libev.c index 034711a..ca1bf3c 100644 --- a/examples/copy-libev.c +++ b/examples/copy-libev.c @@ -17,8 +17,10 @@ */ #include <assert.h> +#include <errno.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include <libnbd.h> @@ -42,10 +44,18 @@ #define MIN(a,b) (a) < (b) ? (a) : (b) -#define DEBUG(fmt, ...) \ - do { \ - if (debug) \ - fprintf (stderr, "copy-libev: " fmt "\n", ## __VA_ARGS__); \ +#define PROG "copy-libev" + +#define DEBUG(fmt, ...) \ + do { \ + if (debug) \ + fprintf (stderr, PROG ": " fmt "\n", ## __VA_ARGS__); \ + } while (0) + +#define FAIL(fmt, ...) \ + do { \ + fprintf (stderr, PROG ": " fmt "\n", ## __VA_ARGS__); \ + exit (EXIT_FAILURE); \ } while (0) struct connection { @@ -111,10 +121,8 @@ start_read(struct request *r) (nbd_completion_callback) { .callback=read_completed, .user_data=r }, 0); - if (cookie == -1) { - fprintf (stderr, "start_read: %s", nbd_get_error ()); - exit (EXIT_FAILURE); - } + if (cookie == -1) + FAIL ("Cannot start read: %s", nbd_get_error ()); offset += r->length; } @@ -133,10 +141,8 @@ read_completed (void *user_data, int *error) (nbd_completion_callback) { .callback=write_completed, .user_data=r }, 0); - if (cookie == -1) { - fprintf (stderr, "read_completed: %s", nbd_get_error ()); - exit (EXIT_FAILURE); - } + if (cookie == -1) + FAIL ("Cannot start write: %s", nbd_get_error ()); return 1; } @@ -207,44 +213,31 @@ main (int argc, char *argv[]) loop = EV_DEFAULT; - if (argc != 3) { - fprintf (stderr, "Usage: copy-ev src-uri dst-uri\n"); - exit (EXIT_FAILURE); - } + if (argc != 3) + FAIL ("Usage: %s src-uri dst-uri", PROG); src.nbd = nbd_create (); - if (src.nbd == NULL) { - fprintf (stderr, "nbd_create: %s\n", nbd_get_error ()); - exit (EXIT_FAILURE); - } - + if (src.nbd == NULL) + FAIL ("Cannot create source: %s", nbd_get_error ()); dst.nbd = nbd_create (); - if (dst.nbd == NULL) { - fprintf (stderr, "nbd_create: %s\n", nbd_get_error ()); - exit (EXIT_FAILURE); - } + if (dst.nbd == NULL) + FAIL ("Cannot create destination: %s", nbd_get_error ()); debug = nbd_get_debug (src.nbd); /* Connecting is fast, so use the syncronous API. */ - if (nbd_connect_uri (src.nbd, argv[1])) { - fprintf (stderr, "nbd_connect_uri: %s\n", nbd_get_error ()); - exit (EXIT_FAILURE); - } + if (nbd_connect_uri (src.nbd, argv[1])) + FAIL ("Cannot connect to source: %s", nbd_get_error ()); - if (nbd_connect_uri (dst.nbd, argv[2])) { - fprintf (stderr, "nbd_connect_uri: %s\n", nbd_get_error ()); - exit (EXIT_FAILURE); - } + if (nbd_connect_uri (dst.nbd, argv[2])) + FAIL ("Cannot connect to destination: %s", nbd_get_error ()); size = nbd_get_size (src.nbd); - if (size > nbd_get_size (dst.nbd)) { - fprintf (stderr, "destinatio is not large enough\n"); - exit (EXIT_FAILURE); - } + if (size > nbd_get_size (dst.nbd)) + FAIL ("Destinatio is not large enough\n"); /* Start the copy "loop". When request completes, it starts the * next request, until entire image was copied. */ @@ -253,10 +246,8 @@ main (int argc, char *argv[]) struct request *r = &requests[i]; r->data = malloc (REQUEST_SIZE); - if (r->data == NULL) { - perror ("malloc"); - exit (EXIT_FAILURE); - } + if (r->data == NULL) + FAIL ("Cannot allocate buffer: %s", strerror (errno)); start_read(r); } @@ -284,11 +275,9 @@ main (int argc, char *argv[]) /* Copy completed - flush data to storage. */ - DEBUG("flush"); - if (nbd_flush (dst.nbd, 0)) { - fprintf (stderr, "Cannot flush: %s", nbd_get_error ()); - exit (EXIT_FAILURE); - } + DEBUG ("flush"); + if (nbd_flush (dst.nbd, 0)) + FAIL ("Cannot flush: %s", nbd_get_error ()); /* We don't care about errors here since data was flushed. */ -- 2.26.2
Nir Soffer
2021-Mar-05 22:33 UTC
[Libguestfs] [PATCH libnbd v2 2/2] examples: copy-libev.c: Simple sparsifying
If the destination server supports the zero operation, and a request includes only zeroes, use nbd_zero instead of nbd_pwrite. To keep the example standalone, don't use common/include/iszero.h. Implement is_zero based on Rusty Russell's blog: https://rusty.ozlabs.org/?p=560 Signed-off-by: Nir Soffer <nsoffer at redhat.com> --- examples/copy-libev.c | 94 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 14 deletions(-) diff --git a/examples/copy-libev.c b/examples/copy-libev.c index ca1bf3c..1b1e9df 100644 --- a/examples/copy-libev.c +++ b/examples/copy-libev.c @@ -61,6 +61,7 @@ struct connection { ev_io watcher; struct nbd_handle *nbd; + bool can_zero; }; struct request { @@ -79,9 +80,35 @@ static int64_t offset; static int64_t written; static bool debug; +static void start_request(struct request *r); static void start_read(struct request *r); +static void start_write(struct request *r); +static void start_zero(struct request *r); static int read_completed(void *user_data, int *error); -static int write_completed(void *user_data, int *error); +static int request_completed(void *user_data, int *error); + +/* Return true iff data is all zero bytes. + * + * Based on Rusty Russell's memeqzero: + * https://rusty.ozlabs.org/?p=560 + */ +static bool +is_zero (const unsigned char *data, size_t len) +{ + const unsigned char *p = data; + size_t i; + + for (i = 0; i < 16; i++) { + if (len == 0) + return true; + if (*p) + return false; + p++; + len--; + } + + return memcmp (data, p, len) == 0; +} static inline int get_fd(struct connection *c) @@ -104,16 +131,25 @@ get_events(struct connection *c) return events; } +/* Start async copy or zero request. */ static void -start_read(struct request *r) +start_request(struct request *r) { - int64_t cookie; - assert (offset < size); r->length = MIN (REQUEST_SIZE, size - offset); r->offset = offset; + start_read (r); + + offset += r->length; +} + +static void +start_read(struct request *r) +{ + int64_t cookie; + DEBUG ("start read offset=%ld len=%ld", r->offset, r->length); cookie = nbd_aio_pread ( @@ -123,38 +159,64 @@ start_read(struct request *r) 0); if (cookie == -1) FAIL ("Cannot start read: %s", nbd_get_error ()); - - offset += r->length; } static int read_completed (void *user_data, int *error) { struct request *r = (struct request *)user_data; + + DEBUG ("read completed offset=%ld len=%ld", r->offset, r->length); + + if (dst.can_zero && is_zero (r->data, r->length)) + start_zero (r); + else + start_write (r); + + return 1; +} + +static void +start_write(struct request *r) +{ int64_t cookie; - DEBUG ("read completed, starting write offset=%ld len=%ld", - r->offset, r->length); + DEBUG ("start write offset=%ld len=%ld", r->offset, r->length); cookie = nbd_aio_pwrite ( dst.nbd, r->data, r->length, r->offset, - (nbd_completion_callback) { .callback=write_completed, + (nbd_completion_callback) { .callback=request_completed, .user_data=r }, 0); if (cookie == -1) FAIL ("Cannot start write: %s", nbd_get_error ()); +} - return 1; +static void +start_zero(struct request *r) +{ + int64_t cookie; + + DEBUG ("start zero offset=%ld len=%ld", r->offset, r->length); + + cookie = nbd_aio_zero ( + dst.nbd, r->length, r->offset, + (nbd_completion_callback) { .callback=request_completed, + .user_data=r }, + 0); + if (cookie == -1) + FAIL ("Cannot start zero: %s", nbd_get_error ()); } +/* Called when async copy or zero request completed. */ static int -write_completed (void *user_data, int *error) +request_completed (void *user_data, int *error) { struct request *r = (struct request *)user_data; written += r->length; - DEBUG ("write completed offset=%ld len=%ld", r->offset, r->length); + DEBUG ("request completed offset=%ld len=%ld", r->offset, r->length); if (written == size) { /* The last write completed. Stop all watchers and break out @@ -168,7 +230,7 @@ write_completed (void *user_data, int *error) /* If we have data to read, start a new read. */ if (offset < size) - start_read(r); + start_request(r); return 1; } @@ -239,6 +301,10 @@ main (int argc, char *argv[]) if (size > nbd_get_size (dst.nbd)) FAIL ("Destinatio is not large enough\n"); + /* Check destination server capabilities. */ + + dst.can_zero = nbd_can_zero (dst.nbd) > 0; + /* Start the copy "loop". When request completes, it starts the * next request, until entire image was copied. */ @@ -249,7 +315,7 @@ main (int argc, char *argv[]) if (r->data == NULL) FAIL ("Cannot allocate buffer: %s", strerror (errno)); - start_read(r); + start_request(r); } /* Start watching events on src and dst handles. */ -- 2.26.2