Update Red Hat Copyright Notices
[nbdkit.git] / filters / delay / delay.c
blob57b812eb30bee8463ce7621b3c1ca8b039c19408
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 <string.h>
39 #include <stdbool.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <time.h>
44 #include <nbdkit-filter.h>
46 static unsigned delay_read_ms = 0; /* read delay (milliseconds) */
47 static unsigned delay_write_ms = 0; /* write delay (milliseconds) */
48 static unsigned delay_zero_ms = 0; /* zero delay (milliseconds) */
49 static unsigned delay_trim_ms = 0; /* trim delay (milliseconds) */
50 static unsigned delay_extents_ms = 0;/* extents delay (milliseconds) */
51 static unsigned delay_cache_ms = 0; /* cache delay (milliseconds) */
52 static unsigned delay_open_ms = 0; /* open delay (milliseconds) */
53 static unsigned delay_close_ms = 0; /* close delay (milliseconds) */
55 static int delay_fast_zero = 1; /* whether delaying zero includes fast zero */
57 static int
58 parse_delay (const char *key, const char *value, unsigned *r)
60 size_t len = strlen (value);
62 if (len > 2 && strcmp (&value[len-2], "ms") == 0) {
63 /* We have to use sscanf here instead of nbdkit_parse_unsigned
64 * because that function will reject the "ms" suffix.
66 if (sscanf (value, "%u", r) == 1)
67 return 0;
68 else {
69 nbdkit_error ("cannot parse %s in milliseconds parameter: %s",
70 key, value);
71 return -1;
74 else {
75 if (nbdkit_parse_unsigned (key, value, r) == -1)
76 return -1;
77 if (*r * UINT64_C (1000) > UINT_MAX) {
78 nbdkit_error ("seconds parameter %s is too large: %s", key, value);
79 return -1;
81 *r *= 1000;
82 return 0;
86 static int
87 delay (unsigned ms, int *err)
89 if (ms > 0 && nbdkit_nanosleep (ms / 1000, (ms % 1000) * 1000000) == -1) {
90 *err = errno;
91 return -1;
93 return 0;
96 static int
97 read_delay (int *err)
99 return delay (delay_read_ms, err);
102 static int
103 write_delay (int *err)
105 return delay (delay_write_ms, err);
108 static int
109 zero_delay (int *err)
111 return delay (delay_zero_ms, err);
114 static int
115 trim_delay (int *err)
117 return delay (delay_trim_ms, err);
120 static int
121 extents_delay (int *err)
123 return delay (delay_extents_ms, err);
126 static int
127 cache_delay (int *err)
129 return delay (delay_cache_ms, err);
132 static int
133 open_delay (int *err)
135 return delay (delay_open_ms, err);
138 /* Called for each key=value passed on the command line. */
139 static int
140 delay_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
141 const char *key, const char *value)
143 if (strcmp (key, "rdelay") == 0 ||
144 strcmp (key, "delay-read") == 0 ||
145 strcmp (key, "delay-reads") == 0) {
146 if (parse_delay (key, value, &delay_read_ms) == -1)
147 return -1;
148 return 0;
150 else if (strcmp (key, "wdelay") == 0) {
151 if (parse_delay (key, value, &delay_write_ms) == -1)
152 return -1;
153 /* Historically wdelay set all write-related delays. */
154 delay_zero_ms = delay_trim_ms = delay_write_ms;
155 return 0;
157 else if (strcmp (key, "delay-write") == 0 ||
158 strcmp (key, "delay-writes") == 0) {
159 if (parse_delay (key, value, &delay_write_ms) == -1)
160 return -1;
161 return 0;
163 else if (strcmp (key, "delay-zero") == 0 ||
164 strcmp (key, "delay-zeroes") == 0) {
165 if (parse_delay (key, value, &delay_zero_ms) == -1)
166 return -1;
167 return 0;
169 else if (strcmp (key, "delay-trim") == 0 ||
170 strcmp (key, "delay-trims") == 0 ||
171 strcmp (key, "delay-discard") == 0 ||
172 strcmp (key, "delay-discards") == 0) {
173 if (parse_delay (key, value, &delay_trim_ms) == -1)
174 return -1;
175 return 0;
177 else if (strcmp (key, "delay-extent") == 0 ||
178 strcmp (key, "delay-extents") == 0) {
179 if (parse_delay (key, value, &delay_extents_ms) == -1)
180 return -1;
181 return 0;
183 else if (strcmp (key, "delay-cache") == 0) {
184 if (parse_delay (key, value, &delay_cache_ms) == -1)
185 return -1;
186 return 0;
188 else if (strcmp (key, "delay-fast-zero") == 0) {
189 delay_fast_zero = nbdkit_parse_bool (value);
190 if (delay_fast_zero < 0)
191 return -1;
192 return 0;
194 else if (strcmp (key, "delay-open") == 0) {
195 if (parse_delay (key, value, &delay_open_ms) == -1)
196 return -1;
197 return 0;
199 else if (strcmp (key, "delay-close") == 0) {
200 if (parse_delay (key, value, &delay_close_ms) == -1)
201 return -1;
202 return 0;
204 else
205 return next (nxdata, key, value);
208 #define delay_config_help \
209 "rdelay=<NN>[ms] Read delay in seconds/milliseconds.\n" \
210 "delay-read=<NN>[ms] Read delay in seconds/milliseconds.\n" \
211 "delay-write=<NN>[ms] Write delay in seconds/milliseconds.\n" \
212 "delay-zero=<NN>[ms] Zero delay in seconds/milliseconds.\n" \
213 "delay-trim=<NN>[ms] Trim delay in seconds/milliseconds.\n" \
214 "delay-extents=<NN>[ms] Extents delay in seconds/milliseconds.\n" \
215 "delay-cache=<NN>[ms] Cache delay in seconds/milliseconds.\n" \
216 "wdelay=<NN>[ms] Write, zero and trim delay in secs/msecs.\n" \
217 "delay-fast-zero=<BOOL> Delay fast zero requests (default true).\n" \
218 "delay-open=<NN>[ms] Open delay in seconds/milliseconds.\n" \
219 "delay-close=<NN>[ms] Close delay in seconds/milliseconds."
221 /* Override the plugin's .can_fast_zero if needed */
222 static int
223 delay_can_fast_zero (nbdkit_next *next,
224 void *handle)
226 /* Advertise if we are handling fast zero requests locally */
227 if (delay_zero_ms && !delay_fast_zero)
228 return 1;
229 return next->can_fast_zero (next);
232 /* Open connection. */
233 static void *
234 delay_open (nbdkit_next_open *next, nbdkit_context *nxdata,
235 int readonly, const char *exportname, int is_tls)
237 int err;
239 if (open_delay (&err) == -1) {
240 errno = err;
241 nbdkit_error ("delay: %m");
242 return NULL;
245 if (next (nxdata, readonly, exportname) == -1)
246 return NULL;
248 return NBDKIT_HANDLE_NOT_NEEDED;
251 /* Close connection.
253 * We cannot call nbdkit_nanosleep here because the socket may have
254 * been closed and that function will abort and return immediately.
255 * However we want to force a sleep (even if the server is shutting
256 * down) so use regular nanosleep instead.
258 * We cannot use the .close callback because that happens after the
259 * socket has closed, thus not delaying the client. By using
260 * .finalize we can delay well-behaved clients (those that use
261 * NBD_CMD_DISC). We cannot delay clients that drop the connection.
263 static int
264 delay_finalize (nbdkit_next *next, void *handle)
266 const unsigned ms = delay_close_ms;
268 if (ms > 0) {
269 struct timespec ts;
271 ts.tv_sec = ms / 1000;
272 ts.tv_nsec = (ms % 1000) * 1000000;
273 /* If nanosleep fails we don't really want to interrupt the chain
274 * of finalize calls through the other filters, so ignore any
275 * error here.
277 nanosleep (&ts, NULL);
280 return next->finalize (next);
283 /* Read data. */
284 static int
285 delay_pread (nbdkit_next *next,
286 void *handle, void *buf, uint32_t count, uint64_t offset,
287 uint32_t flags, int *err)
289 if (read_delay (err) == -1)
290 return -1;
291 return next->pread (next, buf, count, offset, flags, err);
294 /* Write data. */
295 static int
296 delay_pwrite (nbdkit_next *next,
297 void *handle,
298 const void *buf, uint32_t count, uint64_t offset, uint32_t flags,
299 int *err)
301 if (write_delay (err) == -1)
302 return -1;
303 return next->pwrite (next, buf, count, offset, flags, err);
306 /* Zero data. */
307 static int
308 delay_zero (nbdkit_next *next,
309 void *handle, uint32_t count, uint64_t offset, uint32_t flags,
310 int *err)
312 if ((flags & NBDKIT_FLAG_FAST_ZERO) && delay_zero_ms && !delay_fast_zero) {
313 *err = ENOTSUP;
314 return -1;
316 if (zero_delay (err) == -1)
317 return -1;
318 return next->zero (next, count, offset, flags, err);
321 /* Trim data. */
322 static int
323 delay_trim (nbdkit_next *next,
324 void *handle, uint32_t count, uint64_t offset,
325 uint32_t flags, int *err)
327 if (trim_delay (err) == -1)
328 return -1;
329 return next->trim (next, count, offset, flags, err);
332 /* Extents. */
333 static int
334 delay_extents (nbdkit_next *next,
335 void *handle, uint32_t count, uint64_t offset, uint32_t flags,
336 struct nbdkit_extents *extents, int *err)
338 if (extents_delay (err) == -1)
339 return -1;
340 return next->extents (next, count, offset, flags, extents, err);
343 /* Cache. */
344 static int
345 delay_cache (nbdkit_next *next,
346 void *handle, uint32_t count, uint64_t offset, uint32_t flags,
347 int *err)
349 if (cache_delay (err) == -1)
350 return -1;
351 return next->cache (next, count, offset, flags, err);
354 static struct nbdkit_filter filter = {
355 .name = "delay",
356 .longname = "nbdkit delay filter",
357 .config = delay_config,
358 .config_help = delay_config_help,
359 .can_fast_zero = delay_can_fast_zero,
360 .open = delay_open,
361 .finalize = delay_finalize,
362 .pread = delay_pread,
363 .pwrite = delay_pwrite,
364 .zero = delay_zero,
365 .trim = delay_trim,
366 .extents = delay_extents,
367 .cache = delay_cache,
370 NBDKIT_REGISTER_FILTER (filter)