4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 #include <nbdkit-filter.h>
46 #include "windows-compat.h"
48 static unsigned retries
= 5; /* 0 = filter is disabled */
49 static unsigned initial_delay
= 2;
50 static bool exponential_backoff
= true;
51 static bool force_readonly
= false;
53 /* Currently nbdkit_backend_reopen is not safe if another thread makes a
54 * request on the same connection (but on other connections it's OK).
55 * To work around this for now we limit the thread model here, but
56 * this is something we could improve in server/backend.c in future.
59 retry_thread_model (void)
61 return NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS
;
65 retry_config (nbdkit_next_config
*next
, nbdkit_backend
*nxdata
,
66 const char *key
, const char *value
)
70 if (strcmp (key
, "retries") == 0) {
71 if (nbdkit_parse_unsigned ("retries", value
, &retries
) == -1)
75 else if (strcmp (key
, "retry-delay") == 0) {
76 if (nbdkit_parse_unsigned ("retry-delay", value
, &initial_delay
) == -1)
78 if (initial_delay
== 0) {
79 nbdkit_error ("retry-delay cannot be 0");
84 else if (strcmp (key
, "retry-exponential") == 0) {
85 r
= nbdkit_parse_bool (value
);
88 exponential_backoff
= r
;
91 else if (strcmp (key
, "retry-readonly") == 0) {
92 r
= nbdkit_parse_bool (value
);
99 return next (nxdata
, key
, value
);
102 #define retry_config_help \
103 "retries=<N> Number of retries (default: 5).\n" \
104 "retry-delay=<N> Seconds to wait before retry (default: 2).\n" \
105 "retry-exponential=yes|no Exponential back-off (default: yes).\n" \
106 "retry-readonly=yes|no Force read-only on failure (default: no).\n"
108 struct retry_handle
{
109 int readonly
; /* Save original readonly setting. */
110 char *exportname
; /* Client exportname. */
111 nbdkit_context
*context
; /* Context learned during .open. */
116 /* This function encapsulates the common retry logic used across all
117 * data commands. If it returns true then the data command will retry
118 * the operation. ‘struct retry_data’ is stack data saved between
119 * retries within the same command, and is initialized to zero.
122 int retry
; /* Retry number (0 = first time). */
123 int delay
; /* Seconds to wait before retrying. */
127 valid_range (nbdkit_next
*next
,
128 uint32_t count
, uint64_t offset
, bool is_write
, int *err
)
130 if ((int64_t) offset
+ count
> next
->get_size (next
)) {
131 *err
= is_write
? ENOSPC
: EIO
;
138 do_retry (struct retry_handle
*h
, struct retry_data
*data
,
139 nbdkit_next
**next
, const char *method
, int *err
)
141 nbdkit_next
*new_next
, *old_next
;
143 /* If it's the first retry, initialize the other fields in *data. */
144 if (data
->retry
== 0)
145 data
->delay
= initial_delay
;
148 if (data
->retry
>= retries
) {
149 nbdkit_debug ("could not recover after %d retries", retries
);
153 /* Since we will retry, log the original errno otherwise it will be lost. */
154 nbdkit_debug ("%s failed: original errno = %d", method
, *err
);
156 nbdkit_debug ("retry %d: waiting %d seconds before retrying",
157 data
->retry
+1, data
->delay
);
158 if (nbdkit_nanosleep (data
->delay
, 0) == -1) {
159 /* We could do this but it would overwrite the more important
160 * errno from the underlying data call.
167 /* Update *data in case we are called again. */
169 if (exponential_backoff
)
172 /* Close the old connection. */
176 /* Failure to finalize a connection indicates permanent data loss,
177 * which we treat the same as the original command failing.
179 if ((*next
)->finalize (*next
) == -1) {
183 nbdkit_next_context_close (*next
);
184 old_next
= nbdkit_context_set_next (h
->context
, NULL
);
185 assert (old_next
== *next
);
188 /* Open a new connection. */
189 new_next
= nbdkit_next_context_open (nbdkit_context_get_backend (h
->context
),
190 h
->readonly
|| force_readonly
,
191 h
->exportname
, false);
192 if (new_next
== NULL
) {
196 if (new_next
->prepare (new_next
) == -1) {
197 new_next
->finalize (new_next
);
198 nbdkit_next_context_close (new_next
);
202 old_next
= nbdkit_context_set_next (h
->context
, new_next
);
203 assert (old_next
== NULL
);
207 /* Retry the data command. */
212 retry_open (nbdkit_next_open
*next
, nbdkit_context
*nxdata
,
213 int readonly
, const char *exportname
, int is_tls
)
215 struct retry_handle
*h
;
216 struct retry_data data
= {0};
218 h
= malloc (sizeof *h
);
220 nbdkit_error ("malloc: %m");
224 h
->readonly
= readonly
;
225 h
->exportname
= strdup (exportname
);
227 if (h
->exportname
== NULL
) {
228 nbdkit_error ("strdup: %m");
234 if (next (nxdata
, readonly
, exportname
) != -1)
237 /* Careful - our .open must not return a handle unless do_retry()
238 * works, as the caller's next action will be calling .get_size
239 * and similar probe functions which we do not bother to wire up
240 * into retry logic because they only need to be used right after
243 nbdkit_next
*next_handle
= NULL
;
247 while (! h
->open
&& do_retry (h
, &data
, &next_handle
, "open", &err
))
251 free (h
->exportname
);
260 retry_close (void *handle
)
262 struct retry_handle
*h
= handle
;
264 nbdkit_debug ("reopens needed: %u", h
->reopens
);
265 free (h
->exportname
);
270 retry_pread (nbdkit_next
*next
,
271 void *handle
, void *buf
, uint32_t count
, uint64_t offset
,
272 uint32_t flags
, int *err
)
274 struct retry_handle
*h
= handle
;
275 struct retry_data data
= {0};
279 if (! (h
->open
&& valid_range (next
, count
, offset
, false, err
)))
282 r
= next
->pread (next
, buf
, count
, offset
, flags
, err
);
283 if (r
== -1 && do_retry (h
, &data
, &next
, "pread", err
))
291 retry_pwrite (nbdkit_next
*next
,
293 const void *buf
, uint32_t count
, uint64_t offset
,
294 uint32_t flags
, int *err
)
296 struct retry_handle
*h
= handle
;
297 struct retry_data data
= {0};
301 if (h
->reopens
&& force_readonly
) {
305 if (! (h
->open
&& valid_range (next
, count
, offset
, true, err
)))
307 else if (next
->can_write (next
) != 1) {
311 else if (flags
& NBDKIT_FLAG_FUA
&&
312 next
->can_fua (next
) <= NBDKIT_FUA_NONE
) {
317 r
= next
->pwrite (next
, buf
, count
, offset
, flags
, err
);
318 if (r
== -1 && do_retry (h
, &data
, &next
, "pwrite", err
))
326 retry_trim (nbdkit_next
*next
,
328 uint32_t count
, uint64_t offset
, uint32_t flags
,
331 struct retry_handle
*h
= handle
;
332 struct retry_data data
= {0};
336 if (h
->reopens
&& force_readonly
) {
340 if (! (h
->open
&& valid_range (next
, count
, offset
, true, err
)))
342 else if (next
->can_trim (next
) != 1) {
346 else if (flags
& NBDKIT_FLAG_FUA
&&
347 next
->can_fua (next
) <= NBDKIT_FUA_NONE
) {
352 r
= next
->trim (next
, count
, offset
, flags
, err
);
353 if (r
== -1 && do_retry (h
, &data
, &next
, "trim", err
))
361 retry_flush (nbdkit_next
*next
,
362 void *handle
, uint32_t flags
,
365 struct retry_handle
*h
= handle
;
366 struct retry_data data
= {0};
372 else if (next
->can_flush (next
) != 1) {
377 r
= next
->flush (next
, flags
, err
);
378 if (r
== -1 && do_retry (h
, &data
, &next
, "flush", err
))
386 retry_zero (nbdkit_next
*next
,
388 uint32_t count
, uint64_t offset
, uint32_t flags
,
391 struct retry_handle
*h
= handle
;
392 struct retry_data data
= {0};
396 if (h
->reopens
&& force_readonly
) {
400 if (flags
& NBDKIT_FLAG_FAST_ZERO
&&
401 (! h
->open
|| next
->can_fast_zero (next
) != 1)) {
405 if (! (h
->open
&& valid_range (next
, count
, offset
, true, err
)))
407 else if (next
->can_zero (next
) <= NBDKIT_ZERO_NONE
) {
411 else if (flags
& NBDKIT_FLAG_FUA
&&
412 next
->can_fua (next
) <= NBDKIT_FUA_NONE
) {
417 r
= next
->zero (next
, count
, offset
, flags
, err
);
418 if (r
== -1 && do_retry (h
, &data
, &next
, "zero", err
))
426 retry_extents (nbdkit_next
*next
,
428 uint32_t count
, uint64_t offset
, uint32_t flags
,
429 struct nbdkit_extents
*extents
, int *err
)
431 struct retry_handle
*h
= handle
;
432 struct retry_data data
= {0};
433 CLEANUP_EXTENTS_FREE
struct nbdkit_extents
*extents2
= NULL
;
438 if (! (h
->open
&& valid_range (next
, count
, offset
, false, err
)))
440 else if (next
->can_extents (next
) != 1) {
445 /* Each retry must begin with extents reset to the right beginning. */
446 nbdkit_extents_free (extents2
);
447 extents2
= nbdkit_extents_new (offset
, next
->get_size (next
));
448 if (extents2
== NULL
) {
450 return -1; /* Not worth a retry after ENOMEM. */
452 r
= next
->extents (next
, count
, offset
, flags
, extents2
, err
);
454 if (r
== -1 && do_retry (h
, &data
, &next
, "extents", err
))
458 /* Transfer the successful extents back to the caller. */
459 for (i
= 0; i
< nbdkit_extents_count (extents2
); ++i
) {
460 struct nbdkit_extent e
= nbdkit_get_extent (extents2
, i
);
462 if (nbdkit_add_extent (extents
, e
.offset
, e
.length
, e
.type
) == -1) {
474 retry_cache (nbdkit_next
*next
,
476 uint32_t count
, uint64_t offset
, uint32_t flags
,
479 struct retry_handle
*h
= handle
;
480 struct retry_data data
= {0};
484 if (! (h
->open
&& valid_range (next
, count
, offset
, false, err
)))
486 else if (next
->can_cache (next
) <= NBDKIT_CACHE_NONE
) {
491 r
= next
->cache (next
, count
, offset
, flags
, err
);
492 if (r
== -1 && do_retry (h
, &data
, &next
, "cache", err
))
498 static struct nbdkit_filter filter
= {
500 .longname
= "nbdkit retry filter",
501 .thread_model
= retry_thread_model
,
502 .config
= retry_config
,
503 .config_help
= retry_config_help
,
505 .close
= retry_close
,
506 .pread
= retry_pread
,
507 .pwrite
= retry_pwrite
,
509 .flush
= retry_flush
,
511 .extents
= retry_extents
,
512 .cache
= retry_cache
,
515 NBDKIT_REGISTER_FILTER (filter
)