[Libguestfs] [nbdkit PATCH 1/2] retry: Add in retry support during .open

Richard W.M. Jones rjones at redhat.com
Sat Jan 28 13:47:32 UTC 2023


On Fri, Jan 27, 2023 at 02:41:22PM -0600, Eric Blake wrote:
> Now that a filter can open a backend as many times as it wants,
> there's no longer a technical reason we can't retry .open.  However,
> adding retry logic here does mean we have to weaken an assert in the
> server backend code, since prepare can now be reached more than once.
> 
> Test coverage will be added in a separate patch, so that it becomes
> easy to swap patch order and see that the test fails without this
> patch.
> 
> See also https://bugzilla.redhat.com/show_bug.cgi?id=1841820
> ---
>  server/backend.c      |  7 +++-
>  filters/retry/retry.c | 98 +++++++++++++++++++++++++------------------
>  2 files changed, 63 insertions(+), 42 deletions(-)
> 
> diff --git a/server/backend.c b/server/backend.c
> index 3bbba601..45889ea9 100644
> --- a/server/backend.c
> +++ b/server/backend.c
> @@ -1,5 +1,5 @@
>  /* nbdkit
> - * Copyright (C) 2013-2021 Red Hat Inc.
> + * Copyright (C) 2013-2023 Red Hat Inc.
>   *
>   * Redistribution and use in source and binary forms, with or without
>   * modification, are permitted provided that the following conditions are
> @@ -312,7 +312,10 @@ backend_prepare (struct context *c)
>    struct backend *b = c->b;
> 
>    assert (c->handle);
> -  assert ((c->state & (HANDLE_OPEN | HANDLE_CONNECTED)) == HANDLE_OPEN);
> +  assert (c->state & HANDLE_OPEN);
> +
> +  if (c->state & HANDLE_CONNECTED)
> +    return 0;

OK because CONNECTED means the handle has been opened & prepared already.

>    /* Call these in order starting from the filter closest to the
>     * plugin, similar to typical .open order.  But remember that
> diff --git a/filters/retry/retry.c b/filters/retry/retry.c
> index fb1d0526..7abf74c4 100644
> --- a/filters/retry/retry.c
> +++ b/filters/retry/retry.c
> @@ -1,5 +1,5 @@
>  /* nbdkit
> - * Copyright (C) 2019-2021 Red Hat Inc.
> + * Copyright (C) 2019-2023 Red Hat Inc.
>   *
>   * Redistribution and use in source and binary forms, with or without
>   * modification, are permitted provided that the following conditions are
> @@ -113,45 +113,6 @@ struct retry_handle {
>    bool open;
>  };
> 
> -static void *
> -retry_open (nbdkit_next_open *next, nbdkit_context *nxdata,
> -            int readonly, const char *exportname, int is_tls)
> -{
> -  struct retry_handle *h;
> -
> -  if (next (nxdata, readonly, exportname) == -1)
> -    return NULL;
> -
> -  h = malloc (sizeof *h);
> -  if (h == NULL) {
> -    nbdkit_error ("malloc: %m");
> -    return NULL;
> -  }
> -
> -  h->readonly = readonly;
> -  h->exportname = strdup (exportname);
> -  h->context = nxdata;
> -  if (h->exportname == NULL) {
> -    nbdkit_error ("strdup: %m");
> -    free (h);
> -    return NULL;
> -  }
> -  h->reopens = 0;
> -  h->open = true;
> -
> -  return h;
> -}
> -
> -static void
> -retry_close (void *handle)
> -{
> -  struct retry_handle *h = handle;
> -
> -  nbdkit_debug ("reopens needed: %u", h->reopens);
> -  free (h->exportname);
> -  free (h);
> -}
> -
>  /* This function encapsulates the common retry logic used across all
>   * data commands.  If it returns true then the data command will retry
>   * the operation.  ???struct retry_data??? is stack data saved between
> @@ -247,6 +208,63 @@ do_retry (struct retry_handle *h, struct retry_data *data,
>    return true;
>  }
> 
> +static void *
> +retry_open (nbdkit_next_open *next, nbdkit_context *nxdata,
> +            int readonly, const char *exportname, int is_tls)
> +{
> +  struct retry_handle *h;
> +  struct retry_data data = {0};
> +
> +  h = malloc (sizeof *h);
> +  if (h == NULL) {
> +    nbdkit_error ("malloc: %m");
> +    return NULL;
> +  }
> +
> +  h->readonly = readonly;
> +  h->exportname = strdup (exportname);
> +  h->context = nxdata;
> +  if (h->exportname == NULL) {
> +    nbdkit_error ("strdup: %m");
> +    free (h);
> +    return NULL;
> +  }
> +  h->reopens = 0;
> +
> +  if (next (nxdata, readonly, exportname) != -1)
> +    h->open = true;
> +  else {
> +    /* Careful - our .open must not return a handle unless do_retry()
> +     * works, as the caller's next action will be calling .get_size
> +     * and similar probe functions which we do not bother to wire up
> +     * into retry logic because they only need to be used right after
> +     * connecting.
> +     */
> +    nbdkit_next *next_handle = NULL;
> +    int err = ESHUTDOWN;
> +
> +    while (! h->open && do_retry (h, &data, &next_handle, "open", &err))
> +      ;
> +
> +    if (! h->open) {
> +      free (h->exportname);
> +      free (h);
> +      return NULL;
> +    }
> +  }
> +  return h;
> +}
> +
> +static void
> +retry_close (void *handle)
> +{
> +  struct retry_handle *h = handle;
> +
> +  nbdkit_debug ("reopens needed: %u", h->reopens);
> +  free (h->exportname);
> +  free (h);
> +}
> +
>  static int
>  retry_pread (nbdkit_next *next,
>               void *handle, void *buf, uint32_t count, uint64_t offset,
> -- 

Seems OK, ACK (modulo your comment about leaking memory
in your reply).

I checked the documentation of the filter and it doesn't mention the
limitation, so it seems we don't need to adjust the documentation at
all.

I will try this out with the ssh filter once the bug moves into post.

Thanks,

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-builder quickly builds VMs from scratch
http://libguestfs.org/virt-builder.1.html


More information about the Libguestfs mailing list