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 <errno.h>
36 : :
37 : : #include "tapdisk.h"
38 : : #include "tapdisk-vbd.h"
39 : : #include "tapdisk-driver.h"
40 : : #include "tapdisk-interface.h"
41 : : #include "tapdisk-disktype.h"
42 : :
43 : : #define DBG(_f, _a...) tlog_syslog(TLOG_DBG, _f, ##_a)
44 : : #define INFO(_f, _a...) tlog_syslog(TLOG_INFO, _f, ##_a)
45 : : #define WARN(_f, _a...) tlog_syslog(TLOG_WARN, "WARNING: "_f "in %s:%d", \
46 : : ##_a, __func__, __LINE__)
47 : :
48 : : #define BUG() td_panic()
49 : : #define BUG_ON(_cond) if (unlikely(_cond)) { td_panic(); }
50 : : #define WARN_ON(_p) if (unlikely(_cond)) { WARN(_cond); }
51 : :
52 : 0 : int ll_write_error(int curr, int error)
53 : : {
54 [ # # ][ # # ]: 0 : if (error && (!curr || curr == -ENOSPC))
55 : 0 : return error;
56 : :
57 : : return 0;
58 : : }
59 : :
60 : 0 : void ll_log_switch(int type, int error,
61 : : td_image_t *local, td_image_t *shared)
62 : : {
63 : 0 : WARN("WARNING: %s, on %s:%s. Switching to %s:%s.",
64 : : strerror(-error),
65 : : tapdisk_disk_types[local->type]->name, local->name,
66 : : tapdisk_disk_types[shared->type]->name, shared->name);
67 : 0 : }
68 : :
69 : : /*
70 : : * LLP: Local leaf persistent cache
71 : : * -- Persistent write caching in local storage.
72 : : *
73 : : * VBD
74 : : * \
75 : : * +--r/w--> llp+vhd:/local/leaf
76 : : * \
77 : : * +--r/w--> vhd:/shared/leaf
78 : : * \
79 : : * +--r/o--> vhd:/shared/parent
80 : : *
81 : : * We drive two 'leaf' (r/w) images: One LOCAL (i.e. on local storage,
82 : : * unreliable and prone to out-of-space failures), and one SHARED
83 : : * (i.e. in shared storage with plenty of physical backing).
84 : : *
85 : : * All images are on a linear read chain: LOCAL inherits from SHARED,
86 : : * which inherits from a shared master image. This filter driver
87 : : * aggregates LOCAL. SHARED is our immediate parent, forced into R/W
88 : : * mode.
89 : : *
90 : : * Unless LOCAL failed, reads are issued to LOCAL, to save shared
91 : : * storage bandwidth. In case of failure, SHARED provides continued
92 : : * VDI consistency.
93 : : *
94 : : */
95 : : enum {
96 : : LLP_MIRROR = 1,
97 : : /*
98 : : * LLP_MIRROR:
99 : : *
100 : : * Writes are mirrored to both LOCAL and SHARED. Reads are
101 : : * issued to LOCAL.
102 : : *
103 : : * Failure to write LOCAL are recoverable. The driver will
104 : : * transition to LLP_SHARED.
105 : : *
106 : : * Failure to write SHARED is irrecoverable, and signaled to
107 : : * the original issuer.
108 : : */
109 : :
110 : : LLP_SHARED = 2,
111 : : /*
112 : : * LLP_SHARED:
113 : : *
114 : : * Writes are issued to SHARED only. As are reads.
115 : : *
116 : : * Failure to write SHARED is irrecoverable.
117 : : */
118 : : };
119 : :
120 : : typedef struct llpcache td_llpcache_t;
121 : : typedef struct llpcache_request td_llpcache_req_t;
122 : : #define TD_LLPCACHE_MAX_REQ (MAX_REQUESTS*2)
123 : :
124 : : struct llpcache_vreq {
125 : : enum { LOCAL = 0, SHARED = 1 } target;
126 : : td_vbd_request_t vreq;
127 : : };
128 : :
129 : : struct llpcache_request {
130 : : td_request_t treq;
131 : :
132 : : struct td_iovec iov;
133 : : int error;
134 : :
135 : : struct llpcache_vreq lvr[2];
136 : :
137 : : unsigned int pending;
138 : : int mode;
139 : : };
140 : :
141 : : struct llpcache {
142 : : td_image_t *local;
143 : : int mode;
144 : :
145 : : td_llpcache_req_t reqv[TD_LLPCACHE_MAX_REQ];
146 : : td_llpcache_req_t *free[TD_LLPCACHE_MAX_REQ];
147 : : int n_free;
148 : : };
149 : :
150 : : static td_llpcache_req_t *
151 : : llpcache_alloc_request(td_llpcache_t *s)
152 : : {
153 : 0 : td_llpcache_req_t *req = NULL;
154 : :
155 [ # # ]: 0 : if (likely(s->n_free))
156 : 0 : req = s->free[--s->n_free];
157 : :
158 : : return req;
159 : : }
160 : :
161 : : static void
162 : : llpcache_free_request(td_llpcache_t *s, td_llpcache_req_t *req)
163 : : {
164 [ # # ][ # # ]: 0 : BUG_ON(s->n_free >= TD_LLPCACHE_MAX_REQ);
[ # # ]
165 : 0 : s->free[s->n_free++] = req;
166 : : }
167 : :
168 : : static void
169 : 0 : __llpcache_write_cb(td_vbd_request_t *vreq, int error,
170 : : void *token, int final)
171 : : {
172 : 0 : td_llpcache_t *s = token;
173 : : struct llpcache_vreq *lvr;
174 : : td_llpcache_req_t *req;
175 : : int mask;
176 : :
177 : 0 : lvr = container_of(vreq, struct llpcache_vreq, vreq);
178 : 0 : req = container_of(lvr, td_llpcache_req_t, lvr[lvr->target]);
179 : :
180 : 0 : mask = 1U << lvr->target;
181 [ # # ]: 0 : BUG_ON(!(req->pending & mask))
182 : :
183 [ # # ][ # # ]: 0 : if (lvr->target == LOCAL && error == -ENOSPC) {
184 : 0 : td_image_t *shared =
185 : 0 : container_of(req->treq.image->next.next,
186 : : td_image_t, next);
187 : 0 : ll_log_switch(DISK_TYPE_LLPCACHE, error,
188 : : s->local, shared);
189 : 0 : s->mode = LLP_SHARED;
190 : 0 : error = 0;
191 : : }
192 : :
193 : 0 : req->pending &= ~mask;
194 : 0 : req->error = ll_write_error(req->error, error);
195 : :
196 [ # # ]: 0 : if (!req->pending) {
197 : : /* FIXME: Make sure this won't retry. */
198 : 0 : td_complete_request(req->treq, req->error);
199 : : llpcache_free_request(s, req);
200 : : }
201 : 0 : }
202 : :
203 : : /*
204 : : * NB. Write mirroring. Lacking per-image queues, it's still a
205 : : * hack. But shall do for now:
206 : : *
207 : : * 1. Store the treq, thereby blocking the original vreq.
208 : : * 2. Reissue, as two clone vreqs. One local, one shared.
209 : : * 3. Clones seen again then get forwarded.
210 : : * 4. Treq completes after both vreqs.
211 : : *
212 : : * We can recognize clones by matching the vreq->token field.
213 : : */
214 : :
215 : : static int
216 : 0 : llpcache_requeue_treq(td_llpcache_t *s, td_llpcache_req_t *req, int target)
217 : : {
218 : : struct llpcache_vreq *lvr;
219 : : td_vbd_request_t *vreq;
220 : : int err;
221 : :
222 : 0 : lvr = &req->lvr[target];
223 : 0 : lvr->target = target;
224 : :
225 : 0 : vreq = &lvr->vreq;
226 : 0 : vreq->op = TD_OP_WRITE;
227 : 0 : vreq->sec = req->treq.sec;
228 : 0 : vreq->iov = &req->iov;
229 : 0 : vreq->iovcnt = 1;
230 : 0 : vreq->cb = __llpcache_write_cb;
231 : 0 : vreq->token = s;
232 : :
233 : 0 : err = tapdisk_vbd_queue_request(req->treq.vreq->vbd, vreq);
234 [ # # ]: 0 : if (err)
235 : : goto fail;
236 : :
237 : 0 : req->pending |= 1UL << target;
238 : 0 : return 0;
239 : :
240 : : fail:
241 [ # # ]: 0 : req->error = req->error ? : err;
242 : 0 : return err;
243 : : }
244 : :
245 : : static void
246 : 0 : llpcache_fork_write(td_llpcache_t *s, td_request_t treq)
247 : : {
248 : : td_llpcache_req_t *req;
249 : : struct td_iovec *iov;
250 : : int err;
251 : :
252 : 0 : req = llpcache_alloc_request(s);
253 [ # # ]: 0 : if (!req) {
254 : 0 : td_complete_request(treq, -EBUSY);
255 : 0 : return;
256 : : }
257 : :
258 : : memset(req, 0, sizeof(td_llpcache_req_t));
259 : :
260 : 0 : req->treq = treq;
261 : :
262 : 0 : iov = &req->iov;
263 : 0 : iov->base = treq.buf;
264 : 0 : iov->secs = treq.secs;
265 : :
266 : 0 : err = llpcache_requeue_treq(s, req, LOCAL);
267 [ # # ]: 0 : if (err)
268 : : goto fail;
269 : :
270 : 0 : err = llpcache_requeue_treq(s, req, SHARED);
271 [ # # ]: 0 : if (err)
272 : : goto fail;
273 : :
274 : : return;
275 : :
276 : : fail:
277 [ # # ]: 0 : if (!req->pending) {
278 : 0 : td_complete_request(treq, req->error);
279 : : llpcache_free_request(s, req);
280 : : }
281 : : }
282 : :
283 : : static void
284 : 0 : llpcache_forward_write(td_llpcache_t *s, td_request_t treq)
285 : : {
286 : 0 : const td_vbd_request_t *vreq = treq.vreq;
287 : : struct llpcache_vreq *lvr;
288 : :
289 : 0 : lvr = container_of(vreq, struct llpcache_vreq, vreq);
290 : :
291 [ # # # ]: 0 : switch (lvr->target) {
292 : : case SHARED:
293 : 0 : td_forward_request(treq);
294 : 0 : break;
295 : : case LOCAL:
296 : 0 : td_queue_write(s->local, treq);
297 : 0 : break;
298 : : default:
299 : 0 : BUG();
300 : : }
301 : 0 : }
302 : :
303 : : static void
304 : 0 : llpcache_queue_write(td_driver_t *driver, td_request_t treq)
305 : : {
306 : 0 : td_llpcache_t *s = driver->data;
307 : :
308 [ # # ]: 0 : if (treq.vreq->token == s)
309 : 0 : llpcache_forward_write(s, treq);
310 : : else
311 : 0 : llpcache_fork_write(s, treq);
312 : 0 : }
313 : :
314 : : static void
315 : 0 : llpcache_queue_read(td_driver_t *driver, td_request_t treq)
316 : : {
317 : 0 : td_llpcache_t *s = driver->data;
318 : :
319 [ # # # ]: 0 : switch (s->mode) {
320 : : case LLP_MIRROR:
321 : 0 : td_queue_read(s->local, treq);
322 : : break;
323 : : case LLP_SHARED:
324 : 0 : td_forward_request(treq);
325 : : default:
326 : 0 : BUG();
327 : : }
328 : 0 : }
329 : :
330 : : static int
331 : 0 : llpcache_close(td_driver_t *driver)
332 : : {
333 : 0 : td_llpcache_t *s = driver->data;
334 : :
335 [ # # ][ # # ]: 0 : if (s->local) {
336 : 0 : tapdisk_image_close(s->local);
337 : 0 : s->local = NULL;
338 : : }
339 : :
340 : 0 : return 0;
341 : : }
342 : :
343 : : static int
344 : 0 : llpcache_open(td_driver_t *driver, const char *name,
345 : : struct td_vbd_encryption *encryption, td_flag_t flags)
346 : : {
347 : 0 : td_llpcache_t *s = driver->data;
348 : : int i, err;
349 : :
350 : 0 : s->mode = LLP_MIRROR;
351 : :
352 [ # # ]: 0 : for (i = 0; i < TD_LLPCACHE_MAX_REQ; i++)
353 : 0 : llpcache_free_request(s, &s->reqv[i]);
354 : :
355 : 0 : err = tapdisk_image_open(DISK_TYPE_VHD, name, flags, encryption, &s->local);
356 [ # # ]: 0 : if (err)
357 : : goto fail;
358 : :
359 : 0 : driver->info = s->local->driver->info;
360 : :
361 : 0 : return 0;
362 : :
363 : : fail:
364 : : llpcache_close(driver);
365 : 0 : return err;
366 : : }
367 : :
368 : : static int
369 : 0 : llcache_get_parent_id(td_driver_t *driver, td_disk_id_t *id)
370 : : {
371 : 0 : td_llpcache_t *s = driver->data;
372 : : int err;
373 : :
374 : 0 : err = td_get_parent_id(s->local, id);
375 [ # # ]: 0 : if (!err)
376 : 0 : id->flags &= ~TD_OPEN_RDONLY;
377 : :
378 : 0 : return err;
379 : : }
380 : :
381 : : static int
382 : 0 : llcache_validate_parent(td_driver_t *driver,
383 : : td_driver_t *pdriver, td_flag_t flags)
384 : : {
385 : 0 : return -ENOSYS;
386 : : }
387 : :
388 : :
389 : : struct tap_disk tapdisk_llpcache = {
390 : : .disk_type = "tapdisk_llpcache",
391 : : .flags = 0,
392 : : .private_data_size = sizeof(td_llpcache_t),
393 : : .td_open = llpcache_open,
394 : : .td_close = llpcache_close,
395 : : .td_queue_read = llpcache_queue_read,
396 : : .td_queue_write = llpcache_queue_write,
397 : : .td_get_parent_id = llcache_get_parent_id,
398 : : .td_validate_parent = llcache_validate_parent,
399 : : };
400 : :
401 : : /*
402 : : * LLE: Local Leaf Ephemeral Cache
403 : : * -- Non-persistent write caching in local storage.
404 : : *
405 : : * VBD
406 : : * \
407 : : * +--r/w--> lle+vhd:/shared/leaf
408 : : * \
409 : : * +--r/w--> vhd:/local/leaf
410 : : * \
411 : : * +--r/o--> vhd:/shared/parent
412 : : *
413 : : * Note that LOCAL and SHARED chain order differs from LLP. Shared
414 : : * storage data masks local data.
415 : : *
416 : : * This means VDI state in shared storage state alone is
417 : : * inconsistent. Wherever local is unavailable, SHARED must be
418 : : * discarded too.
419 : : */
420 : : enum {
421 : : LLE_LOCAL = 1,
422 : : /*
423 : : * LLE_LOCAL:
424 : : *
425 : : * Writes are forwarded to LOCAL only. As are reads. This
426 : : * reduces network overhead.
427 : : *
428 : : * Failure to write LOCAL is recoverable. The driver will
429 : : * transition to LLE_SHARED.
430 : : *
431 : : * Failure to write to shared are irrecoverable and signaled
432 : : * to the original issuer.
433 : : */
434 : :
435 : : LLE_SHARED = 2,
436 : : /*
437 : : * LLE_SHARED:
438 : : *
439 : : * Writes are issued to SHARED. As are reads.
440 : : *
441 : : * Failure to write to SHARED is irrecoverable.
442 : : */
443 : : };
444 : :
445 : : typedef struct llecache td_llecache_t;
446 : : typedef struct llecache_request td_llecache_req_t;
447 : : #define TD_LLECACHE_MAX_REQ (MAX_REQUESTS*2)
448 : :
449 : : struct llecache_request {
450 : : td_llecache_t *s;
451 : : td_request_t treq;
452 : : int pending;
453 : : int error;
454 : : };
455 : :
456 : : struct llecache {
457 : : td_image_t *shared;
458 : : int mode;
459 : :
460 : : td_llecache_req_t reqv[TD_LLECACHE_MAX_REQ];
461 : : td_llecache_req_t *free[TD_LLECACHE_MAX_REQ];
462 : : int n_free;
463 : : };
464 : :
465 : : static td_llecache_req_t *
466 : : llecache_alloc_request(td_llecache_t *s)
467 : : {
468 : 0 : td_llecache_req_t *req = NULL;
469 : :
470 [ # # ]: 0 : if (likely(s->n_free))
471 : 0 : req = s->free[--s->n_free];
472 : :
473 : : return req;
474 : : }
475 : :
476 : : static void
477 : : llecache_free_request(td_llecache_t *s, td_llecache_req_t *req)
478 : : {
479 [ # # ][ # # ]: 0 : BUG_ON(s->n_free >= TD_LLECACHE_MAX_REQ);
480 : 0 : s->free[s->n_free++] = req;
481 : : }
482 : :
483 : : static int
484 : 0 : llecache_close(td_driver_t *driver)
485 : : {
486 : 0 : td_llecache_t *s = driver->data;
487 : :
488 [ # # ][ # # ]: 0 : if (s->shared) {
489 : 0 : tapdisk_image_close(s->shared);
490 : 0 : s->shared = NULL;
491 : : }
492 : :
493 : 0 : return 0;
494 : : }
495 : :
496 : : static int
497 : 0 : llecache_open(td_driver_t *driver, const char *name,
498 : : struct td_vbd_encryption *encryption, td_flag_t flags)
499 : : {
500 : 0 : td_llecache_t *s = driver->data;
501 : : int i, err;
502 : :
503 : 0 : s->mode = LLE_LOCAL;
504 : :
505 [ # # ]: 0 : for (i = 0; i < TD_LLECACHE_MAX_REQ; i++)
506 : 0 : llecache_free_request(s, &s->reqv[i]);
507 : :
508 : 0 : err = tapdisk_image_open(DISK_TYPE_VHD, name, flags, encryption, &s->shared);
509 [ # # ]: 0 : if (err)
510 : : goto fail;
511 : :
512 : 0 : driver->info = s->shared->driver->info;
513 : :
514 : 0 : return 0;
515 : :
516 : : fail:
517 : : llecache_close(driver);
518 : 0 : return err;
519 : : }
520 : :
521 : : static void
522 : 0 : __llecache_write_cb(td_request_t treq, int error)
523 : : {
524 : 0 : td_llecache_req_t *req = treq.cb_data;
525 : 0 : td_llecache_t *s = req->s;
526 : :
527 [ # # ]: 0 : BUG_ON(req->pending < treq.secs);
528 : :
529 : 0 : req->pending -= treq.secs;
530 : 0 : req->error = ll_write_error(req->error, error);
531 : :
532 [ # # ]: 0 : if (req->pending)
533 : 0 : return;
534 : :
535 [ # # ]: 0 : if (req->error == -ENOSPC) {
536 : 0 : ll_log_switch(DISK_TYPE_LLECACHE, req->error,
537 : : treq.image, s->shared);
538 : :
539 : 0 : s->mode = LLE_SHARED;
540 : 0 : td_queue_write(s->shared, req->treq);
541 : :
542 : : } else
543 : 0 : td_complete_request(req->treq, error);
544 : :
545 : : llecache_free_request(s, req);
546 : : }
547 : :
548 : : static void
549 : 0 : llecache_forward_write(td_llecache_t *s, td_request_t treq)
550 : : {
551 : : td_llecache_req_t *req;
552 : : td_request_t clone;
553 : :
554 : 0 : req = llecache_alloc_request(s);
555 [ # # ]: 0 : if (!req) {
556 : 0 : td_complete_request(treq, -EBUSY);
557 : 0 : return;
558 : : }
559 : :
560 : : memset(req, 0, sizeof(td_llecache_req_t));
561 : :
562 : 0 : req->treq = treq;
563 : 0 : req->pending = treq.secs;
564 : 0 : req->s = s;
565 : :
566 : 0 : clone = treq;
567 : 0 : clone.cb = __llecache_write_cb;
568 : 0 : clone.cb_data = req;
569 : :
570 : 0 : td_forward_request(clone);
571 : : }
572 : :
573 : : static void
574 : 0 : llecache_queue_write(td_driver_t *driver, td_request_t treq)
575 : : {
576 : 0 : td_llecache_t *s = driver->data;
577 : :
578 [ # # # ]: 0 : switch (s->mode) {
579 : : case LLE_LOCAL:
580 : 0 : llecache_forward_write(s, treq);
581 : 0 : break;
582 : : case LLE_SHARED:
583 : 0 : td_queue_write(s->shared, treq);
584 : 0 : break;
585 : : }
586 : 0 : }
587 : :
588 : : static void
589 : 0 : llecache_queue_read(td_driver_t *driver, td_request_t treq)
590 : : {
591 : 0 : td_llecache_t *s = driver->data;
592 : :
593 [ # # # ]: 0 : switch (s->mode) {
594 : : case LLE_LOCAL:
595 : 0 : td_forward_request(treq);
596 : 0 : break;
597 : : case LLE_SHARED:
598 : 0 : td_queue_read(s->shared, treq);
599 : 0 : break;
600 : : default:
601 : 0 : BUG();
602 : : }
603 : 0 : }
604 : :
605 : : struct tap_disk tapdisk_llecache = {
606 : : .disk_type = "tapdisk_llecache",
607 : : .flags = 0,
608 : : .private_data_size = sizeof(td_llecache_t),
609 : : .td_open = llecache_open,
610 : : .td_close = llecache_close,
611 : : .td_queue_read = llecache_queue_read,
612 : : .td_queue_write = llecache_queue_write,
613 : : .td_get_parent_id = llcache_get_parent_id,
614 : : .td_validate_parent = llcache_validate_parent,
615 : : };
|