Update Red Hat Copyright Notices
[nbdkit.git] / filters / retry / retry.c
blobc4e85bf7f2e651ddba26885a205e331824e1c3ab
1 /* nbdkit
2 * Copyright Red Hat
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
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
30 * SUCH DAMAGE.
33 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <stdbool.h>
39 #include <inttypes.h>
40 #include <string.h>
41 #include <sys/time.h>
43 #include <nbdkit-filter.h>
45 #include "cleanup.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.
58 static int
59 retry_thread_model (void)
61 return NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS;
64 static int
65 retry_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
66 const char *key, const char *value)
68 int r;
70 if (strcmp (key, "retries") == 0) {
71 if (nbdkit_parse_unsigned ("retries", value, &retries) == -1)
72 return -1;
73 return 0;
75 else if (strcmp (key, "retry-delay") == 0) {
76 if (nbdkit_parse_unsigned ("retry-delay", value, &initial_delay) == -1)
77 return -1;
78 if (initial_delay == 0) {
79 nbdkit_error ("retry-delay cannot be 0");
80 return -1;
82 return 0;
84 else if (strcmp (key, "retry-exponential") == 0) {
85 r = nbdkit_parse_bool (value);
86 if (r == -1)
87 return -1;
88 exponential_backoff = r;
89 return 0;
91 else if (strcmp (key, "retry-readonly") == 0) {
92 r = nbdkit_parse_bool (value);
93 if (r == -1)
94 return -1;
95 force_readonly = r;
96 return 0;
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. */
112 unsigned reopens;
113 bool 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.
121 struct retry_data {
122 int retry; /* Retry number (0 = first time). */
123 int delay; /* Seconds to wait before retrying. */
126 static bool
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;
132 return false;
134 return true;
137 static bool
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;
147 again:
148 if (data->retry >= retries) {
149 nbdkit_debug ("could not recover after %d retries", retries);
150 return false;
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.
162 if (*err == 0)
163 *err = errno;
164 return false;
167 /* Update *data in case we are called again. */
168 data->retry++;
169 if (exponential_backoff)
170 data->delay *= 2;
172 /* Close the old connection. */
173 h->reopens++;
174 h->open = false;
175 if (*next != NULL) {
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) {
180 *err = ESHUTDOWN;
181 goto again;
183 nbdkit_next_context_close (*next);
184 old_next = nbdkit_context_set_next (h->context, NULL);
185 assert (old_next == *next);
186 *next = NULL;
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) {
193 *err = ESHUTDOWN;
194 goto again;
196 if (new_next->prepare (new_next) == -1) {
197 new_next->finalize (new_next);
198 nbdkit_next_context_close (new_next);
199 *err = ESHUTDOWN;
200 goto again;
202 old_next = nbdkit_context_set_next (h->context, new_next);
203 assert (old_next == NULL);
204 *next = new_next;
205 h->open = true;
207 /* Retry the data command. */
208 return true;
211 static void *
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);
219 if (h == NULL) {
220 nbdkit_error ("malloc: %m");
221 return NULL;
224 h->readonly = readonly;
225 h->exportname = strdup (exportname);
226 h->context = nxdata;
227 if (h->exportname == NULL) {
228 nbdkit_error ("strdup: %m");
229 free (h);
230 return NULL;
232 h->reopens = 0;
234 if (next (nxdata, readonly, exportname) != -1)
235 h->open = true;
236 else {
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
241 * connecting.
243 nbdkit_next *next_handle = NULL;
244 int err = ESHUTDOWN;
246 h->open = false;
247 while (! h->open && do_retry (h, &data, &next_handle, "open", &err))
250 if (! h->open) {
251 free (h->exportname);
252 free (h);
253 return NULL;
256 return h;
259 static void
260 retry_close (void *handle)
262 struct retry_handle *h = handle;
264 nbdkit_debug ("reopens needed: %u", h->reopens);
265 free (h->exportname);
266 free (h);
269 static int
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};
276 int r;
278 again:
279 if (! (h->open && valid_range (next, count, offset, false, err)))
280 r = -1;
281 else
282 r = next->pread (next, buf, count, offset, flags, err);
283 if (r == -1 && do_retry (h, &data, &next, "pread", err))
284 goto again;
286 return r;
289 /* Write. */
290 static int
291 retry_pwrite (nbdkit_next *next,
292 void *handle,
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};
298 int r;
300 again:
301 if (h->reopens && force_readonly) {
302 *err = EROFS;
303 return -1;
305 if (! (h->open && valid_range (next, count, offset, true, err)))
306 r = -1;
307 else if (next->can_write (next) != 1) {
308 *err = EROFS;
309 r = -1;
311 else if (flags & NBDKIT_FLAG_FUA &&
312 next->can_fua (next) <= NBDKIT_FUA_NONE) {
313 *err = EIO;
314 r = -1;
316 else
317 r = next->pwrite (next, buf, count, offset, flags, err);
318 if (r == -1 && do_retry (h, &data, &next, "pwrite", err))
319 goto again;
321 return r;
324 /* Trim. */
325 static int
326 retry_trim (nbdkit_next *next,
327 void *handle,
328 uint32_t count, uint64_t offset, uint32_t flags,
329 int *err)
331 struct retry_handle *h = handle;
332 struct retry_data data = {0};
333 int r;
335 again:
336 if (h->reopens && force_readonly) {
337 *err = EROFS;
338 return -1;
340 if (! (h->open && valid_range (next, count, offset, true, err)))
341 r = -1;
342 else if (next->can_trim (next) != 1) {
343 *err = EROFS;
344 r = -1;
346 else if (flags & NBDKIT_FLAG_FUA &&
347 next->can_fua (next) <= NBDKIT_FUA_NONE) {
348 *err = EIO;
349 r = -1;
351 else
352 r = next->trim (next, count, offset, flags, err);
353 if (r == -1 && do_retry (h, &data, &next, "trim", err))
354 goto again;
356 return r;
359 /* Flush. */
360 static int
361 retry_flush (nbdkit_next *next,
362 void *handle, uint32_t flags,
363 int *err)
365 struct retry_handle *h = handle;
366 struct retry_data data = {0};
367 int r;
369 again:
370 if (! h->open)
371 r = -1;
372 else if (next->can_flush (next) != 1) {
373 *err = EIO;
374 r = -1;
376 else
377 r = next->flush (next, flags, err);
378 if (r == -1 && do_retry (h, &data, &next, "flush", err))
379 goto again;
381 return r;
384 /* Zero. */
385 static int
386 retry_zero (nbdkit_next *next,
387 void *handle,
388 uint32_t count, uint64_t offset, uint32_t flags,
389 int *err)
391 struct retry_handle *h = handle;
392 struct retry_data data = {0};
393 int r;
395 again:
396 if (h->reopens && force_readonly) {
397 *err = EROFS;
398 return -1;
400 if (flags & NBDKIT_FLAG_FAST_ZERO &&
401 (! h->open || next->can_fast_zero (next) != 1)) {
402 *err = EOPNOTSUPP;
403 return -1;
405 if (! (h->open && valid_range (next, count, offset, true, err)))
406 r = -1;
407 else if (next->can_zero (next) <= NBDKIT_ZERO_NONE) {
408 *err = EROFS;
409 r = -1;
411 else if (flags & NBDKIT_FLAG_FUA &&
412 next->can_fua (next) <= NBDKIT_FUA_NONE) {
413 *err = EIO;
414 r = -1;
416 else
417 r = next->zero (next, count, offset, flags, err);
418 if (r == -1 && do_retry (h, &data, &next, "zero", err))
419 goto again;
421 return r;
424 /* Extents. */
425 static int
426 retry_extents (nbdkit_next *next,
427 void *handle,
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;
434 int r;
435 size_t i;
437 again:
438 if (! (h->open && valid_range (next, count, offset, false, err)))
439 r = -1;
440 else if (next->can_extents (next) != 1) {
441 *err = EIO;
442 r = -1;
444 else {
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) {
449 *err = errno;
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))
455 goto again;
457 if (r == 0) {
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) {
463 *err = errno;
464 return -1;
469 return r;
472 /* Cache. */
473 static int
474 retry_cache (nbdkit_next *next,
475 void *handle,
476 uint32_t count, uint64_t offset, uint32_t flags,
477 int *err)
479 struct retry_handle *h = handle;
480 struct retry_data data = {0};
481 int r;
483 again:
484 if (! (h->open && valid_range (next, count, offset, false, err)))
485 r = -1;
486 else if (next->can_cache (next) <= NBDKIT_CACHE_NONE) {
487 *err = EIO;
488 r = -1;
490 else
491 r = next->cache (next, count, offset, flags, err);
492 if (r == -1 && do_retry (h, &data, &next, "cache", err))
493 goto again;
495 return r;
498 static struct nbdkit_filter filter = {
499 .name = "retry",
500 .longname = "nbdkit retry filter",
501 .thread_model = retry_thread_model,
502 .config = retry_config,
503 .config_help = retry_config_help,
504 .open = retry_open,
505 .close = retry_close,
506 .pread = retry_pread,
507 .pwrite = retry_pwrite,
508 .trim = retry_trim,
509 .flush = retry_flush,
510 .zero = retry_zero,
511 .extents = retry_extents,
512 .cache = retry_cache,
515 NBDKIT_REGISTER_FILTER (filter)