Branch data Line data Source code
1 : : /*
2 : : * Write any sectors not found in the leaf back to the leaf.
3 : : * Copyright (c) 2016, Citrix Systems, Inc.
4 : : *
5 : : * All rights reserved.
6 : : *
7 : : * Redistribution and use in source and binary forms, with or without
8 : : * modification, are permitted provided that the following conditions are met:
9 : : *
10 : : * 1. Redistributions of source code must retain the above copyright
11 : : * notice, this list of conditions and the following disclaimer.
12 : : * 2. Redistributions in binary form must reproduce the above copyright
13 : : * notice, this list of conditions and the following disclaimer in the
14 : : * documentation and/or other materials provided with the distribution.
15 : : * 3. Neither the name of the copyright holder nor the names of its
16 : : * contributors may be used to endorse or promote products derived from
17 : : * this software without specific prior written permission.
18 : : *
19 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
23 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 : : */
31 : :
32 : : #ifdef HAVE_CONFIG_H
33 : : #include "config.h"
34 : : #endif
35 : :
36 : : #include <errno.h>
37 : : #include <fcntl.h>
38 : : #include <unistd.h>
39 : : #include <stdlib.h>
40 : : #include <limits.h>
41 : : #include <sys/mman.h>
42 : : #include <sys/vfs.h>
43 : :
44 : : #include "vhd.h"
45 : : #include "td-req.h"
46 : : #include "tapdisk.h"
47 : : #include "tapdisk-utils.h"
48 : : #include "tapdisk-driver.h"
49 : : #include "tapdisk-server.h"
50 : : #include "tapdisk-interface.h"
51 : : #include "tapdisk-disktype.h"
52 : :
53 : : #define DEBUG 1
54 : :
55 : : #ifdef DEBUG
56 : : #define DBG(_f, _a...) tlog_write(TLOG_DBG, _f, ##_a)
57 : : #else
58 : : #define DBG(_f, _a...) ((void)0)
59 : : #endif
60 : : #define WARN(_f, _a...) tlog_syslog(TLOG_WARN, "WARNING: "_f "in %s:%d", \
61 : : ##_a, __func__, __LINE__)
62 : : #define INFO(_f, _a...) tlog_syslog(TLOG_INFO, _f, ##_a)
63 : : #define BUG() td_panic()
64 : : #define BUG_ON(_cond) if (unlikely(_cond)) { td_panic(); }
65 : : #define WARN_ON(_p) if (unlikely(_cond)) { WARN(_cond); }
66 : :
67 : : #define MIN(a, b) ((a) < (b) ? (a) : (b))
68 : :
69 : : #define TD_LCACHE_MAX_REQ (MAX_REQUESTS*2)
70 : : #define TD_LCACHE_BUFSZ (BLKIF_MAX_BUFFER_SEGMENTS_PER_REQUEST << PAGE_SHIFT)
71 : :
72 : :
73 : : typedef struct lcache td_lcache_t;
74 : : typedef struct lcache_request td_lcache_req_t;
75 : :
76 : : struct lcache_request {
77 : : char *buf;
78 : : int err;
79 : :
80 : : td_request_t treq;
81 : : int secs;
82 : :
83 : : td_vbd_request_t vreq;
84 : : struct td_iovec iov;
85 : :
86 : : td_lcache_t *cache;
87 : : };
88 : :
89 : : struct lcache {
90 : : char *name;
91 : :
92 : : td_lcache_req_t reqv[TD_LCACHE_MAX_REQ];
93 : : td_lcache_req_t *free[TD_LCACHE_MAX_REQ];
94 : : int n_free;
95 : :
96 : : char *buf;
97 : : size_t bufsz;
98 : :
99 : : int wr_en;
100 : : struct timeval ts;
101 : : };
102 : :
103 : : static td_lcache_req_t *
104 : : lcache_alloc_request(td_lcache_t *cache)
105 : : {
106 : 0 : td_lcache_req_t *req = NULL;
107 : :
108 [ # # ][ # # ]: 0 : if (likely(cache->n_free))
109 : 0 : req = cache->free[--cache->n_free];
110 : :
111 : : return req;
112 : : }
113 : :
114 : : static void
115 : : lcache_free_request(td_lcache_t *cache, td_lcache_req_t *req)
116 : : {
117 [ # # ][ # # ]: 0 : BUG_ON(cache->n_free >= TD_LCACHE_MAX_REQ);
[ # # ]
118 : 0 : cache->free[cache->n_free++] = req;
119 : : }
120 : :
121 : : static void
122 : 0 : lcache_destroy_buffers(td_lcache_t *cache)
123 : : {
124 : : td_lcache_req_t *req;
125 : :
126 : : do {
127 : 0 : req = lcache_alloc_request(cache);
128 [ # # ]: 0 : if (req)
129 : 0 : munmap(req->buf, (size_t)TD_REQ_BUFFER_SIZE);
130 [ # # ]: 0 : } while (req);
131 : 0 : }
132 : :
133 : : static int
134 : 0 : lcache_create_buffers(td_lcache_t *cache)
135 : : {
136 : : int prot, flags, i, err;
137 : :
138 : 0 : prot = PROT_READ|PROT_WRITE;
139 : 0 : flags = MAP_ANONYMOUS|MAP_PRIVATE|MAP_LOCKED;
140 : :
141 : 0 : cache->n_free = 0;
142 : :
143 [ # # ]: 0 : for (i = 0; i < TD_LCACHE_MAX_REQ; i++) {
144 : 0 : td_lcache_req_t *req = &cache->reqv[i];
145 : :
146 : 0 : req->buf = mmap(NULL, (size_t)TD_REQ_BUFFER_SIZE, prot, flags, -1, 0);
147 [ # # ]: 0 : if (req->buf == MAP_FAILED) {
148 : 0 : req->buf = NULL;
149 : 0 : err = -errno;
150 : : goto fail;
151 : : }
152 : :
153 : : lcache_free_request(cache, req);
154 : : }
155 : :
156 : : return 0;
157 : :
158 : : fail:
159 : : EPRINTF("Buffer init failure: %d", err);
160 : 0 : lcache_destroy_buffers(cache);
161 : 0 : return err;
162 : : }
163 : :
164 : : static int
165 : 0 : lcache_close(td_driver_t *driver)
166 : : {
167 : 0 : td_lcache_t *cache = driver->data;
168 : :
169 : 0 : lcache_destroy_buffers(cache);
170 : :
171 : 0 : free(cache->name);
172 : :
173 : 0 : return 0;
174 : : }
175 : :
176 : : static int
177 : 0 : lcache_open(td_driver_t *driver, const char *name,
178 : : struct td_vbd_encryption *encryption, td_flag_t flags)
179 : : {
180 : 0 : td_lcache_t *cache = driver->data;
181 : : int err;
182 : :
183 : 0 : err = tapdisk_namedup(&cache->name, (char *)name);
184 [ # # ]: 0 : if (err)
185 : : goto fail;
186 : :
187 : 0 : err = lcache_create_buffers(cache);
188 [ # # ]: 0 : if (err)
189 : : goto fail;
190 : :
191 : 0 : timerclear(&cache->ts);
192 : 0 : cache->wr_en = 1;
193 : :
194 : 0 : return 0;
195 : :
196 : : fail:
197 : 0 : lcache_close(driver);
198 : 0 : return err;
199 : : }
200 : :
201 : : /*
202 : : * NB. lcache->{wr_en,ts}: test free space in the caching SR before
203 : : * attempting to store our reads. VHD block allocation writes on Ext3
204 : : * have the nasty property of blocking excessively after running out
205 : : * of space. We therefore enable/disable ourselves at a 1/s
206 : : * granularity, querying free space through statfs beforehand.
207 : : */
208 : :
209 : : static long
210 : 0 : lcache_fs_bfree(const td_lcache_t *cache, long *bsize)
211 : : {
212 : : struct statfs fst;
213 : : int err;
214 : :
215 : 0 : err = statfs(cache->name, &fst);
216 [ # # ]: 0 : if (err)
217 : 0 : return err;
218 : :
219 [ # # ]: 0 : if (likely(bsize))
220 : 0 : *bsize = fst.f_bsize;
221 : :
222 : 0 : return MIN(fst.f_bfree, LONG_MAX);
223 : : }
224 : :
225 : : static int
226 : 0 : __lcache_wr_enabled(const td_lcache_t *cache)
227 : : {
228 : 0 : long threshold = 2<<20; /* B */
229 : 0 : long bfree, bsz = 1;
230 : : int enable;
231 : :
232 : 0 : bfree = lcache_fs_bfree(cache, &bsz);
233 : 0 : enable = bfree > threshold / bsz;
234 : :
235 : 0 : return enable;
236 : : }
237 : :
238 : : static int
239 : 0 : lcache_wr_enabled(td_lcache_t *cache)
240 : : {
241 : 0 : const int timeout = 1; /* s */
242 : : struct timeval now, delta;
243 : :
244 : 0 : gettimeofday(&now, NULL);
245 [ # # ]: 0 : timersub(&now, &cache->ts, &delta);
246 : :
247 [ # # ]: 0 : if (delta.tv_sec >= timeout) {
248 : 0 : cache->wr_en = __lcache_wr_enabled(cache);
249 : 0 : cache->ts = now;
250 : : }
251 : :
252 : 0 : return cache->wr_en;
253 : : }
254 : :
255 : : static void
256 : 0 : __lcache_write_cb(td_vbd_request_t *vreq, int error,
257 : : void *token, int final)
258 : : {
259 : 0 : td_lcache_req_t *req = container_of(vreq, td_lcache_req_t, vreq);
260 : 0 : td_lcache_t *cache = token;
261 : :
262 [ # # ]: 0 : if (error == -ENOSPC)
263 : 0 : cache->wr_en = 0;
264 : :
265 : : lcache_free_request(cache, req);
266 : 0 : }
267 : :
268 : : static void
269 : 0 : lcache_store_read(td_lcache_t *cache, td_lcache_req_t *req)
270 : : {
271 : : td_vbd_request_t *vreq;
272 : : struct td_iovec *iov;
273 : : td_vbd_t *vbd;
274 : : int err;
275 : :
276 : 0 : iov = &req->iov;
277 : 0 : iov->base = req->buf;
278 : 0 : iov->secs = req->treq.secs;
279 : :
280 : 0 : vreq = &req->vreq;
281 : 0 : vreq->op = TD_OP_WRITE;
282 : 0 : vreq->sec = req->treq.sec;
283 : 0 : vreq->iov = iov;
284 : 0 : vreq->iovcnt = 1;
285 : 0 : vreq->cb = __lcache_write_cb;
286 : 0 : vreq->token = cache;
287 : 0 : vreq->skip_mirror = true;
288 : :
289 : 0 : vbd = req->treq.vreq->vbd;
290 : :
291 : 0 : err = tapdisk_vbd_queue_request(vbd, vreq);
292 [ # # ]: 0 : BUG_ON(err);
293 : 0 : }
294 : :
295 : : static void
296 : 0 : lcache_complete_read(td_lcache_t *cache, td_lcache_req_t *req)
297 : : {
298 [ # # ]: 0 : if (likely(!req->err)) {
299 : 0 : size_t sz = (size_t)req->treq.secs << SECTOR_SHIFT;
300 : 0 : memcpy(req->treq.buf, req->buf, sz);
301 : : }
302 : :
303 : 0 : td_complete_request(req->treq, req->err);
304 : :
305 [ # # ][ # # ]: 0 : if (unlikely(req->err) || !lcache_wr_enabled(cache)) {
306 : : lcache_free_request(cache, req);
307 : 0 : return;
308 : : }
309 : :
310 : 0 : lcache_store_read(cache, req);
311 : : }
312 : :
313 : : static void
314 : 0 : __lcache_read_cb(td_request_t treq, int err)
315 : : {
316 : 0 : td_lcache_req_t *req = treq.cb_data;
317 : 0 : td_lcache_t *cache = req->cache;
318 : :
319 [ # # ]: 0 : BUG_ON(req->secs < treq.secs);
320 : 0 : req->secs -= treq.secs;
321 [ # # ]: 0 : req->err = req->err ? : err;
322 : :
323 [ # # ]: 0 : if (!req->secs)
324 : 0 : lcache_complete_read(cache, req);
325 : 0 : }
326 : :
327 : : static void
328 : 0 : lcache_queue_read(td_driver_t *driver, td_request_t treq)
329 : : {
330 : 0 : td_lcache_t *cache = driver->data;
331 : : td_request_t clone;
332 : : td_lcache_req_t *req;
333 : :
334 : 0 : req = lcache_alloc_request(cache);
335 [ # # ]: 0 : if (!req) {
336 : 0 : td_complete_request(treq, -EBUSY);
337 : 0 : return;
338 : : }
339 : :
340 : 0 : req->treq = treq;
341 : 0 : req->cache = cache;
342 : :
343 : 0 : req->secs = req->treq.secs;
344 : 0 : req->err = 0;
345 : :
346 : 0 : clone = treq;
347 : 0 : clone.buf = req->buf;
348 : 0 : clone.cb = __lcache_read_cb;
349 : 0 : clone.cb_data = req;
350 : :
351 : 0 : td_forward_request(clone);
352 : : }
353 : :
354 : : static int
355 : 0 : lcache_get_parent_id(td_driver_t *driver, td_disk_id_t *id)
356 : : {
357 : 0 : return -EINVAL;
358 : : }
359 : :
360 : : static int
361 : 0 : lcache_validate_parent(td_driver_t *driver,
362 : : td_driver_t *pdriver, td_flag_t flags)
363 : : {
364 : : /* Check types for both */
365 [ # # ][ # # ]: 0 : if (driver->type != DISK_TYPE_LCACHE ||
366 : 0 : pdriver->type != DISK_TYPE_VHD) {
367 : 0 : EPRINTF("Unexpected driver types. %s:%d, parent %s:%d",
368 : : tapdisk_disk_types[driver->type]->name,
369 : : driver->type,
370 : : tapdisk_disk_types[pdriver->type]->name,
371 : : pdriver->type);
372 : 0 : return -EINVAL;
373 : : }
374 : :
375 : : /*
376 : : Checking names needs to be more selective if required
377 : : if (strcmp(driver->name, pdriver->name))
378 : : return -EINVAL;
379 : : */
380 : :
381 : : return 0;
382 : : }
383 : :
384 : : struct tap_disk tapdisk_lcache = {
385 : : .disk_type = "tapdisk_lcache",
386 : : .flags = 0,
387 : : .private_data_size = sizeof(td_lcache_t),
388 : : .td_open = lcache_open,
389 : : .td_close = lcache_close,
390 : : .td_queue_read = lcache_queue_read,
391 : : .td_get_parent_id = lcache_get_parent_id,
392 : : .td_validate_parent = lcache_validate_parent,
393 : : };
|