Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. 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.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #ifdef HAVE_CONFIG_H
32 : : #include "config.h"
33 : : #endif
34 : :
35 : : #include <stdio.h>
36 : : #include <errno.h>
37 : : #include <fcntl.h>
38 : : #include <regex.h>
39 : : #include <unistd.h>
40 : : #include <stdlib.h>
41 : : #include <libgen.h>
42 : : #include <sys/mman.h>
43 : : #include <sys/ioctl.h>
44 : : #include <sys/stat.h>
45 : : #include <sys/types.h>
46 : : #include <sys/file.h>
47 : :
48 : : #include "debug.h"
49 : : #include "libvhd.h"
50 : : #include "tapdisk-image.h"
51 : : #include "tapdisk-driver.h"
52 : : #include "tapdisk-server.h"
53 : : #include "tapdisk-vbd.h"
54 : : #include "tapdisk-metrics.h"
55 : : #include "tapdisk-disktype.h"
56 : : #include "tapdisk-interface.h"
57 : : #include "tapdisk-stats.h"
58 : : #include "tapdisk-storage.h"
59 : : #include "tapdisk-nbdserver.h"
60 : : #include "td-stats.h"
61 : : #include "tapdisk-utils.h"
62 : :
63 : : #define DBG(_level, _f, _a...) tlog_write(_level, _f, ##_a)
64 : : #define ERR(_err, _f, _a...) tlog_error(_err, _f, ##_a)
65 : :
66 : : #define INFO(_f, _a...) tlog_syslog(TLOG_INFO, "vbd: " _f, ##_a)
67 : : #define ERROR(_f, _a...) tlog_syslog(TLOG_WARN, "vbd: " _f, ##_a)
68 : :
69 : : #define TD_VBD_EIO_RETRIES 10
70 : : #define TD_VBD_EIO_SLEEP 1
71 : : #define TD_VBD_WATCHDOG_TIMEOUT 10
72 : :
73 : : char* op_strings[TD_OPS_END] ={"read", "write", "block_status"};
74 : :
75 : : static void tapdisk_vbd_complete_vbd_request(td_vbd_t *, td_vbd_request_t *);
76 : : static int tapdisk_vbd_queue_ready(td_vbd_t *);
77 : : static void tapdisk_vbd_check_complete_requests(td_vbd_t *);
78 : : static void tapdisk_vbd_check_requests_for_issue(td_vbd_t *);
79 : :
80 : : static bool log=true;
81 : :
82 : : /*
83 : : * initialization
84 : : */
85 : :
86 : : static void
87 : 2 : tapdisk_vbd_mark_progress(td_vbd_t *vbd)
88 : : {
89 : 2 : gettimeofday(&vbd->ts, NULL);
90 : : }
91 : :
92 : : td_vbd_t*
93 : 0 : tapdisk_vbd_create(uint16_t uuid)
94 : : {
95 : 0 : td_vbd_t *vbd;
96 : :
97 : 0 : vbd = calloc(1, sizeof(td_vbd_t));
98 [ # # ]: 0 : if (!vbd) {
99 : 0 : EPRINTF("failed to allocate tapdisk state\n");
100 : 0 : return NULL;
101 : : }
102 : :
103 : 0 : shm_init(&vbd->rrd.shm);
104 : :
105 : 0 : vbd->uuid = uuid;
106 : 0 : vbd->req_timeout = TD_VBD_REQUEST_TIMEOUT;
107 : 0 : vbd->watchdog_warned = false;
108 : :
109 : 0 : INIT_LIST_HEAD(&vbd->images);
110 : 0 : INIT_LIST_HEAD(&vbd->new_requests);
111 : 0 : INIT_LIST_HEAD(&vbd->pending_requests);
112 : 0 : INIT_LIST_HEAD(&vbd->failed_requests);
113 : 0 : INIT_LIST_HEAD(&vbd->completed_requests);
114 : 0 : INIT_LIST_HEAD(&vbd->next);
115 : 0 : INIT_LIST_HEAD(&vbd->rings);
116 : 0 : INIT_LIST_HEAD(&vbd->dead_rings);
117 : 0 : tapdisk_vbd_mark_progress(vbd);
118 : :
119 : 0 : return vbd;
120 : : }
121 : :
122 : : int
123 : 0 : tapdisk_vbd_initialize(int rfd, int wfd, uint16_t uuid)
124 : : {
125 : 0 : td_vbd_t *vbd;
126 : :
127 : 0 : vbd = tapdisk_server_get_vbd(uuid);
128 [ # # ]: 0 : if (vbd) {
129 : 0 : EPRINTF("duplicate vbds! %u\n", uuid);
130 : 0 : return -EEXIST;
131 : : }
132 : :
133 : 0 : vbd = tapdisk_vbd_create(uuid);
134 [ # # ]: 0 : if (!vbd) {
135 : 0 : EPRINTF("failed to create vbd\n");
136 : 0 : return -ENOMEM;
137 : : }
138 : :
139 : 0 : tapdisk_server_add_vbd(vbd);
140 : :
141 : 0 : return 0;
142 : : }
143 : :
144 : : static inline void
145 : : tapdisk_vbd_add_image(td_vbd_t *vbd, td_image_t *image)
146 : : {
147 : : list_add_tail(&image->next, &vbd->images);
148 : : }
149 : :
150 : : static inline int
151 : 0 : tapdisk_vbd_is_last_image(td_vbd_t *vbd, td_image_t *image)
152 : : {
153 : 0 : return list_is_last(&image->next, &vbd->images);
154 : : }
155 : :
156 : : static inline td_image_t *
157 : 1 : tapdisk_vbd_first_image(td_vbd_t *vbd)
158 : : {
159 : 1 : td_image_t *image = NULL;
160 : 1 : if (!list_empty(&vbd->images))
161 : 1 : image = list_entry(vbd->images.next, td_image_t, next);
162 : 1 : return image;
163 : : }
164 : :
165 : : static inline td_image_t *
166 : : tapdisk_vbd_last_image(td_vbd_t *vbd)
167 : : {
168 : : td_image_t *image = NULL;
169 : : if (!list_empty(&vbd->images))
170 : : image = list_entry(vbd->images.prev, td_image_t, next);
171 : : return image;
172 : : }
173 : :
174 : : static inline td_image_t *
175 : 0 : tapdisk_vbd_next_image(td_image_t *image)
176 : : {
177 : 0 : return list_entry(image->next.next, td_image_t, next);
178 : : }
179 : :
180 : : static int
181 : 0 : tapdisk_vbd_validate_chain(td_vbd_t *vbd)
182 : : {
183 : 0 : return tapdisk_image_validate_chain(&vbd->images);
184 : : }
185 : :
186 : : static int
187 : 0 : vbd_stats_destroy(td_vbd_t *vbd) {
188 : :
189 : 0 : int err = 0;
190 : :
191 [ # # ]: 0 : ASSERT(vbd);
192 : :
193 : 0 : err = shm_destroy(&vbd->rrd.shm);
194 [ # # ]: 0 : if (unlikely(err)) {
195 : 0 : EPRINTF("failed to destroy RRD file: %s\n", strerror(err));
196 : 0 : goto out;
197 : : }
198 : :
199 : 0 : free(vbd->rrd.shm.path);
200 : 0 : vbd->rrd.shm.path = NULL;
201 : :
202 : 0 : out:
203 : 0 : return -err;
204 : : }
205 : :
206 : : static int
207 : 0 : vbd_stats_create(td_vbd_t *vbd) {
208 : :
209 : 0 : int err;
210 : :
211 [ # # ]: 0 : ASSERT(vbd);
212 : :
213 : 0 : err = mkdir("/dev/shm/metrics", S_IRUSR | S_IWUSR);
214 [ # # ]: 0 : if (likely(err)) {
215 : 0 : err = errno;
216 [ # # ]: 0 : if (unlikely(err != EEXIST))
217 : 0 : goto out;
218 : : else
219 : 0 : err = 0;
220 : : }
221 : :
222 : : /*
223 : : * FIXME Rename this to something like "vbd3-domid-devid". Consider
224 : : * consolidating this with the io_ring shared memory file. Check if blkback
225 : : * exports the same information in some sysfs file and if so move this to
226 : : * the ring location.
227 : : */
228 : 0 : err = asprintf(&vbd->rrd.shm.path, "/dev/shm/metrics/tap-%d-%d", getpid(),
229 : 0 : vbd->uuid);
230 [ # # ]: 0 : if (err == -1) {
231 : 0 : err = errno;
232 : 0 : vbd->rrd.shm.path = NULL;
233 : 0 : EPRINTF("failed to create metric file: %s\n", strerror(err));
234 : 0 : goto out;
235 : : }
236 : 0 : err = 0;
237 : :
238 : 0 : vbd->rrd.shm.size = PAGE_SIZE;
239 : 0 : err = shm_create(&vbd->rrd.shm);
240 [ # # ]: 0 : if (err)
241 : 0 : EPRINTF("failed to create RRD: %s\n", strerror(err));
242 : :
243 : 0 : out:
244 [ # # ]: 0 : if (err) {
245 : 0 : int err2 = vbd_stats_destroy(vbd);
246 [ # # ]: 0 : if (err2)
247 : 0 : EPRINTF("failed to clean up failed RRD shared memory creation: "
248 : : "%s (error ignored)\n", strerror(-err2));
249 : : }
250 : 0 : return -err;
251 : : }
252 : :
253 : : void
254 : 0 : tapdisk_vbd_close_vdi(td_vbd_t *vbd)
255 : : {
256 : 0 : int err;
257 : :
258 : 0 : err = vbd_stats_destroy(vbd);
259 [ # # ]: 0 : if (err) {
260 : 0 : EPRINTF("failed to destroy RRD stats file: %s (error ignored)\n",
261 : : strerror(-err));
262 : : }
263 : :
264 : 0 : err = td_metrics_vdi_stop(&vbd->vdi_stats);
265 [ # # ]: 0 : if (err) {
266 : 0 : EPRINTF("failed to destroy stats file: %s\n", strerror(-err));
267 : : }
268 : :
269 : 0 : tapdisk_image_close_chain(&vbd->images);
270 : :
271 [ # # ]: 0 : if (vbd->secondary &&
272 [ # # ]: 0 : vbd->secondary_mode != TD_VBD_SECONDARY_MIRROR) {
273 : 0 : tapdisk_image_close(vbd->secondary);
274 : 0 : vbd->secondary = NULL;
275 : : }
276 : :
277 [ # # ]: 0 : if (vbd->retired) {
278 : 0 : tapdisk_image_close(vbd->retired);
279 : 0 : vbd->retired = NULL;
280 : : }
281 : :
282 : 0 : td_flag_set(vbd->state, TD_VBD_CLOSED);
283 : 0 : }
284 : :
285 : : static int
286 : 0 : tapdisk_vbd_add_block_cache(td_vbd_t *vbd)
287 : : {
288 : 0 : td_image_t *cache, *image, *target, *tmp;
289 : 0 : int err;
290 : :
291 : 0 : target = NULL;
292 : :
293 [ # # # # ]: 0 : tapdisk_vbd_for_each_image(vbd, image, tmp)
294 [ # # ]: 0 : if (td_flag_test(image->flags, TD_OPEN_RDONLY) &&
295 : : td_flag_test(image->flags, TD_OPEN_SHAREABLE)) {
296 : : target = image;
297 : : break;
298 : : }
299 : :
300 [ # # ]: 0 : if (!target)
301 : : return 0;
302 : :
303 : 0 : cache = tapdisk_image_allocate(target->name,
304 : : DISK_TYPE_BLOCK_CACHE,
305 : : target->flags);
306 [ # # ]: 0 : if (!cache)
307 : : return -ENOMEM;
308 : :
309 : : /* try to load existing cache */
310 : 0 : err = td_load(cache);
311 [ # # ]: 0 : if (!err)
312 : 0 : goto done;
313 : :
314 : : /* hack driver to send open() correct image size */
315 [ # # ]: 0 : if (!target->driver) {
316 : 0 : err = -ENODEV;
317 : 0 : goto fail;
318 : : }
319 : :
320 : 0 : cache->driver = tapdisk_driver_allocate(cache->type,
321 : 0 : cache->name,
322 : : cache->flags);
323 [ # # ]: 0 : if (!cache->driver) {
324 : 0 : err = -ENOMEM;
325 : 0 : goto fail;
326 : : }
327 : :
328 : 0 : cache->driver->info = target->driver->info;
329 : :
330 : : /* try to open new cache */
331 : 0 : err = td_open(cache, &vbd->encryption);
332 [ # # ]: 0 : if (!err)
333 : 0 : goto done;
334 : :
335 : 0 : fail:
336 : : /* give up */
337 : 0 : tapdisk_image_free(target);
338 : 0 : tapdisk_image_free(cache);
339 : 0 : return err;
340 : :
341 : 0 : done:
342 : : /* insert cache before image */
343 : 0 : list_add(&cache->next, target->next.prev);
344 : 0 : return 0;
345 : : }
346 : :
347 : : static int
348 : 0 : tapdisk_vbd_add_local_cache(td_vbd_t *vbd)
349 : : {
350 : 0 : td_image_t *cache, *parent;
351 : 0 : int err;
352 : :
353 [ # # ]: 0 : parent = tapdisk_vbd_first_image(vbd);
354 [ # # ]: 0 : if (tapdisk_vbd_is_last_image(vbd, parent)) {
355 : 0 : DPRINTF("Single-image chain, nothing to cache");
356 : 0 : return 0;
357 : : }
358 : :
359 : 0 : cache = tapdisk_image_allocate(parent->name,
360 : : DISK_TYPE_LCACHE,
361 : : parent->flags);
362 : :
363 [ # # ]: 0 : if (!cache)
364 : : return -ENOMEM;
365 : :
366 : : /* try to load existing cache */
367 : 0 : err = td_load(cache);
368 [ # # ]: 0 : if (!err)
369 : 0 : goto done;
370 : :
371 : 0 : cache->driver = tapdisk_driver_allocate(cache->type,
372 : 0 : cache->name,
373 : : cache->flags);
374 [ # # ]: 0 : if (!cache->driver) {
375 : 0 : err = -ENOMEM;
376 : 0 : goto fail;
377 : : }
378 : :
379 : 0 : cache->driver->info = parent->driver->info;
380 : :
381 : : /* try to open new cache */
382 : 0 : err = td_open(cache, &vbd->encryption);
383 [ # # ]: 0 : if (!err)
384 : 0 : goto done;
385 : :
386 : 0 : fail:
387 : 0 : tapdisk_image_free(cache);
388 : 0 : return err;
389 : :
390 : 0 : done:
391 : : /* insert cache right above leaf image */
392 : 0 : list_add(&cache->next, &parent->next);
393 : :
394 : 0 : DPRINTF("Added local_cache driver\n");
395 : 0 : return 0;
396 : : }
397 : :
398 : : int
399 : 0 : tapdisk_vbd_add_secondary(td_vbd_t *vbd)
400 : : {
401 : 0 : td_image_t *leaf, *second = NULL;
402 : 0 : const char *path;
403 : 0 : int type, err;
404 : :
405 [ # # ]: 0 : if (strcmp(vbd->secondary_name, "null") == 0) {
406 : 0 : DPRINTF("Removing secondary image\n");
407 : 0 : vbd->secondary_mode = TD_VBD_SECONDARY_DISABLED;
408 : 0 : vbd->secondary = NULL;
409 : 0 : vbd->nbd_mirror_failed = 0;
410 : 0 : return 0;
411 : : }
412 : :
413 : 0 : DPRINTF("Adding secondary image: %s\n", vbd->secondary_name);
414 : :
415 : 0 : type = tapdisk_disktype_parse_params(vbd->secondary_name, &path);
416 [ # # ]: 0 : if (type < 0)
417 : : return type;
418 : :
419 [ # # ]: 0 : leaf = tapdisk_vbd_first_image(vbd);
420 : 0 : if (!leaf) {
421 : 0 : err = -EINVAL;
422 : 0 : goto fail;
423 : : }
424 : :
425 : 0 : err = tapdisk_image_open(type, path, leaf->flags, &vbd->encryption, &second);
426 [ # # ]: 0 : if (err) {
427 [ # # ]: 0 : if (type == DISK_TYPE_NBD)
428 : 0 : vbd->nbd_mirror_failed = 1;
429 : :
430 : 0 : vbd->secondary=NULL;
431 : 0 : vbd->secondary_mode=TD_VBD_SECONDARY_DISABLED;
432 : :
433 : 0 : goto fail;
434 : : }
435 : :
436 [ # # ]: 0 : if (second->info.size != leaf->info.size) {
437 : 0 : EPRINTF("Secondary image size %"PRIu64" != image size %"PRIu64"\n",
438 : : second->info.size, leaf->info.size);
439 : 0 : err = -EINVAL;
440 : 0 : goto fail;
441 : : }
442 : :
443 : 0 : vbd->secondary = second;
444 : 0 : leaf->flags |= TD_IGNORE_ENOSPC;
445 [ # # ]: 0 : if (td_flag_test(vbd->flags, TD_OPEN_STANDBY)) {
446 : 0 : DPRINTF("In standby mode\n");
447 : 0 : vbd->secondary_mode = TD_VBD_SECONDARY_STANDBY;
448 : : } else {
449 : 0 : DPRINTF("In mirror mode\n");
450 : 0 : vbd->secondary_mode = TD_VBD_SECONDARY_MIRROR;
451 : : /*
452 : : * we actually need this image to also be part of the chain,
453 : : * since it may already contain data
454 : : */
455 : 0 : list_add(&second->next, &leaf->next);
456 : : }
457 : :
458 : 0 : DPRINTF("Added secondary image\n");
459 : : return 0;
460 : :
461 : 0 : fail:
462 [ # # ]: 0 : if (second)
463 : 0 : tapdisk_image_close(second);
464 : : return err;
465 : : }
466 : :
467 : 0 : static void signal_enospc(td_vbd_t *vbd)
468 : : {
469 : 0 : int fd, err;
470 : 0 : char *fn;
471 : :
472 : 0 : err = asprintf(&fn, BLKTAP2_ENOSPC_SIGNAL_FILE"%d", vbd->uuid);
473 [ # # ]: 0 : if (err == -1) {
474 : 0 : EPRINTF("Failed to signal ENOSPC condition\n");
475 : 0 : return;
476 : : }
477 : :
478 : 0 : fd = open(fn, O_WRONLY | O_CREAT | O_NONBLOCK, 0666);
479 [ # # ]: 0 : if (fd == -1)
480 : 0 : EPRINTF("Failed to open file to signal ENOSPC condition\n");
481 : : else
482 : 0 : close(fd);
483 : :
484 : 0 : free(fn);
485 : : }
486 : :
487 : : #if 0
488 : : static int
489 : : tapdisk_vbd_open_index(td_vbd_t *vbd)
490 : : {
491 : : int err;
492 : : char *path;
493 : : td_flag_t flags;
494 : : td_image_t *last, *image;
495 : :
496 : : last = tapdisk_vbd_last_image(vbd);
497 : : err = asprintf(&path, "%s.bat", last->name);
498 : : if (err == -1)
499 : : return -errno;
500 : :
501 : : err = access(path, R_OK);
502 : : if (err == -1) {
503 : : free(path);
504 : : return -errno;
505 : : }
506 : :
507 : : flags = vbd->flags | TD_OPEN_RDONLY | TD_OPEN_SHAREABLE;
508 : : image = tapdisk_image_allocate(path, DISK_TYPE_VINDEX, flags);
509 : : if (!image) {
510 : : err = -ENOMEM;
511 : : goto fail;
512 : : }
513 : :
514 : : err = td_open(image);
515 : : if (err)
516 : : goto fail;
517 : :
518 : : tapdisk_vbd_add_image(vbd, image);
519 : : return 0;
520 : :
521 : : fail:
522 : : if (image)
523 : : tapdisk_image_free(image);
524 : : free(path);
525 : : return err;
526 : : }
527 : : #endif
528 : :
529 : 0 : static int tapdisk_vbd_add_dirty_log(td_vbd_t *vbd)
530 : : {
531 : 0 : int err;
532 : 0 : td_driver_t *driver;
533 : 0 : td_image_t *log, *parent;
534 : :
535 : 0 : driver = NULL;
536 : 0 : log = NULL;
537 : :
538 : 0 : DPRINTF("CBT:tapdisk_vbd_add_dirty_log called for %s with log file %s\n",
539 : : vbd->name, vbd->logpath);
540 : :
541 [ # # ]: 0 : parent = tapdisk_vbd_first_image(vbd);
542 : :
543 : 0 : DPRINTF("CBT: Size in Image: %"PRIu64" sectors\n", parent->info.size);
544 : :
545 : 0 : log = tapdisk_image_allocate(vbd->logpath,
546 : : DISK_TYPE_LOG,
547 : : parent->flags);
548 [ # # ]: 0 : if (!log)
549 : : return -ENOMEM;
550 : :
551 : 0 : driver = tapdisk_driver_allocate(log->type,
552 : 0 : log->name,
553 : : log->flags);
554 [ # # ]: 0 : if (!driver) {
555 : 0 : err = -ENOMEM;
556 : 0 : goto fail;
557 : : }
558 : :
559 : 0 : driver->info = parent->driver->info;
560 : 0 : log->driver = driver;
561 : :
562 : 0 : err = td_open(log, &vbd->encryption);
563 [ # # ]: 0 : if (err)
564 : 0 : goto fail;
565 : :
566 : : /* insert log before image */
567 : 0 : list_add(&log->next, parent->next.prev);
568 : 0 : return 0;
569 : :
570 : 0 : fail:
571 : 0 : tapdisk_image_free(log);
572 : 0 : return err;
573 : : }
574 : :
575 : : int
576 : 0 : tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_devnum)
577 : : {
578 : 0 : char *tmp = vbd->name;
579 : 0 : int err;
580 : :
581 [ # # ]: 0 : if (!list_empty(&vbd->images)) {
582 : 0 : err = -EBUSY;
583 : 0 : goto fail;
584 : : }
585 : :
586 [ # # # # ]: 0 : if (!name && !vbd->name) {
587 : 0 : err = -EINVAL;
588 : 0 : goto fail;
589 : : }
590 : :
591 : : if (name) {
592 : 0 : vbd->name = strdup(name);
593 [ # # ]: 0 : if (!vbd->name) {
594 : 0 : err = -errno;
595 : 0 : goto fail;
596 : : }
597 : : }
598 : :
599 : 0 : err = tapdisk_image_open_chain(vbd->name, flags, prt_devnum, &vbd->encryption, &vbd->images);
600 [ # # ]: 0 : if (err)
601 : 0 : goto fail;
602 : :
603 : 0 : td_flag_clear(vbd->state, TD_VBD_CLOSED);
604 : 0 : vbd->flags = flags;
605 : :
606 [ # # ]: 0 : if (td_flag_test(vbd->flags, TD_OPEN_ADD_LOG)) {
607 [ # # ]: 0 : if (!vbd->logpath) {
608 : 0 : err = -EINVAL;
609 : 0 : goto fail;
610 : : }
611 : 0 : err = tapdisk_vbd_add_dirty_log(vbd);
612 [ # # ]: 0 : if (err)
613 : 0 : goto fail;
614 : : }
615 : :
616 [ # # ]: 0 : if (td_flag_test(vbd->flags, TD_OPEN_ADD_CACHE)) {
617 : 0 : err = tapdisk_vbd_add_block_cache(vbd);
618 [ # # ]: 0 : if (err)
619 : 0 : goto fail;
620 : : }
621 : :
622 : 0 : err = tapdisk_vbd_validate_chain(vbd);
623 [ # # ]: 0 : if (err)
624 : 0 : goto fail;
625 : :
626 [ # # ]: 0 : if (td_flag_test(vbd->flags, TD_OPEN_SECONDARY)) {
627 : 0 : err = tapdisk_vbd_add_secondary(vbd);
628 [ # # ]: 0 : if (err) {
629 [ # # ]: 0 : if (vbd->nbd_mirror_failed != 1)
630 : 0 : goto fail;
631 : 0 : INFO("Ignoring failed NBD secondary attach\n");
632 : 0 : err = 0;
633 : : }
634 : : }
635 : :
636 [ # # ]: 0 : if (td_flag_test(vbd->flags, TD_OPEN_LOCAL_CACHE)) {
637 : 0 : err = tapdisk_vbd_add_local_cache(vbd);
638 [ # # ]: 0 : if (err)
639 : 0 : goto fail;
640 : : }
641 : :
642 : 0 : err = vbd_stats_create(vbd);
643 [ # # ]: 0 : if (err)
644 : 0 : goto fail;
645 : :
646 : 0 : err = td_metrics_vdi_start(vbd->uuid, &vbd->vdi_stats);
647 [ # # ]: 0 : if (err)
648 : 0 : goto fail;
649 [ # # ]: 0 : if (tmp != vbd->name)
650 : 0 : free(tmp);
651 : :
652 : : return err;
653 : :
654 : 0 : fail:
655 [ # # ]: 0 : if (vbd->name != tmp) {
656 : 0 : free(vbd->name);
657 : 0 : vbd->name = tmp;
658 : : }
659 : :
660 [ # # ]: 0 : if (!list_empty(&vbd->images))
661 : 0 : tapdisk_image_close_chain(&vbd->images);
662 : :
663 : 0 : vbd->flags = 0;
664 : :
665 : 0 : return err;
666 : : }
667 : :
668 : : static int
669 : 0 : open_vbd_marker(int id)
670 : : {
671 : 0 : char *path = NULL;
672 : 0 : int err, fid;
673 : :
674 : 0 : err = asprintf(&path, "%s/tapdisk-%d", BLKTAP2_NP_RUN_DIR, id);
675 [ # # ]: 0 : if (err == -1) {
676 : 0 : return -errno;
677 : : }
678 : :
679 : 0 : fid = open(path, O_RDONLY, 0600);
680 [ # # ]: 0 : if (fid == -1) {
681 : 0 : err = -errno;
682 : 0 : EPRINTF("Failed to open VBD marker file for %d\n", id);
683 : 0 : goto out;
684 : : }
685 : :
686 : 0 : free(path);
687 : :
688 : 0 : return fid;
689 : :
690 : 0 : out:
691 [ # # ]: 0 : if (path) {
692 : 0 : free(path);
693 : : }
694 : : return err;
695 : : }
696 : :
697 : 0 : void tapdisk_vbd_unlock(td_vbd_t *vbd)
698 : : {
699 : 0 : flock(vbd->lock_fd, LOCK_UN);
700 : 0 : close(vbd->lock_fd);
701 : 0 : }
702 : :
703 : 0 : int tapdisk_vbd_lock(td_vbd_t *vbd)
704 : : {
705 : 0 : int fid;
706 : :
707 : 0 : fid = open_vbd_marker(vbd->uuid);
708 [ # # ]: 0 : if (fid < 0) {
709 : : /* Already logged */
710 : : return -1;
711 : : }
712 : :
713 : 0 : vbd->lock_fd = fid;
714 : 0 : flock(vbd->lock_fd, LOCK_EX);
715 : :
716 : 0 : return 0;
717 : : }
718 : :
719 : : /*
720 : : int
721 : : tapdisk_vbd_open(td_vbd_t *vbd, const char *name,
722 : : int minor, const char *ring, td_flag_t flags)
723 : : {
724 : : int err;
725 : :
726 : : err = tapdisk_vbd_open_vdi(vbd, name, flags, -1);
727 : : if (err)
728 : : goto out;
729 : :
730 : : err = tapdisk_vbd_attach(vbd, ring, minor);
731 : : if (err)
732 : : goto out;
733 : :
734 : : return 0;
735 : :
736 : : out:
737 : : tapdisk_vbd_detach(vbd);
738 : : tapdisk_vbd_close_vdi(vbd);
739 : : free(vbd->name);
740 : : vbd->name = NULL;
741 : : return err;
742 : : }
743 : : */
744 : :
745 : : static void
746 : 0 : tapdisk_vbd_queue_count(td_vbd_t *vbd, int *new,
747 : : int *pending, int *failed, int *completed)
748 : : {
749 : 0 : int n, p, f, c;
750 : 0 : td_vbd_request_t *vreq, *tvreq;
751 : :
752 : 0 : n = 0;
753 : 0 : p = 0;
754 : 0 : f = 0;
755 : 0 : c = 0;
756 : :
757 [ # # ]: 0 : tapdisk_vbd_for_each_request(vreq, tvreq, &vbd->new_requests)
758 : 0 : n++;
759 : :
760 [ # # ]: 0 : tapdisk_vbd_for_each_request(vreq, tvreq, &vbd->pending_requests)
761 : 0 : p++;
762 : :
763 [ # # ]: 0 : tapdisk_vbd_for_each_request(vreq, tvreq, &vbd->failed_requests)
764 : 0 : f++;
765 : :
766 [ # # ]: 0 : tapdisk_vbd_for_each_request(vreq, tvreq, &vbd->completed_requests)
767 : 0 : c++;
768 : :
769 : 0 : *new = n;
770 : 0 : *pending = p;
771 : 0 : *failed = f;
772 : 0 : *completed = c;
773 : 0 : }
774 : :
775 : : static int
776 : 0 : tapdisk_vbd_shutdown(td_vbd_t *vbd)
777 : : {
778 : 0 : int new, pending, failed, completed;
779 : :
780 [ # # ]: 0 : if (!list_empty(&vbd->pending_requests))
781 : 0 : return -EAGAIN;
782 : :
783 : 0 : tapdisk_vbd_queue_count(vbd, &new, &pending, &failed, &completed);
784 : :
785 : 0 : DPRINTF("%s: state: 0x%08x, new: 0x%02x, pending: 0x%02x, "
786 : : "failed: 0x%02x, completed: 0x%02x\n",
787 : : vbd->name, vbd->state, new, pending, failed, completed);
788 : 0 : DPRINTF("last activity: %010ld.%06ld, errors: 0x%04"PRIx64", "
789 : : "retries: 0x%04"PRIx64", received: 0x%08"PRIx64", "
790 : : "returned: 0x%08"PRIx64", kicked: 0x%08"PRIx64"\n",
791 : : vbd->ts.tv_sec, vbd->ts.tv_usec,
792 : : vbd->errors, vbd->retries, vbd->received, vbd->returned,
793 : : vbd->kicked);
794 : :
795 : 0 : tapdisk_vbd_close_vdi(vbd);
796 : 0 : tapdisk_vbd_unlock(vbd);
797 : 0 : tapdisk_server_remove_vbd(vbd);
798 : 0 : tapdisk_vbd_free(vbd);
799 : :
800 : 0 : return 0;
801 : : }
802 : :
803 : : void
804 : 0 : tapdisk_vbd_free(td_vbd_t *vbd)
805 : : {
806 : 0 : free(vbd->name);
807 : 0 : free(vbd->encryption.encryption_key);
808 : 0 : free(vbd);
809 : 0 : }
810 : :
811 : : int
812 : 0 : tapdisk_vbd_close(td_vbd_t *vbd)
813 : : {
814 : : /*
815 : : * don't close if any requests are pending in the aio layer
816 : : */
817 [ # # ]: 0 : if (!list_empty(&vbd->pending_requests))
818 : 0 : goto fail;
819 : :
820 : : /*
821 : : * if the queue is still active and we have more
822 : : * requests, try to complete them before closing.
823 : : */
824 [ # # ]: 0 : if (tapdisk_vbd_queue_ready(vbd) &&
825 [ # # # # ]: 0 : (!list_empty(&vbd->new_requests) ||
826 [ # # # # ]: 0 : !list_empty(&vbd->failed_requests) ||
827 [ # # ]: 0 : !list_empty(&vbd->completed_requests)))
828 : 0 : goto fail;
829 : :
830 : 0 : return tapdisk_vbd_shutdown(vbd);
831 : :
832 : 0 : fail:
833 : 0 : td_flag_set(vbd->state, TD_VBD_SHUTDOWN_REQUESTED);
834 : 0 : DBG(TLOG_WARN, "%s: requests pending\n", vbd->name);
835 : 0 : return -EAGAIN;
836 : : }
837 : :
838 : : /*
839 : : * control operations
840 : : */
841 : :
842 : : void
843 : 0 : tapdisk_vbd_debug(td_vbd_t *vbd)
844 : : {
845 : 0 : td_image_t *image, *tmp;
846 : 0 : int new, pending, failed, completed;
847 : :
848 : 0 : tapdisk_vbd_queue_count(vbd, &new, &pending, &failed, &completed);
849 : :
850 : 0 : DBG(TLOG_WARN, "%s: state: 0x%08x, new: 0x%02x, pending: 0x%02x, "
851 : : "failed: 0x%02x, completed: 0x%02x, last activity: %010ld.%06ld, "
852 : : "errors: 0x%04"PRIx64", retries: 0x%04"PRIx64", "
853 : : "received: 0x%08"PRIx64", returned: 0x%08"PRIx64", "
854 : : "kicked: 0x%08"PRIx64"\n",
855 : : vbd->name, vbd->state, new, pending, failed, completed,
856 : : vbd->ts.tv_sec, vbd->ts.tv_usec, vbd->errors, vbd->retries,
857 : : vbd->received, vbd->returned, vbd->kicked);
858 : :
859 [ # # ]: 0 : tapdisk_vbd_for_each_image(vbd, image, tmp)
860 : 0 : td_debug(image);
861 : 0 : }
862 : :
863 : : static void
864 : 0 : tapdisk_vbd_drop_log(td_vbd_t *vbd)
865 : : {
866 [ # # ]: 0 : if (td_flag_test(vbd->state, TD_VBD_LOG_DROPPED))
867 : : return;
868 : :
869 : 0 : tapdisk_vbd_debug(vbd);
870 : 0 : tlog_precious(0);
871 : 0 : td_flag_set(vbd->state, TD_VBD_LOG_DROPPED);
872 : : }
873 : :
874 : : int
875 : 0 : tapdisk_vbd_get_disk_info(td_vbd_t *vbd, td_disk_info_t *info)
876 : : {
877 [ # # ]: 0 : if (list_empty(&vbd->images))
878 : : return -EINVAL;
879 : :
880 : 0 : *info = tapdisk_vbd_first_image(vbd)->info;
881 : 0 : return 0;
882 : : }
883 : :
884 : : static int
885 : 1 : tapdisk_vbd_queue_ready(td_vbd_t *vbd)
886 : : {
887 : 1 : return (!td_flag_test(vbd->state, TD_VBD_DEAD) &&
888 : : !td_flag_test(vbd->state, TD_VBD_CLOSED) &&
889 [ # # ]: 0 : !td_flag_test(vbd->state, TD_VBD_QUIESCED) &&
890 : : !td_flag_test(vbd->state, TD_VBD_QUIESCE_REQUESTED));
891 : : }
892 : :
893 : : int
894 : 0 : tapdisk_vbd_retry_needed(td_vbd_t *vbd)
895 : : {
896 [ # # # # ]: 0 : return !(list_empty(&vbd->failed_requests) &&
897 [ # # ]: 0 : list_empty(&vbd->new_requests));
898 : : }
899 : :
900 : : int
901 : 0 : tapdisk_vbd_quiesce_queue(td_vbd_t *vbd)
902 : : {
903 [ # # ]: 0 : if (!list_empty(&vbd->pending_requests)) {
904 : 0 : td_flag_set(vbd->state, TD_VBD_QUIESCE_REQUESTED);
905 : 0 : return -EAGAIN;
906 : : }
907 : :
908 : 0 : td_flag_clear(vbd->state, TD_VBD_QUIESCE_REQUESTED);
909 : 0 : td_flag_set(vbd->state, TD_VBD_QUIESCED);
910 : 0 : return 0;
911 : : }
912 : :
913 : : int
914 : 0 : tapdisk_vbd_start_queue(td_vbd_t *vbd)
915 : : {
916 : 0 : td_flag_clear(vbd->state, TD_VBD_QUIESCED);
917 : 0 : td_flag_clear(vbd->state, TD_VBD_QUIESCE_REQUESTED);
918 : 0 : tapdisk_vbd_mark_progress(vbd);
919 : 0 : return 0;
920 : : }
921 : :
922 : : int
923 : 0 : tapdisk_vbd_kill_queue(td_vbd_t *vbd)
924 : : {
925 [ # # ]: 0 : tapdisk_vbd_quiesce_queue(vbd);
926 : 0 : td_flag_set(vbd->state, TD_VBD_DEAD);
927 : 0 : return 0;
928 : : }
929 : :
930 : : #if 0
931 : : static int
932 : : tapdisk_vbd_open_image(td_vbd_t *vbd, td_image_t *image)
933 : : {
934 : : int err;
935 : : td_image_t *parent;
936 : :
937 : : err = td_open(image);
938 : : if (err)
939 : : return err;
940 : :
941 : : if (!tapdisk_vbd_is_last_image(vbd, image)) {
942 : : parent = tapdisk_vbd_next_image(image);
943 : : err = td_validate_parent(image, parent);
944 : : if (err) {
945 : : td_close(image);
946 : : return err;
947 : : }
948 : : }
949 : :
950 : : return 0;
951 : : }
952 : : #endif
953 : :
954 : : /*
955 : : * Pausing a tapdisk can produce a lot of logging if the storage is not available
956 : : * and there are inflight data requests. All the caller to squash the logging
957 : : * when entering a retry process.
958 : : */
959 : 0 : void tapdisk_vbd_squash_pause_logging(bool squash)
960 : : {
961 : 0 : log = !squash;
962 : 0 : }
963 : :
964 : : int
965 : 0 : tapdisk_vbd_pause(td_vbd_t *vbd)
966 : : {
967 : 0 : int err;
968 : 0 : struct td_xenblkif *blkif;
969 : :
970 [ # # ]: 0 : if (log) {
971 : 0 : INFO("pause requested\n");
972 : : }
973 : :
974 : 0 : td_flag_set(vbd->state, TD_VBD_PAUSE_REQUESTED);
975 : :
976 [ # # ]: 0 : if (vbd->nbdserver)
977 : 0 : tapdisk_nbdserver_pause(vbd->nbdserver, log);
978 [ # # ]: 0 : if (vbd->nbdserver_new)
979 : 0 : tapdisk_nbdserver_pause(vbd->nbdserver_new, log);
980 : :
981 [ # # ]: 0 : list_for_each_entry(blkif, &vbd->rings, entry)
982 : 0 : tapdisk_xenblkif_suspend(blkif);
983 : :
984 [ # # ]: 0 : err = tapdisk_vbd_quiesce_queue(vbd);
985 [ # # ]: 0 : if (err)
986 : 0 : return err;
987 : :
988 : :
989 : 0 : tapdisk_vbd_close_vdi(vbd);
990 : :
991 : : /* Don't guard this one as at this point the pause operation is complete */
992 : 0 : INFO("pause completed\n");
993 : :
994 [ # # ]: 0 : if (!list_empty(&vbd->failed_requests))
995 : 0 : INFO("warning: failed requests pending\n");
996 : :
997 : 0 : td_flag_clear(vbd->state, TD_VBD_PAUSE_REQUESTED);
998 : 0 : td_flag_set(vbd->state, TD_VBD_PAUSED);
999 : :
1000 : 0 : return 0;
1001 : : }
1002 : :
1003 : : int
1004 : 0 : tapdisk_vbd_resume(td_vbd_t *vbd, const char *name)
1005 : : {
1006 : 0 : int i, err;
1007 : 0 : struct td_xenblkif *blkif;
1008 : :
1009 : 0 : DBG(TLOG_DBG, "resume requested\n");
1010 : :
1011 [ # # ]: 0 : if (!td_flag_test(vbd->state, TD_VBD_PAUSED)) {
1012 : 0 : EPRINTF("resume request for unpaused vbd %s\n", vbd->name);
1013 : 0 : return -EINVAL;
1014 : : }
1015 : :
1016 [ # # ]: 0 : for (i = 0; i < TD_VBD_EIO_RETRIES; i++) {
1017 : 0 : err = tapdisk_vbd_open_vdi(vbd, name, vbd->flags | TD_OPEN_STRICT, -1);
1018 [ # # ]: 0 : if (!err)
1019 : : break;
1020 : :
1021 : 0 : sleep(TD_VBD_EIO_SLEEP);
1022 : : }
1023 : :
1024 [ # # ]: 0 : if (!err) {
1025 : 0 : td_disk_info_t disk_info;
1026 [ # # ]: 0 : err = tapdisk_vbd_get_disk_info(vbd, &disk_info);
1027 [ # # ]: 0 : if (err) {
1028 : 0 : EPRINTF("VBD %d failed to get disk info: %s\n", vbd->uuid,
1029 : : strerror(-err));
1030 : 0 : goto resume_failed;
1031 : : }
1032 [ # # ]: 0 : if (vbd->disk_info.size != disk_info.size
1033 [ # # ]: 0 : || vbd->disk_info.sector_size != disk_info.sector_size
1034 [ # # ]: 0 : || vbd->disk_info.info != disk_info.info) {
1035 : 0 : EPRINTF("VBD %d cannot change disk info\n", vbd->uuid);
1036 : 0 : err = -EMEDIUMTYPE;
1037 : 0 : goto resume_failed;
1038 : : }
1039 : : }
1040 : 0 : resume_failed:
1041 [ # # ]: 0 : if (err) {
1042 : 0 : td_flag_set(vbd->state, TD_VBD_RESUME_FAILED);
1043 : 0 : tapdisk_vbd_close_vdi(vbd);
1044 : 0 : return err;
1045 : : }
1046 : 0 : td_flag_clear(vbd->state, TD_VBD_RESUME_FAILED);
1047 : :
1048 : 0 : DBG(TLOG_DBG, "resume completed\n");
1049 : :
1050 : 0 : tapdisk_vbd_start_queue(vbd);
1051 : 0 : td_flag_clear(vbd->state, TD_VBD_PAUSED);
1052 : 0 : td_flag_clear(vbd->state, TD_VBD_PAUSE_REQUESTED);
1053 : 0 : tapdisk_vbd_check_state(vbd);
1054 : :
1055 [ # # ]: 0 : if (vbd->nbdserver)
1056 : 0 : tapdisk_nbdserver_unpause(vbd->nbdserver);
1057 [ # # ]: 0 : if (vbd->nbdserver_new)
1058 : 0 : tapdisk_nbdserver_unpause(vbd->nbdserver_new);
1059 : :
1060 [ # # ]: 0 : list_for_each_entry(blkif, &vbd->rings, entry)
1061 : 0 : tapdisk_xenblkif_resume(blkif);
1062 : :
1063 : :
1064 : 0 : DBG(TLOG_DBG, "state checked\n");
1065 : :
1066 : 0 : return 0;
1067 : : }
1068 : :
1069 : : static int
1070 : 0 : tapdisk_vbd_request_ttl(td_vbd_request_t *vreq,
1071 : : const struct timeval *now)
1072 : : {
1073 : 0 : struct timeval delta;
1074 : 0 : timersub(now, &vreq->ts, &delta);
1075 : 0 : return vreq->vbd->req_timeout - delta.tv_sec;
1076 : : }
1077 : :
1078 : : static int
1079 : 0 : __tapdisk_vbd_request_timeout(td_vbd_request_t *vreq,
1080 : : const struct timeval *now)
1081 : : {
1082 : 0 : int timeout;
1083 : :
1084 [ # # ]: 0 : timeout = tapdisk_vbd_request_ttl(vreq, now) < 0;
1085 [ # # ]: 0 : if (timeout)
1086 : 0 : ERR(vreq->error,
1087 : : "req %s timed out, retried %d times\n",
1088 : : vreq->name, vreq->num_retries);
1089 : :
1090 : 0 : return timeout;
1091 : : }
1092 : :
1093 : : static int
1094 : 0 : tapdisk_vbd_request_timeout(td_vbd_request_t *vreq)
1095 : : {
1096 : 0 : struct timeval now;
1097 : 0 : gettimeofday(&now, NULL);
1098 : 0 : return __tapdisk_vbd_request_timeout(vreq, &now);
1099 : : }
1100 : :
1101 : : static void
1102 : 0 : tapdisk_vbd_check_complete_requests(td_vbd_t *vbd)
1103 : : {
1104 : 0 : td_vbd_request_t *vreq, *tmp;
1105 : 0 : struct timeval now;
1106 : :
1107 : 0 : gettimeofday(&now, NULL);
1108 [ # # ]: 0 : tapdisk_vbd_for_each_request(vreq, tmp, &vbd->failed_requests)
1109 [ # # ]: 0 : if (__tapdisk_vbd_request_timeout(vreq, &now))
1110 : 0 : tapdisk_vbd_complete_vbd_request(vbd, vreq);
1111 : 0 : }
1112 : :
1113 : : static void
1114 : 0 : tapdisk_vbd_check_requests_for_issue(td_vbd_t *vbd)
1115 : : {
1116 [ # # # # ]: 0 : if (!list_empty(&vbd->new_requests) ||
1117 [ # # ]: 0 : !list_empty(&vbd->failed_requests))
1118 : 0 : tapdisk_vbd_issue_requests(vbd);
1119 : 0 : }
1120 : :
1121 : : void
1122 : 0 : tapdisk_vbd_check_state(td_vbd_t *vbd)
1123 : : {
1124 : 0 : struct td_xenblkif *blkif;
1125 : :
1126 : : /* Don't check if we're already quiesced */
1127 [ # # ]: 0 : if (td_flag_test(vbd->state, TD_VBD_QUIESCED))
1128 : : return;
1129 : :
1130 : : /*
1131 : : * TODO don't ignore return value
1132 : : */
1133 [ # # ]: 0 : list_for_each_entry(blkif, &vbd->rings, entry)
1134 : 0 : tapdisk_xenblkif_ring_stats_update(blkif);
1135 : :
1136 : 0 : tapdisk_vbd_check_complete_requests(vbd);
1137 : :
1138 [ # # ]: 0 : if (!td_flag_test(vbd->state, TD_VBD_QUIESCE_REQUESTED) &&
1139 : : !td_flag_test(vbd->state, TD_VBD_PAUSE_REQUESTED))
1140 : 0 : tapdisk_vbd_check_requests_for_issue(vbd);
1141 : :
1142 [ # # ]: 0 : if (td_flag_test(vbd->state, TD_VBD_QUIESCE_REQUESTED))
1143 [ # # ]: 0 : tapdisk_vbd_quiesce_queue(vbd);
1144 : :
1145 [ # # ]: 0 : if (td_flag_test(vbd->state, TD_VBD_PAUSE_REQUESTED))
1146 : 0 : tapdisk_vbd_pause(vbd);
1147 : :
1148 [ # # ]: 0 : if (td_flag_test(vbd->state, TD_VBD_SHUTDOWN_REQUESTED))
1149 : 0 : tapdisk_vbd_close(vbd);
1150 : : }
1151 : :
1152 : 0 : void watchdog_cleared(td_vbd_t *vbd)
1153 : : {
1154 [ # # ]: 0 : if (vbd->watchdog_warned) {
1155 : 0 : DBG(TLOG_WARN, "%s: watchdog timeout: requests were blocked\n", vbd->name);
1156 : : /* Ideally want a direct way to flush the log */
1157 : 0 : tlog_precious(1);
1158 : : }
1159 : 0 : vbd->watchdog_warned = false;
1160 : 0 : }
1161 : :
1162 : : void
1163 : 0 : tapdisk_vbd_check_progress(td_vbd_t *vbd)
1164 : : {
1165 : 0 : time_t diff;
1166 : 0 : struct timeval now, delta;
1167 : :
1168 [ # # ]: 0 : if (list_empty(&vbd->pending_requests)) {
1169 : 0 : watchdog_cleared(vbd);
1170 : 0 : return;
1171 : : }
1172 : :
1173 : 0 : gettimeofday(&now, NULL);
1174 [ # # ]: 0 : timersub(&now, &vbd->ts, &delta);
1175 : 0 : diff = delta.tv_sec;
1176 : :
1177 [ # # ]: 0 : if (diff >= TD_VBD_WATCHDOG_TIMEOUT)
1178 : : {
1179 [ # # ]: 0 : if(tapdisk_vbd_queue_ready(vbd))
1180 : : {
1181 [ # # ]: 0 : if (!vbd->watchdog_warned) {
1182 : 0 : DBG(TLOG_WARN, "%s: watchdog timeout: pending requests "
1183 : : "idle for %ld seconds\n", vbd->name, diff);
1184 : 0 : vbd->watchdog_warned = true;
1185 : : }
1186 : 0 : tapdisk_vbd_drop_log(vbd);
1187 : : }
1188 : 0 : return;
1189 : : }
1190 : :
1191 : 0 : watchdog_cleared(vbd);
1192 : :
1193 : 0 : tapdisk_server_set_max_timeout(TD_VBD_WATCHDOG_TIMEOUT - diff);
1194 : : }
1195 : :
1196 : : /*
1197 : : * request submission
1198 : : */
1199 : :
1200 : : static int
1201 : 1 : tapdisk_vbd_check_queue(td_vbd_t *vbd)
1202 : : {
1203 : 1 : if (list_empty(&vbd->images))
1204 : : return -ENOSYS;
1205 : :
1206 [ + - ]: 1 : if (!tapdisk_vbd_queue_ready(vbd))
1207 : : return -EAGAIN;
1208 : :
1209 : : return 0;
1210 : : }
1211 : :
1212 : : static int
1213 : 0 : tapdisk_vbd_request_should_retry(td_vbd_t *vbd, td_vbd_request_t *vreq)
1214 : : {
1215 [ # # ]: 0 : if (td_flag_test(vbd->state, TD_VBD_DEAD) ||
1216 : : td_flag_test(vbd->state, TD_VBD_SHUTDOWN_REQUESTED))
1217 : : return 0;
1218 : :
1219 [ # # ]: 0 : if (tapdisk_vbd_request_timeout(vreq))
1220 : : return 0;
1221 : :
1222 [ # # ]: 0 : switch (abs(vreq->error)) {
1223 : : case EAGAIN:
1224 : : case EBUSY:
1225 : : case EINTR:
1226 : : return 1;
1227 : : }
1228 : :
1229 : : return 0;
1230 : : }
1231 : :
1232 : : static void
1233 : 1 : tapdisk_vbd_complete_vbd_request(td_vbd_t *vbd, td_vbd_request_t *vreq)
1234 : : {
1235 [ - + ]: 1 : if (!vreq->submitting && !vreq->secs_pending) {
1236 [ # # # # ]: 0 : if (vreq->error &&
1237 : 0 : tapdisk_vbd_request_should_retry(vbd, vreq))
1238 : 0 : tapdisk_vbd_move_request(vreq, &vbd->failed_requests);
1239 : : else
1240 : 0 : tapdisk_vbd_move_request(vreq, &vbd->completed_requests);
1241 : : }
1242 : 1 : }
1243 : :
1244 : : static void
1245 : 1 : FIXME_maybe_count_enospc_redirect(td_vbd_t *vbd, td_request_t treq)
1246 : : {
1247 : 1 : int write = treq.op == TD_OP_WRITE;
1248 : 1 : if (write &&
1249 [ # # # # ]: 0 : treq.image == tapdisk_vbd_first_image(vbd) &&
1250 [ # # ]: 0 : vbd->FIXME_enospc_redirect_count_enabled)
1251 : 0 : vbd->FIXME_enospc_redirect_count += treq.secs;
1252 : : }
1253 : :
1254 : : static void
1255 : 1 : __tapdisk_vbd_complete_td_request(td_vbd_t *vbd, td_vbd_request_t *vreq,
1256 : : td_request_t treq, int res)
1257 : : {
1258 : 1 : td_image_t *image = treq.image;
1259 : 1 : int err;
1260 : :
1261 : 1 : long long interval;
1262 : :
1263 : 1 : err = (res <= 0 ? res : -res);
1264 : 1 : vbd->secs_pending -= treq.secs;
1265 : 1 : vreq->secs_pending -= treq.secs;
1266 : :
1267 [ + - ]: 1 : if (err != -EBUSY) {
1268 : 1 : int write = treq.op == TD_OP_WRITE;
1269 [ - + ]: 1 : td_sector_count_add(&image->stats.hits, treq.secs, write);
1270 [ - + ]: 1 : if (err)
1271 [ # # ]: 0 : td_sector_count_add(&image->stats.fail,
1272 : : treq.secs, write);
1273 : :
1274 [ - + ]: 1 : FIXME_maybe_count_enospc_redirect(vbd, treq);
1275 : : }
1276 : :
1277 [ - + ]: 1 : if (err) {
1278 [ # # ]: 0 : if (err != -EBUSY) {
1279 [ # # ]: 0 : if (!vreq->error &&
1280 [ # # ]: 0 : err != vreq->prev_error)
1281 [ # # ]: 0 : tlog_drv_error(image->driver, err,
1282 : : "req %s: %s %s 0x%04x secs @ 0x%08"PRIx64" - %s",
1283 : : vreq->name,
1284 : : op_strings[treq.op],
1285 : : image->name,
1286 : : treq.secs, treq.sec, strerror(abs(err)));
1287 : 0 : vbd->errors++;
1288 : : }
1289 [ # # ]: 0 : vreq->error = (vreq->error ? : err);
1290 : : }
1291 : :
1292 [ - + ]: 1 : interval = timeval_to_us(&vbd->ts) - timeval_to_us(&vreq->ts);
1293 : :
1294 [ - + ]: 1 : if(treq.op == TD_OP_READ) {
1295 : 0 : vbd->vdi_stats.stats->read_reqs_completed++;
1296 : 0 : vbd->vdi_stats.stats->read_sectors += treq.secs;
1297 : 0 : vbd->vdi_stats.stats->read_total_ticks += interval;
1298 : : }
1299 : :
1300 [ - + ]: 1 : if(treq.op == TD_OP_WRITE) {
1301 : 0 : vbd->vdi_stats.stats->write_reqs_completed++;
1302 : 0 : vbd->vdi_stats.stats->write_sectors += treq.secs;
1303 : 0 : vbd->vdi_stats.stats->write_total_ticks += interval;
1304 : : }
1305 : :
1306 : 1 : tapdisk_vbd_complete_vbd_request(vbd, vreq);
1307 : 1 : }
1308 : :
1309 : : static void
1310 : 0 : __tapdisk_vbd_reissue_td_request(td_vbd_t *vbd,
1311 : : td_image_t *image, td_request_t treq)
1312 : : {
1313 : 0 : td_image_t *parent;
1314 : 0 : td_vbd_request_t *vreq;
1315 : :
1316 : 0 : vreq = treq.vreq;
1317 : 0 : gettimeofday(&vreq->last_try, NULL);
1318 : :
1319 : 0 : vreq->submitting++;
1320 : :
1321 [ # # ]: 0 : if (tapdisk_vbd_is_last_image(vbd, image)) {
1322 [ # # ]: 0 : if (unlikely(treq.op == TD_OP_BLOCK_STATUS)) {
1323 : 0 : treq.status = TD_BLOCK_STATE_HOLE;
1324 : : } else {
1325 : 0 : memset(treq.buf, 0, (size_t)treq.secs << SECTOR_SHIFT);
1326 : : }
1327 : 0 : td_complete_request(treq, 0);
1328 : 0 : goto done;
1329 : : }
1330 : : /*
1331 : : * If intellicache is enabled, check to confirm mirroring
1332 : : * is disabled due to out of space.
1333 : : */
1334 [ # # # # ]: 0 : if (unlikely(vbd->retired && vbd->retired == image))
1335 [ # # ]: 0 : parent = tapdisk_vbd_first_image(vbd);
1336 : : else
1337 : 0 : parent = tapdisk_vbd_next_image(image);
1338 : :
1339 : 0 : treq.image = parent;
1340 : :
1341 : : /* return zeros for requests that extend beyond end of parent image */
1342 [ # # ]: 0 : if (treq.sec + treq.secs > parent->info.size) {
1343 : 0 : td_request_t clone = treq;
1344 : :
1345 [ # # ]: 0 : if (parent->info.size > treq.sec) {
1346 : 0 : int secs = parent->info.size - treq.sec;
1347 : 0 : clone.sec += secs;
1348 : 0 : clone.secs -= secs;
1349 : 0 : clone.buf += (secs << SECTOR_SHIFT);
1350 : 0 : treq.secs = secs;
1351 : : } else
1352 : 0 : treq.secs = 0;
1353 : :
1354 : 0 : memset(clone.buf, 0, (size_t)clone.secs << SECTOR_SHIFT);
1355 : 0 : td_complete_request(clone, 0);
1356 : :
1357 [ # # ]: 0 : if (!treq.secs)
1358 : 0 : goto done;
1359 : : }
1360 : :
1361 [ # # # # ]: 0 : switch (treq.op) {
1362 : 0 : case TD_OP_WRITE:
1363 : 0 : td_queue_write(parent, treq);
1364 : 0 : break;
1365 : :
1366 : 0 : case TD_OP_READ:
1367 : 0 : td_queue_read(parent, treq);
1368 : 0 : break;
1369 : 0 : case TD_OP_BLOCK_STATUS:
1370 : 0 : td_queue_block_status(parent, &treq);
1371 : 0 : break;
1372 : : }
1373 : :
1374 : 0 : done:
1375 : 0 : vreq->submitting--;
1376 [ # # ]: 0 : if (!vreq->secs_pending)
1377 : 0 : tapdisk_vbd_complete_vbd_request(vbd, vreq);
1378 : 0 : }
1379 : :
1380 : : void
1381 : 0 : tapdisk_vbd_forward_request(td_request_t treq)
1382 : : {
1383 : 0 : td_vbd_t *vbd;
1384 : 0 : td_image_t *image;
1385 : 0 : td_vbd_request_t *vreq;
1386 : :
1387 : 0 : image = treq.image;
1388 : 0 : vreq = treq.vreq;
1389 : 0 : vbd = vreq->vbd;
1390 : :
1391 : 0 : tapdisk_vbd_mark_progress(vbd);
1392 : :
1393 [ # # ]: 0 : if (tapdisk_vbd_queue_ready(vbd))
1394 : 0 : __tapdisk_vbd_reissue_td_request(vbd, image, treq);
1395 : : else
1396 : 0 : td_complete_request(treq, -EBUSY);
1397 : 0 : }
1398 : :
1399 : : int
1400 : 2 : add_extent(tapdisk_extents_t *extents, td_request_t *vreq)
1401 : : {
1402 : 2 : tapdisk_extent_t *extent;
1403 : 2 : extent = (tapdisk_extent_t*)malloc(sizeof(*extent));
1404 [ + - ]: 2 : if(extent == NULL)
1405 : : return -1;
1406 : :
1407 : 2 : extent->start = vreq->sec;
1408 : 2 : extent->length = vreq->secs;
1409 : 2 : extent->flag = vreq->status;
1410 : 2 : extent->next = NULL;
1411 : :
1412 [ - + - - ]: 2 : if(extents->head != NULL && extents->tail != NULL) {
1413 : 0 : extents->tail->next = extent;
1414 : 0 : extents->tail = extent;
1415 : : } else {
1416 : 2 : extents->tail = extent;
1417 : 2 : extents->head = extent;
1418 : : }
1419 : :
1420 : 2 : extents->count += 1;
1421 : 2 : return 0;
1422 : : }
1423 : :
1424 : : int
1425 : 1 : block_status_add_extent(tapdisk_extents_t *extents, td_request_t *vreq)
1426 : : {
1427 : 1 : int ret = 0;
1428 [ + - ]: 1 : if(extents->tail == NULL) {
1429 : 1 : ret = add_extent(extents, vreq);
1430 : : } else {
1431 [ # # ]: 0 : if(extents->tail->flag == vreq->status) {
1432 : 0 : extents->tail->length += vreq->secs;
1433 : : } else {
1434 : 0 : ret = add_extent(extents, vreq);
1435 : : }
1436 : : }
1437 : 1 : return ret;
1438 : : }
1439 : :
1440 : : void
1441 : 1 : tapdisk_vbd_complete_block_status_request(td_request_t treq, int res)
1442 : : {
1443 : 1 : td_vbd_t *vbd;
1444 : 1 : td_image_t *image;
1445 : 1 : td_vbd_request_t *vreq;
1446 : :
1447 : 1 : image = treq.image;
1448 : 1 : vreq = treq.vreq;
1449 : 1 : vbd = vreq->vbd;
1450 : 1 : tapdisk_vbd_mark_progress(vbd);
1451 : :
1452 : : /* Record this extents in the vreqs data */
1453 : 1 : tapdisk_extents_t* extents = (tapdisk_extents_t*)vreq->data;
1454 [ - + ]: 1 : if( block_status_add_extent(extents, &treq) != 0) {
1455 : 0 : ERROR("Could not allocate extent structure");
1456 : : /* Propagate the ENOMEM */
1457 : 0 : res = -ENOMEM;
1458 : : }
1459 : :
1460 : 1 : DBG(TLOG_DBG, "%s: req %s seg %d sec 0x%08"PRIx64
1461 : : " secs 0x%04x buf %p op %d res %d\n", image->name,
1462 : : vreq->name, treq.sidx, treq.sec, treq.secs,
1463 : : treq.buf, vreq->op, res);
1464 : :
1465 : 1 : __tapdisk_vbd_complete_td_request(vbd, vreq, treq, res);
1466 : 1 : }
1467 : :
1468 : : void
1469 : 0 : tapdisk_vbd_complete_td_request(td_request_t treq, int res)
1470 : : {
1471 : 0 : td_vbd_t *vbd;
1472 : 0 : td_image_t *image, *leaf;
1473 : 0 : td_vbd_request_t *vreq;
1474 : :
1475 : 0 : image = treq.image;
1476 : 0 : vreq = treq.vreq;
1477 : 0 : vbd = vreq->vbd;
1478 : :
1479 : 0 : tapdisk_vbd_mark_progress(vbd);
1480 : :
1481 [ # # # # ]: 0 : if (abs(res) == ENOSPC && td_flag_test(image->flags,
1482 : : TD_IGNORE_ENOSPC)) {
1483 : 0 : res = 0;
1484 [ # # ]: 0 : leaf = tapdisk_vbd_first_image(vbd);
1485 [ # # ]: 0 : if (vbd->secondary_mode == TD_VBD_SECONDARY_MIRROR) {
1486 : 0 : DPRINTF("ENOSPC: disabling mirroring\n");
1487 : 0 : list_del_init(&leaf->next);
1488 : 0 : vbd->retired = leaf;
1489 [ # # ]: 0 : } else if (vbd->secondary_mode == TD_VBD_SECONDARY_STANDBY) {
1490 : 0 : DPRINTF("ENOSPC: failing over to secondary image\n");
1491 : 0 : list_add(&vbd->secondary->next, leaf->next.prev);
1492 : 0 : vbd->FIXME_enospc_redirect_count_enabled = 1;
1493 : : }
1494 [ # # ]: 0 : if (vbd->secondary_mode != TD_VBD_SECONDARY_DISABLED) {
1495 : 0 : vbd->secondary = NULL;
1496 : 0 : vbd->secondary_mode = TD_VBD_SECONDARY_DISABLED;
1497 : 0 : signal_enospc(vbd);
1498 : : }
1499 : : }
1500 : :
1501 [ # # # # ]: 0 : if (res != 0 && image->type == DISK_TYPE_NBD &&
1502 [ # # ]: 0 : ((image == vbd->secondary) ||
1503 [ # # ]: 0 : (image == vbd->retired))) {
1504 : 0 : ERROR("Got non-zero res %d for NBD secondary - disabling "
1505 : : "mirroring: %s", res, vreq->name);
1506 : 0 : vbd->nbd_mirror_failed = 1;
1507 : 0 : res = 0; /* Pretend the writes have completed successfully */
1508 : :
1509 : : /* It was the secondary that timed out - disable secondary */
1510 [ # # ]: 0 : list_del_init(&image->next);
1511 : 0 : vbd->retired = image;
1512 [ # # ]: 0 : if (vbd->secondary_mode != TD_VBD_SECONDARY_DISABLED) {
1513 : 0 : vbd->secondary = NULL;
1514 : 0 : vbd->secondary_mode = TD_VBD_SECONDARY_DISABLED;
1515 : : }
1516 : : }
1517 : :
1518 : 0 : DBG(TLOG_DBG, "%s: req %s seg %d sec 0x%08"PRIx64
1519 : : " secs 0x%04x buf %p op %d res %d\n", image->name,
1520 : : vreq->name, treq.sidx, treq.sec, treq.secs,
1521 : : treq.buf, vreq->op, res);
1522 : :
1523 : 0 : __tapdisk_vbd_complete_td_request(vbd, vreq, treq, res);
1524 : 0 : }
1525 : :
1526 : : static inline void
1527 : 0 : queue_mirror_req(td_vbd_t *vbd, td_request_t clone)
1528 : : {
1529 : 0 : clone.image = vbd->secondary;
1530 : 0 : td_queue_write(vbd->secondary, clone);
1531 : : }
1532 : :
1533 : : int
1534 : 1 : tapdisk_vbd_issue_request(td_vbd_t *vbd, td_vbd_request_t *vreq)
1535 : : {
1536 : 1 : td_image_t *image;
1537 : 1 : td_request_t treq;
1538 [ + - ]: 1 : bzero(&treq, sizeof(treq));
1539 : 1 : td_sector_t sec;
1540 : 1 : int i, err;
1541 : :
1542 : 1 : sec = vreq->sec;
1543 [ + - ]: 1 : image = tapdisk_vbd_first_image(vbd);
1544 : :
1545 : 1 : vreq->submitting = 1;
1546 : :
1547 : 1 : tapdisk_vbd_mark_progress(vbd);
1548 : 1 : vreq->last_try = vbd->ts;
1549 : :
1550 [ + - ]: 1 : tapdisk_vbd_move_request(vreq, &vbd->pending_requests);
1551 : :
1552 [ + - ]: 1 : err = tapdisk_vbd_check_queue(vbd);
1553 : 1 : if (err) {
1554 : 0 : vreq->error = err;
1555 : 0 : goto fail;
1556 : : }
1557 : :
1558 : 1 : err = tapdisk_image_check_request(image, vreq);
1559 [ - + ]: 1 : if (err) {
1560 : 0 : vreq->error = err;
1561 : 0 : goto fail;
1562 : : }
1563 : :
1564 [ + + ]: 2 : for (i = 0; i < vreq->iovcnt; i++) {
1565 : 1 : struct td_iovec *iov = &vreq->iov[i];
1566 : :
1567 : 1 : treq.sidx = i;
1568 : 1 : treq.buf = iov->base;
1569 : 1 : treq.sec = sec;
1570 : 1 : treq.secs = iov->secs;
1571 : 1 : treq.image = image;
1572 : 1 : treq.cb = tapdisk_vbd_complete_td_request;
1573 : 1 : treq.cb_data = NULL;
1574 : 1 : treq.vreq = vreq;
1575 : :
1576 : :
1577 : 1 : vreq->secs_pending += iov->secs;
1578 : 1 : vbd->secs_pending += iov->secs;
1579 [ - + ]: 1 : if (vbd->secondary_mode == TD_VBD_SECONDARY_MIRROR &&
1580 [ # # ]: 0 : vreq->op == TD_OP_WRITE &&
1581 [ # # ]: 0 : likely(vreq->skip_mirror == false))
1582 : : {
1583 : 0 : vreq->secs_pending += iov->secs;
1584 : 0 : vbd->secs_pending += iov->secs;
1585 : : }
1586 : :
1587 [ - - + - ]: 1 : switch (vreq->op) {
1588 : 0 : case TD_OP_WRITE:
1589 : 0 : treq.op = TD_OP_WRITE;
1590 : 0 : vbd->vdi_stats.stats->write_reqs_submitted++;
1591 : : /*
1592 : : * it's important to queue the mirror request before
1593 : : * queuing the main one. If the main image runs into
1594 : : * ENOSPC, the mirroring could be disabled before
1595 : : * td_queue_write returns, so if the mirror request was
1596 : : * queued after (which would then not happen), we'd
1597 : : * lose that write and cause the process to hang with
1598 : : * unacknowledged writes.
1599 : : *
1600 : : * Skip when lcache is storing a read-through.
1601 : : */
1602 [ # # ]: 0 : if (vbd->secondary_mode == TD_VBD_SECONDARY_MIRROR &&
1603 [ # # ]: 0 : likely(vreq->skip_mirror == false)) {
1604 : 0 : queue_mirror_req(vbd, treq);
1605 : : }
1606 : :
1607 : 0 : td_queue_write(treq.image, treq);
1608 : : break;
1609 : :
1610 : 0 : case TD_OP_READ:
1611 : 0 : treq.op = TD_OP_READ;
1612 : 0 : vbd->vdi_stats.stats->read_reqs_submitted++;
1613 : 0 : td_queue_read(treq.image, treq);
1614 : : break;
1615 : 1 : case TD_OP_BLOCK_STATUS:
1616 : 1 : treq.op = TD_OP_BLOCK_STATUS;
1617 : 1 : treq.cb = tapdisk_vbd_complete_block_status_request;
1618 : 1 : td_queue_block_status(treq.image, &treq);
1619 : : break;
1620 : : }
1621 : :
1622 : 1 : DBG(TLOG_DBG, "%s: req %s seg %d sec 0x%08"PRIx64" secs 0x%04x "
1623 : : "buf %p op %d\n", image->name, vreq->name, i, treq.sec, treq.secs,
1624 : : treq.buf, vreq->op);
1625 : 1 : sec += iov->secs;
1626 : : }
1627 : :
1628 : : err = 0;
1629 : :
1630 : 1 : out:
1631 : 1 : vreq->submitting--;
1632 [ - + ]: 1 : if (!vreq->secs_pending) {
1633 [ # # ]: 0 : err = (err ? : vreq->error);
1634 : 0 : tapdisk_vbd_complete_vbd_request(vbd, vreq);
1635 : : }
1636 : :
1637 : 1 : return err;
1638 : :
1639 : 0 : fail:
1640 : 0 : vreq->error = err;
1641 : 0 : goto out;
1642 : : }
1643 : :
1644 : : static int
1645 : 0 : tapdisk_vbd_request_completed(td_vbd_t *vbd, td_vbd_request_t *vreq)
1646 : : {
1647 : 0 : return vreq->list_head == &vbd->completed_requests;
1648 : : }
1649 : :
1650 : : static int
1651 : 0 : tapdisk_vbd_reissue_failed_requests(td_vbd_t *vbd)
1652 : : {
1653 : 0 : int err;
1654 : 0 : struct timeval now;
1655 : 0 : td_vbd_request_t *vreq, *tmp;
1656 : :
1657 : 0 : err = 0;
1658 : 0 : gettimeofday(&now, NULL);
1659 : :
1660 [ # # # # ]: 0 : tapdisk_vbd_for_each_request(vreq, tmp, &vbd->failed_requests) {
1661 [ # # ]: 0 : if (vreq->secs_pending)
1662 : 0 : continue;
1663 : :
1664 [ # # ]: 0 : if (td_flag_test(vbd->state, TD_VBD_SHUTDOWN_REQUESTED)) {
1665 : 0 : tapdisk_vbd_complete_vbd_request(vbd, vreq);
1666 : 0 : continue;
1667 : : }
1668 : :
1669 [ # # ]: 0 : if (vreq->error != -EBUSY &&
1670 [ # # ]: 0 : now.tv_sec - vreq->last_try.tv_sec < TD_VBD_RETRY_INTERVAL)
1671 : 0 : continue;
1672 : :
1673 : 0 : vbd->retries++;
1674 : 0 : vreq->num_retries++;
1675 : :
1676 : 0 : vreq->prev_error = vreq->error;
1677 : 0 : vreq->error = 0;
1678 : :
1679 : 0 : DBG(TLOG_DBG, "retry #%d of req %s, "
1680 : : "sec 0x%08"PRIx64", iovcnt: %d\n", vreq->num_retries,
1681 : : vreq->name, vreq->sec, vreq->iovcnt);
1682 : :
1683 : 0 : err = tapdisk_vbd_issue_request(vbd, vreq);
1684 : : /*
1685 : : * if this request failed, but was not completed,
1686 : : * we'll back off for a while.
1687 : : */
1688 [ # # # # ]: 0 : if (err && !tapdisk_vbd_request_completed(vbd, vreq))
1689 : : break;
1690 : : }
1691 : :
1692 : 0 : return 0;
1693 : : }
1694 : :
1695 : : static void
1696 : 0 : tapdisk_vbd_count_new_request(td_vbd_t *vbd, td_vbd_request_t *vreq)
1697 : : {
1698 : 0 : struct td_iovec *iov;
1699 : 0 : int write;
1700 : :
1701 : 0 : write = vreq->op == TD_OP_WRITE;
1702 : :
1703 [ # # ]: 0 : for (iov = &vreq->iov[0]; iov < &vreq->iov[vreq->iovcnt]; iov++)
1704 [ # # ]: 0 : td_sector_count_add(&vbd->secs, iov->secs, write);
1705 : 0 : }
1706 : :
1707 : : static int
1708 : 0 : tapdisk_vbd_issue_new_requests(td_vbd_t *vbd)
1709 : : {
1710 : 0 : int err;
1711 : 0 : td_vbd_request_t *vreq, *tmp;
1712 : :
1713 [ # # ]: 0 : tapdisk_vbd_for_each_request(vreq, tmp, &vbd->new_requests) {
1714 : 0 : err = tapdisk_vbd_issue_request(vbd, vreq);
1715 : : /*
1716 : : * if this request failed, but was not completed,
1717 : : * we'll back off for a while.
1718 : : */
1719 [ # # # # ]: 0 : if (err && !tapdisk_vbd_request_completed(vbd, vreq))
1720 : : return err;
1721 : :
1722 : 0 : tapdisk_vbd_count_new_request(vbd, vreq);
1723 : : }
1724 : :
1725 : : return 0;
1726 : : }
1727 : :
1728 : : int
1729 : 0 : tapdisk_vbd_recheck_state(td_vbd_t *vbd)
1730 : : {
1731 : 0 : int err = 0;
1732 : :
1733 [ # # ]: 0 : if (list_empty(&vbd->new_requests))
1734 : : return 0;
1735 : :
1736 [ # # ]: 0 : if (td_flag_test(vbd->state, TD_VBD_QUIESCED) ||
1737 : : td_flag_test(vbd->state, TD_VBD_QUIESCE_REQUESTED))
1738 : : return 0;
1739 : :
1740 : 0 : err = tapdisk_vbd_issue_requests(vbd);
1741 : :
1742 : : /* If we have errors stop checking in this cycle */
1743 : 0 : return err ? 0 : 1;
1744 : : }
1745 : :
1746 : : static int
1747 : 0 : tapdisk_vbd_kill_requests(td_vbd_t *vbd)
1748 : : {
1749 : 0 : td_vbd_request_t *vreq, *tmp;
1750 : :
1751 [ # # ]: 0 : tapdisk_vbd_for_each_request(vreq, tmp, &vbd->new_requests) {
1752 : 0 : vreq->error = -ESHUTDOWN;
1753 : 0 : tapdisk_vbd_move_request(vreq, &vbd->completed_requests);
1754 : : }
1755 : :
1756 [ # # ]: 0 : tapdisk_vbd_for_each_request(vreq, tmp, &vbd->failed_requests) {
1757 : 0 : vreq->error = -ESHUTDOWN;
1758 : 0 : tapdisk_vbd_move_request(vreq, &vbd->completed_requests);
1759 : : }
1760 : :
1761 : 0 : return 0;
1762 : : }
1763 : :
1764 : : int
1765 : 0 : tapdisk_vbd_issue_requests(td_vbd_t *vbd)
1766 : : {
1767 : 0 : int err;
1768 : :
1769 [ # # ]: 0 : if (td_flag_test(vbd->state, TD_VBD_DEAD))
1770 : 0 : return tapdisk_vbd_kill_requests(vbd);
1771 : :
1772 [ # # ]: 0 : if (td_flag_test(vbd->state, TD_VBD_QUIESCED) ||
1773 : : td_flag_test(vbd->state, TD_VBD_QUIESCE_REQUESTED)) {
1774 : :
1775 [ # # ]: 0 : if (td_flag_test(vbd->state, TD_VBD_RESUME_FAILED))
1776 : 0 : return tapdisk_vbd_kill_requests(vbd);
1777 : : else
1778 : : return -EAGAIN;
1779 : : }
1780 : :
1781 : 0 : err = tapdisk_vbd_reissue_failed_requests(vbd);
1782 : 0 : if (err)
1783 : : return err;
1784 : :
1785 : 0 : return tapdisk_vbd_issue_new_requests(vbd);
1786 : : }
1787 : :
1788 : : int
1789 : 0 : tapdisk_vbd_queue_request(td_vbd_t *vbd, td_vbd_request_t *vreq)
1790 : : {
1791 : 0 : gettimeofday(&vreq->ts, NULL);
1792 : 0 : vreq->vbd = vbd;
1793 : :
1794 : 0 : list_add_tail(&vreq->next, &vbd->new_requests);
1795 : 0 : vbd->received++;
1796 : :
1797 : 0 : return 0;
1798 : : }
1799 : :
1800 : : void
1801 : 0 : tapdisk_vbd_kick(td_vbd_t *vbd)
1802 : : {
1803 : 0 : const struct list_head *list = &vbd->completed_requests;
1804 : 0 : td_vbd_request_t *vreq, *prev, *next;
1805 : :
1806 : 0 : vbd->kicked++;
1807 : :
1808 [ # # ]: 0 : while (!list_empty(list)) {
1809 : :
1810 : : /*
1811 : : * Take one request off the completed requests list, and then look for
1812 : : * other requests in the same list that have the same token and
1813 : : * complete them. This way we complete requests against the same token
1814 : : * in one go before we proceed to completing requests with other
1815 : : * tokens. The token is usually used to point back to some other
1816 : : * structure, e.g. a blktap or a tapdisk3 connexion. Once all requests
1817 : : * with a specific token have been completed, proceed to the next one
1818 : : * until the list is empty.
1819 : : */
1820 : 0 : prev = list_entry(list->next, td_vbd_request_t, next);
1821 : 0 : list_del(&prev->next);
1822 : :
1823 [ # # # # ]: 0 : tapdisk_vbd_for_each_request(vreq, next, list) {
1824 [ # # ]: 0 : if (vreq->token == prev->token) {
1825 : :
1826 : 0 : prev->cb(prev, prev->error, prev->token, 0);
1827 : 0 : vbd->returned++;
1828 : :
1829 : 0 : list_del(&vreq->next);
1830 : 0 : prev = vreq;
1831 : : }
1832 : : }
1833 : :
1834 : 0 : prev->cb(prev, prev->error, prev->token, 1);
1835 : 0 : vbd->returned++;
1836 : : }
1837 : 0 : }
1838 : :
1839 : : int
1840 : 0 : tapdisk_vbd_start_nbdservers(td_vbd_t *vbd)
1841 : : {
1842 : 0 : td_disk_info_t info;
1843 : 0 : int err;
1844 : :
1845 [ # # ]: 0 : err = tapdisk_vbd_get_disk_info(vbd, &info);
1846 : :
1847 [ # # ]: 0 : if (err)
1848 : 0 : return err;
1849 : :
1850 : 0 : vbd->nbdserver = tapdisk_nbdserver_alloc(vbd, info, TAPDISK_NBD_PROTOCOL_OLD);
1851 [ # # ]: 0 : if (!vbd->nbdserver) {
1852 : 0 : EPRINTF("Error starting nbd server");
1853 : 0 : return -1;
1854 : : }
1855 : 0 : err = tapdisk_nbdserver_listen_unix(vbd->nbdserver);
1856 [ # # ]: 0 : if (err) {
1857 : 0 : tapdisk_nbdserver_free(vbd->nbdserver);
1858 : 0 : EPRINTF("failed to listen on the UNIX domain socket: %s\n",
1859 : : strerror(-err));
1860 : 0 : return err;
1861 : : }
1862 : :
1863 : 0 : vbd->nbdserver_new = tapdisk_nbdserver_alloc(vbd, info, TAPDISK_NBD_PROTOCOL_NEW);
1864 [ # # ]: 0 : if (!vbd->nbdserver_new) {
1865 : 0 : EPRINTF("Error starting new-style nbd server");
1866 : 0 : return -1;
1867 : : }
1868 : 0 : err = tapdisk_nbdserver_listen_unix(vbd->nbdserver_new);
1869 [ # # ]: 0 : if (err) {
1870 : 0 : tapdisk_nbdserver_free(vbd->nbdserver_new);
1871 : 0 : EPRINTF("failed to listen on the UNIX domain socket: %s\n",
1872 : : strerror(-err));
1873 : 0 : return err;
1874 : : }
1875 : :
1876 : : return 0;
1877 : : }
1878 : :
1879 : :
1880 : : static int
1881 : 0 : tapdisk_vbd_reqs_outstanding(td_vbd_t *vbd)
1882 : : {
1883 : 0 : int new, pending, failed, completed;
1884 : :
1885 [ # # ]: 0 : ASSERT(vbd);
1886 : :
1887 : 0 : tapdisk_vbd_queue_count(vbd, &new, &pending, &failed, &completed);
1888 : :
1889 : 0 : return new + pending + failed + completed;
1890 : : }
1891 : :
1892 : :
1893 : : void
1894 : 0 : tapdisk_vbd_stats(td_vbd_t *vbd, td_stats_t *st)
1895 : : {
1896 : 0 : td_image_t *image, *next;
1897 : 0 : struct td_xenblkif *blkif;
1898 : 0 : const bool read_caching =
1899 : 0 : TD_OPEN_NO_O_DIRECT == (vbd->flags & TD_OPEN_NO_O_DIRECT);
1900 : :
1901 : 0 : tapdisk_stats_enter(st, '{');
1902 : 0 : tapdisk_stats_field(st, "name", "s", vbd->name);
1903 : :
1904 : 0 : tapdisk_stats_field(st, "secs", "[");
1905 : 0 : tapdisk_stats_val(st, "llu", vbd->secs.rd);
1906 : 0 : tapdisk_stats_val(st, "llu", vbd->secs.wr);
1907 : 0 : tapdisk_stats_leave(st, ']');
1908 : :
1909 : 0 : tapdisk_stats_field(st, "images", "[");
1910 [ # # ]: 0 : tapdisk_vbd_for_each_image(vbd, image, next)
1911 : 0 : tapdisk_image_stats(image, st);
1912 : 0 : tapdisk_stats_leave(st, ']');
1913 : :
1914 : : /*
1915 : : * TODO Is this used by any one?
1916 : : */
1917 [ # # ]: 0 : if (!list_empty(&vbd->rings)) {
1918 : 0 : tapdisk_stats_field(st, "xenbus", "{");
1919 [ # # ]: 0 : list_for_each_entry(blkif, &vbd->rings, entry)
1920 : 0 : tapdisk_xenblkif_stats(blkif, st);
1921 : 0 : tapdisk_stats_leave(st, '}');
1922 : : }
1923 : :
1924 : 0 : tapdisk_stats_field(st,
1925 : : "FIXME_enospc_redirect_count",
1926 : : "llu", vbd->FIXME_enospc_redirect_count);
1927 : :
1928 : 0 : tapdisk_stats_field(st,
1929 : : "nbd_mirror_failed",
1930 : : "d", vbd->nbd_mirror_failed);
1931 : :
1932 : 0 : tapdisk_stats_field(st,
1933 : : "reqs_outstanding",
1934 : : "d", tapdisk_vbd_reqs_outstanding(vbd));
1935 : :
1936 [ # # ]: 0 : tapdisk_stats_field(st,
1937 : : "read_caching",
1938 : : "s", read_caching ? "true": "false");
1939 : :
1940 : 0 : tapdisk_stats_leave(st, '}');
1941 : 0 : }
1942 : :
1943 : :
1944 : : bool
1945 : 0 : tapdisk_vbd_contains_dead_rings(td_vbd_t * vbd)
1946 : : {
1947 : 0 : return !list_empty(&vbd->dead_rings);
1948 : : }
|