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
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 */
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)
69 nbdkit_error ("cannot parse %s in milliseconds parameter: %s",
75 if (nbdkit_parse_unsigned (key
, value
, r
) == -1)
77 if (*r
* UINT64_C (1000) > UINT_MAX
) {
78 nbdkit_error ("seconds parameter %s is too large: %s", key
, value
);
87 delay (unsigned ms
, int *err
)
89 if (ms
> 0 && nbdkit_nanosleep (ms
/ 1000, (ms
% 1000) * 1000000) == -1) {
99 return delay (delay_read_ms
, err
);
103 write_delay (int *err
)
105 return delay (delay_write_ms
, err
);
109 zero_delay (int *err
)
111 return delay (delay_zero_ms
, err
);
115 trim_delay (int *err
)
117 return delay (delay_trim_ms
, err
);
121 extents_delay (int *err
)
123 return delay (delay_extents_ms
, err
);
127 cache_delay (int *err
)
129 return delay (delay_cache_ms
, err
);
133 open_delay (int *err
)
135 return delay (delay_open_ms
, err
);
138 /* Called for each key=value passed on the command line. */
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)
150 else if (strcmp (key
, "wdelay") == 0) {
151 if (parse_delay (key
, value
, &delay_write_ms
) == -1)
153 /* Historically wdelay set all write-related delays. */
154 delay_zero_ms
= delay_trim_ms
= delay_write_ms
;
157 else if (strcmp (key
, "delay-write") == 0 ||
158 strcmp (key
, "delay-writes") == 0) {
159 if (parse_delay (key
, value
, &delay_write_ms
) == -1)
163 else if (strcmp (key
, "delay-zero") == 0 ||
164 strcmp (key
, "delay-zeroes") == 0) {
165 if (parse_delay (key
, value
, &delay_zero_ms
) == -1)
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)
177 else if (strcmp (key
, "delay-extent") == 0 ||
178 strcmp (key
, "delay-extents") == 0) {
179 if (parse_delay (key
, value
, &delay_extents_ms
) == -1)
183 else if (strcmp (key
, "delay-cache") == 0) {
184 if (parse_delay (key
, value
, &delay_cache_ms
) == -1)
188 else if (strcmp (key
, "delay-fast-zero") == 0) {
189 delay_fast_zero
= nbdkit_parse_bool (value
);
190 if (delay_fast_zero
< 0)
194 else if (strcmp (key
, "delay-open") == 0) {
195 if (parse_delay (key
, value
, &delay_open_ms
) == -1)
199 else if (strcmp (key
, "delay-close") == 0) {
200 if (parse_delay (key
, value
, &delay_close_ms
) == -1)
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 */
223 delay_can_fast_zero (nbdkit_next
*next
,
226 /* Advertise if we are handling fast zero requests locally */
227 if (delay_zero_ms
&& !delay_fast_zero
)
229 return next
->can_fast_zero (next
);
232 /* Open connection. */
234 delay_open (nbdkit_next_open
*next
, nbdkit_context
*nxdata
,
235 int readonly
, const char *exportname
, int is_tls
)
239 if (open_delay (&err
) == -1) {
241 nbdkit_error ("delay: %m");
245 if (next (nxdata
, readonly
, exportname
) == -1)
248 return NBDKIT_HANDLE_NOT_NEEDED
;
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.
264 delay_finalize (nbdkit_next
*next
, void *handle
)
266 const unsigned ms
= delay_close_ms
;
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
277 nanosleep (&ts
, NULL
);
280 return next
->finalize (next
);
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)
291 return next
->pread (next
, buf
, count
, offset
, flags
, err
);
296 delay_pwrite (nbdkit_next
*next
,
298 const void *buf
, uint32_t count
, uint64_t offset
, uint32_t flags
,
301 if (write_delay (err
) == -1)
303 return next
->pwrite (next
, buf
, count
, offset
, flags
, err
);
308 delay_zero (nbdkit_next
*next
,
309 void *handle
, uint32_t count
, uint64_t offset
, uint32_t flags
,
312 if ((flags
& NBDKIT_FLAG_FAST_ZERO
) && delay_zero_ms
&& !delay_fast_zero
) {
316 if (zero_delay (err
) == -1)
318 return next
->zero (next
, count
, offset
, flags
, err
);
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)
329 return next
->trim (next
, count
, offset
, flags
, err
);
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)
340 return next
->extents (next
, count
, offset
, flags
, extents
, err
);
345 delay_cache (nbdkit_next
*next
,
346 void *handle
, uint32_t count
, uint64_t offset
, uint32_t flags
,
349 if (cache_delay (err
) == -1)
351 return next
->cache (next
, count
, offset
, flags
, err
);
354 static struct nbdkit_filter filter
= {
356 .longname
= "nbdkit delay filter",
357 .config
= delay_config
,
358 .config_help
= delay_config_help
,
359 .can_fast_zero
= delay_can_fast_zero
,
361 .finalize
= delay_finalize
,
362 .pread
= delay_pread
,
363 .pwrite
= delay_pwrite
,
366 .extents
= delay_extents
,
367 .cache
= delay_cache
,
370 NBDKIT_REGISTER_FILTER (filter
)