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 : : #include <stdlib.h>
32 : : #include <errno.h>
33 : : #include <sys/mman.h>
34 : : #include <unistd.h>
35 : : #include <stdio.h>
36 : : #include <libgen.h>
37 : : #include <zlib.h>
38 : :
39 : : #include "blktap-xenif.h"
40 : : #include "debug.h"
41 : : #include "blktap3.h"
42 : : #include "tapdisk.h"
43 : : #include "tapdisk-log.h"
44 : : #include "util.h"
45 : : #include "tapdisk-server.h"
46 : : #include "tapdisk-metrics.h"
47 : : #include "timeout-math.h"
48 : :
49 : : #include "td-blkif.h"
50 : : #include "td-ctx.h"
51 : : #include "td-req.h"
52 : :
53 : : struct td_xenblkif *
54 : 0 : tapdisk_xenblkif_find(const domid_t domid, const int devid)
55 : : {
56 : 0 : struct td_xenblkif *blkif = NULL;
57 : : struct td_xenio_ctx *ctx;
58 : :
59 [ # # ]: 0 : tapdisk_xenio_for_each_ctx(ctx) {
60 [ # # ][ # # ]: 0 : tapdisk_xenio_ctx_find_blkif(ctx, blkif,
[ # # ][ # # ]
[ # # ]
61 : : blkif->domid == domid &&
62 : : blkif->devid == devid);
63 [ # # ]: 0 : if (blkif)
64 : : return blkif;
65 : : }
66 : :
67 : : return NULL;
68 : : }
69 : :
70 : :
71 : : /**
72 : : * Returns 0 on success, -errno on failure.
73 : : */
74 : : static int
75 : 0 : tapdisk_xenblkif_stats_destroy(struct td_xenblkif *blkif)
76 : : {
77 : : int err;
78 : :
79 : 0 : err = shm_destroy(&blkif->xenvbd_stats.io_ring);
80 [ # # ]: 0 : if (unlikely(err))
81 : : goto out;
82 : 0 : free(blkif->xenvbd_stats.io_ring.path);
83 : 0 : blkif->xenvbd_stats.io_ring.path = NULL;
84 : :
85 : : /*
86 : : * blkif->stats.xenvbd was initialised to blkif->xenvbd_stats.stats.mem
87 : : * in tapdisk_xenblkif_stats_create(). That address will be unmapped
88 : : * by the call to shm_destroy(), and an error return does not mean it
89 : : * wasn't, so we must unconditionally NULL blkif->stats.xenvbd here.
90 : : */
91 : 0 : blkif->stats.xenvbd = NULL;
92 : 0 : err = shm_destroy(&blkif->xenvbd_stats.stats);
93 [ # # ]: 0 : if (unlikely(err))
94 : : goto out;
95 : 0 : free(blkif->xenvbd_stats.stats.path);
96 : 0 : blkif->xenvbd_stats.stats.path = NULL;
97 : :
98 [ # # ]: 0 : if (likely(blkif->xenvbd_stats.root)) {
99 : 0 : err = rmdir(blkif->xenvbd_stats.root);
100 [ # # ][ # # ]: 0 : if (unlikely(err && errno != ENOENT)) {
101 : 0 : err = errno;
102 : 0 : EPRINTF("failed to remove %s: %s\n",
103 : : blkif->xenvbd_stats.root, strerror(err));
104 : : goto out;
105 : : }
106 : 0 : err = 0;
107 : :
108 : 0 : free(blkif->xenvbd_stats.root);
109 : 0 : blkif->xenvbd_stats.root = NULL;
110 : : }
111 : : out:
112 : 0 : return -err;
113 : : }
114 : :
115 : :
116 : : /*
117 : : * TODO provide ring stats in raw format (the same way I/O stats are provided).
118 : : * xen-ringwatch will have to be modified accordingly.
119 : : */
120 : : static int
121 : 0 : tapdisk_xenblkif_stats_create(struct td_xenblkif *blkif)
122 : : {
123 : 0 : int err = 0, len;
124 : 0 : char *_path = NULL;
125 : :
126 : 0 : len = asprintf(&blkif->xenvbd_stats.root, "/dev/shm/vbd3-%d-%d",
127 : : blkif->domid, blkif->devid);
128 [ # # ]: 0 : if (unlikely(len == -1)) {
129 : 0 : err = errno;
130 : 0 : blkif->xenvbd_stats.root = NULL;
131 : 0 : goto out;
132 : : }
133 : :
134 : 0 : err = mkdir(blkif->xenvbd_stats.root, S_IRUSR | S_IWUSR);
135 [ # # ]: 0 : if (unlikely(err)) {
136 : 0 : err = errno;
137 [ # # ]: 0 : if (err != EEXIST) {
138 : 0 : EPRINTF("failed to create %s: %s\n",
139 : : blkif->xenvbd_stats.root, strerror(err));
140 : : goto out;
141 : : }
142 : : err = 0;
143 : : }
144 : :
145 : 0 : len = asprintf(&blkif->xenvbd_stats.io_ring.path, "%s/io_ring~",
146 : : blkif->xenvbd_stats.root);
147 [ # # ]: 0 : if (unlikely(len == -1)) {
148 : 0 : err = errno;
149 : 0 : blkif->xenvbd_stats.io_ring.path = NULL;
150 : 0 : goto out;
151 : : }
152 : 0 : blkif->xenvbd_stats.io_ring.size = PAGE_SIZE;
153 : 0 : err = shm_create(&blkif->xenvbd_stats.io_ring);
154 [ # # ]: 0 : if (unlikely(err)) {
155 : 0 : err = errno;
156 : 0 : EPRINTF("failed to create shm ring stats file: %s\n", strerror(err));
157 : : goto out;
158 : : }
159 : :
160 : 0 : err = asprintf(&blkif->xenvbd_stats.stats.path, "%s/statistics",
161 : : blkif->xenvbd_stats.root);
162 [ # # ]: 0 : if (unlikely(err == -1)) {
163 : 0 : err = errno;
164 : 0 : blkif->xenvbd_stats.stats.path = NULL;
165 : 0 : goto out;
166 : : }
167 : 0 : blkif->xenvbd_stats.stats.size = PAGE_SIZE;
168 : 0 : err = shm_create(&blkif->xenvbd_stats.stats);
169 [ # # ]: 0 : if (unlikely(err))
170 : : goto out;
171 : :
172 : 0 : blkif->xenvbd_stats.last = 0;
173 : :
174 : 0 : blkif->stats.xenvbd = blkif->xenvbd_stats.stats.mem;
175 : :
176 [ # # ]: 0 : if (tapdisk_server_mem_mode()) {
177 : 0 : td_flag_set(blkif->stats.xenvbd->flags, BT3_LOW_MEMORY_MODE);
178 : 0 : td_flag_set(blkif->vbd_stats.stats->flags, BT3_LOW_MEMORY_MODE);
179 : : }
180 : :
181 : 0 : err = tapdisk_xenblkif_ring_stats_update(blkif);
182 [ # # ]: 0 : if (unlikely(err)) {
183 : 0 : EPRINTF("failed to generate shared I/O ring stats: %s\n",
184 : : strerror(-err));
185 : : goto out;
186 : : }
187 : :
188 : 0 : _path = strndup(blkif->xenvbd_stats.io_ring.path, len - 1);
189 [ # # ]: 0 : if (unlikely(!_path)) {
190 : 0 : err = errno;
191 : 0 : goto out;
192 : : }
193 : :
194 : 0 : err = rename(blkif->xenvbd_stats.io_ring.path, _path);
195 [ # # ]: 0 : if (unlikely(err)) {
196 : 0 : err = errno;
197 : 0 : goto out;
198 : : }
199 : :
200 : 0 : free(blkif->xenvbd_stats.io_ring.path);
201 : 0 : blkif->xenvbd_stats.io_ring.path = _path;
202 : 0 : _path = NULL;
203 : : out:
204 : 0 : free(_path);
205 [ # # ]: 0 : if (err) {
206 : 0 : int err2 = tapdisk_xenblkif_stats_destroy(blkif);
207 [ # # ]: 0 : if (err2)
208 : 0 : EPRINTF("failed to clean up failed stats file: "
209 : : "%s (error ignored)\n", strerror(-err2));
210 : : }
211 : 0 : return -err;
212 : : }
213 : :
214 : :
215 : : int
216 : 0 : tapdisk_xenblkif_destroy(struct td_xenblkif * blkif)
217 : : {
218 : : int err;
219 : :
220 [ # # ]: 0 : ASSERT(blkif);
221 : :
222 [ # # ]: 0 : if (tapdisk_xenblkif_chkrng_event_id(blkif) >= 0) {
223 : 0 : tapdisk_server_unregister_event(
224 : : tapdisk_xenblkif_chkrng_event_id(blkif));
225 : 0 : blkif->chkrng_event = -1;
226 : : }
227 : :
228 [ # # ]: 0 : if (tapdisk_xenblkif_stoppolling_event_id(blkif) >= 0) {
229 : 0 : tapdisk_server_unregister_event(
230 : : tapdisk_xenblkif_stoppolling_event_id(blkif));
231 : 0 : blkif->stoppolling_event = -1;
232 : : }
233 : :
234 : 0 : tapdisk_xenblkif_reqs_free(blkif);
235 : :
236 [ # # ]: 0 : if (blkif->ctx) {
237 [ # # ]: 0 : if (blkif->port >= 0)
238 : 0 : xenevtchn_unbind(blkif->ctx->xce_handle, blkif->port);
239 : :
240 [ # # ]: 0 : if (blkif->rings.common.sring) {
241 : 0 : err = xengnttab_unmap(blkif->ctx->xcg_handle,
242 : : blkif->rings.common.sring, blkif->ring_n_pages);
243 [ # # ]: 0 : if (unlikely(err)) {
244 : 0 : err = errno;
245 : 0 : EPRINTF("failed to unmap ring page %p (%d pages): %s "
246 : : "(error ignored)\n",
247 : : blkif->rings.common.sring, blkif->ring_n_pages,
248 : : strerror(err));
249 : 0 : err = 0;
250 : : }
251 : : }
252 : :
253 : 0 : list_del(&blkif->entry_ctx);
254 : 0 : list_del(&blkif->entry);
255 : 0 : tapdisk_xenio_ctx_put(blkif->ctx);
256 : : }
257 : 0 : err = td_metrics_vbd_stop(&blkif->vbd_stats);
258 [ # # ]: 0 : if (unlikely(err))
259 : 0 : EPRINTF("failed to destroy blkfront stats file: %s\n", strerror(-err));
260 : :
261 : 0 : err = tapdisk_xenblkif_stats_destroy(blkif);
262 [ # # ]: 0 : if (unlikely(err)) {
263 : 0 : EPRINTF("failed to clean up ring stats file: %s (error ignored)\n",
264 : : strerror(-err));
265 : 0 : err = 0;
266 : : }
267 : :
268 : 0 : free(blkif);
269 : :
270 : 0 : return err;
271 : : }
272 : :
273 : :
274 : : int
275 : 0 : tapdisk_xenblkif_reqs_pending(const struct td_xenblkif * const blkif)
276 : : {
277 [ # # ]: 0 : ASSERT(blkif);
278 : :
279 : 0 : return blkif->ring_size - blkif->n_reqs_free;
280 : : }
281 : :
282 : :
283 : : int
284 : 0 : tapdisk_xenblkif_disconnect(const domid_t domid, const int devid)
285 : : {
286 : : int err;
287 : : struct td_xenblkif *blkif;
288 : :
289 : 0 : blkif = tapdisk_xenblkif_find(domid, devid);
290 [ # # ]: 0 : if (!blkif)
291 : : return -ENODEV;
292 : :
293 [ # # ]: 0 : if (tapdisk_xenblkif_reqs_pending(blkif)) {
294 : 0 : RING_DEBUG(blkif, "disconnect from ring with %d pending requests\n",
295 : : blkif->ring_size - blkif->n_reqs_free);
296 [ # # ]: 0 : if (td_flag_test(blkif->vbd->state, TD_VBD_PAUSED))
297 : 0 : RING_ERR(blkif, "disconnect from ring with %d pending requests "
298 : : "and the VBD paused\n",
299 : : blkif->ring_size - blkif->n_reqs_free);
300 : 0 : list_move(&blkif->entry, &blkif->vbd->dead_rings);
301 : 0 : blkif->dead = true;
302 [ # # ][ # # ]: 0 : if (blkif->ctx && blkif->port >= 0) {
303 : 0 : xenevtchn_unbind(blkif->ctx->xce_handle, blkif->port);
304 : 0 : blkif->port = -1;
305 : : }
306 : :
307 : 0 : err = td_metrics_vbd_stop(&blkif->vbd_stats);
308 [ # # ]: 0 : if (unlikely(err))
309 : 0 : EPRINTF("failed to destroy blkfront stats file: %s\n", strerror(-err));
310 : :
311 : 0 : err = tapdisk_xenblkif_stats_destroy(blkif);
312 [ # # ]: 0 : if (unlikely(err))
313 : 0 : EPRINTF("failed to clean up ring stats file: %s (error ignored)\n",
314 : : strerror(-err));
315 : :
316 : : /*
317 : : * FIXME shall we unmap the ring or will that lead to some fatal error
318 : : * in tapdisk? IIUC if we don't unmap it we'll get errors during grant
319 : : * copy.
320 : : */
321 : : return 0;
322 : : } else
323 : 0 : return tapdisk_xenblkif_destroy(blkif);
324 : : }
325 : :
326 : :
327 : : void
328 : 0 : tapdisk_xenblkif_sched_stoppolling(const struct td_xenblkif *blkif)
329 : : {
330 : : int err;
331 : :
332 [ # # ]: 0 : ASSERT(blkif);
333 : :
334 : 0 : err = tapdisk_server_event_set_timeout(
335 : 0 : tapdisk_xenblkif_stoppolling_event_id(blkif), TV_USECS(blkif->poll_duration));
336 [ # # ]: 0 : ASSERT(!err);
337 : 0 : }
338 : :
339 : : void
340 : 0 : tapdisk_xenblkif_unsched_stoppolling(const struct td_xenblkif *blkif)
341 : : {
342 : : int err;
343 : :
344 [ # # ]: 0 : ASSERT(blkif);
345 : :
346 : 0 : err = tapdisk_server_event_set_timeout(
347 : 0 : tapdisk_xenblkif_stoppolling_event_id(blkif), TV_INF);
348 [ # # ]: 0 : ASSERT(!err);
349 : 0 : }
350 : :
351 : :
352 : : void
353 : 0 : tapdisk_start_polling(struct td_xenblkif *blkif)
354 : : {
355 [ # # ]: 0 : ASSERT(blkif);
356 : :
357 : : /* Only enter polling if the CPU utilisation is not too high */
358 [ # # ]: 0 : if (tapdisk_server_system_idle_cpu() > (float)blkif->poll_idle_threshold) {
359 : 0 : blkif->in_polling = true;
360 : :
361 : : /* Start checking the ring immediately */
362 : 0 : tapdisk_xenblkif_sched_chkrng(blkif);
363 : :
364 : : /* Schedule the future 'stop polling' event */
365 : 0 : tapdisk_xenblkif_sched_stoppolling(blkif);
366 : :
367 : 0 : tapdisk_server_mask_event(tapdisk_xenblkif_evtchn_event_id(blkif), 1);
368 : : }
369 : 0 : }
370 : :
371 : : static inline void
372 : 0 : tapdisk_xenblkif_cb_stoppolling(event_id_t id __attribute__((unused)),
373 : : char mode __attribute__((unused)), void *private)
374 : : {
375 : 0 : struct td_xenblkif *blkif = private;
376 : :
377 [ # # ]: 0 : ASSERT(blkif);
378 : :
379 : : /* Process the ring one final time, setting the event counter */
380 [ # # ]: 0 : if (!tapdisk_xenio_ctx_process_ring(blkif, blkif->ctx, 1)) {
381 : : /* If there were no new requests this time, then stop polling */
382 : 0 : blkif->in_polling = false;
383 : :
384 : : /* Stop obsessively checking the ring */
385 : 0 : tapdisk_xenblkif_unsched_chkrng(blkif);
386 : :
387 : : /* Make the 'stop polling' event not fire again */
388 : 0 : tapdisk_xenblkif_unsched_stoppolling(blkif);
389 : :
390 : 0 : tapdisk_server_mask_event(tapdisk_xenblkif_evtchn_event_id(blkif), 0);
391 : : }
392 : 0 : }
393 : :
394 : : void
395 : 0 : tapdisk_xenblkif_sched_chkrng(const struct td_xenblkif *blkif)
396 : : {
397 : : int err;
398 : :
399 [ # # ]: 0 : ASSERT(blkif);
400 : :
401 : 0 : err = tapdisk_server_event_set_timeout(
402 : 0 : tapdisk_xenblkif_chkrng_event_id(blkif), TV_ZERO);
403 [ # # ]: 0 : ASSERT(!err);
404 : 0 : }
405 : :
406 : : void
407 : 0 : tapdisk_xenblkif_unsched_chkrng(const struct td_xenblkif *blkif)
408 : : {
409 : : int err;
410 : :
411 [ # # ]: 0 : ASSERT(blkif);
412 : :
413 : 0 : err = tapdisk_server_event_set_timeout(
414 : 0 : tapdisk_xenblkif_chkrng_event_id(blkif), TV_INF);
415 [ # # ]: 0 : ASSERT(!err);
416 : 0 : }
417 : :
418 : : static inline void
419 : 0 : tapdisk_xenblkif_cb_chkrng(event_id_t id __attribute__((unused)),
420 : : char mode __attribute__((unused)), void *private)
421 : : {
422 : 0 : struct td_xenblkif *blkif = private;
423 : :
424 [ # # ]: 0 : ASSERT(blkif);
425 : :
426 : : /*
427 : : * If we are polling, process the ring without setting the event counter.
428 : : * If we are not polling, unschedule this event, process the ring and set
429 : : * the event counter.
430 : : */
431 : :
432 [ # # ]: 0 : if (!blkif->in_polling)
433 : 0 : tapdisk_xenblkif_unsched_chkrng(blkif);
434 : :
435 : 0 : tapdisk_xenio_ctx_process_ring(blkif, blkif->ctx, !blkif->in_polling);
436 : 0 : }
437 : :
438 : :
439 : : int
440 : 0 : tapdisk_xenblkif_connect(domid_t domid, int devid, const grant_ref_t * grefs,
441 : : int order, evtchn_port_t port, int proto, int poll_duration,
442 : : int poll_idle_threshold, const char *pool, td_vbd_t * vbd)
443 : : {
444 : 0 : struct td_xenblkif *td_blkif = NULL; /* TODO rename to blkif */
445 : : struct td_xenio_ctx *td_ctx;
446 : : int err;
447 : : unsigned int i;
448 : : void *sring;
449 : : size_t sz;
450 : :
451 [ # # ]: 0 : ASSERT(grefs);
452 [ # # ]: 0 : ASSERT(vbd);
453 : :
454 : : /*
455 : : * Already connected?
456 : : */
457 [ # # ]: 0 : if (tapdisk_xenblkif_find(domid, devid)) {
458 : : /* TODO log error */
459 : 0 : return -EALREADY;
460 : : }
461 : :
462 : 0 : err = tapdisk_xenio_ctx_get(pool, &td_ctx);
463 [ # # ]: 0 : if (err) {
464 : : /* TODO log error */
465 : : goto fail;
466 : : }
467 : :
468 : 0 : td_blkif = calloc(1, sizeof(*td_blkif));
469 [ # # ]: 0 : if (!td_blkif) {
470 : : /* TODO log error */
471 : 0 : err = -errno;
472 : 0 : goto fail;
473 : : }
474 : :
475 : 0 : td_blkif->domid = domid;
476 : 0 : td_blkif->devid = devid;
477 : 0 : td_blkif->vbd = vbd;
478 : 0 : td_blkif->ctx = td_ctx;
479 : 0 : td_blkif->proto = proto;
480 : 0 : td_blkif->dead = false;
481 : 0 : td_blkif->chkrng_event = -1;
482 : 0 : td_blkif->stoppolling_event = -1;
483 : 0 : td_blkif->in_polling = false;
484 : 0 : td_blkif->poll_duration = poll_duration;
485 : 0 : td_blkif->poll_idle_threshold = poll_idle_threshold;
486 : 0 : td_blkif->barrier.msg = NULL;
487 : 0 : td_blkif->barrier.io_done = false;
488 : 0 : td_blkif->barrier.io_err = 0;
489 : :
490 : 0 : td_blkif->xenvbd_stats.root = NULL;
491 : 0 : shm_init(&td_blkif->xenvbd_stats.io_ring);
492 : 0 : shm_init(&td_blkif->xenvbd_stats.stats);
493 : :
494 : 0 : memset(&td_blkif->stats, 0, sizeof(td_blkif->stats));
495 : :
496 : 0 : INIT_LIST_HEAD(&td_blkif->entry_ctx);
497 : 0 : INIT_LIST_HEAD(&td_blkif->entry);
498 : :
499 : : /*
500 : : * Create the shared ring.
501 : : */
502 : 0 : td_blkif->ring_n_pages = 1 << order;
503 [ # # ]: 0 : if (td_blkif->ring_n_pages > ARRAY_SIZE(td_blkif->ring_ref)) {
504 : 0 : RING_ERR(td_blkif, "too many pages (%u), max %zu\n",
505 : : td_blkif->ring_n_pages, ARRAY_SIZE(td_blkif->ring_ref));
506 : : err = -EINVAL;
507 : : goto fail;
508 : : }
509 : :
510 : : /*
511 : : * TODO Why don't we just keep a copy of the array's address? There should
512 : : * be a reason for copying the addresses of the pages, figure out why.
513 : : * TODO Why do we even store it in the td_blkif in the first place?
514 : : */
515 [ # # ]: 0 : for (i = 0; i < td_blkif->ring_n_pages; i++)
516 : 0 : td_blkif->ring_ref[i] = grefs[i];
517 : :
518 : : /*
519 : : * Map the grant references that will be holding the request descriptors.
520 : : */
521 : 0 : sring = xengnttab_map_domain_grant_refs(td_blkif->ctx->xcg_handle,
522 : 0 : td_blkif->ring_n_pages, td_blkif->domid, td_blkif->ring_ref,
523 : : PROT_READ | PROT_WRITE);
524 [ # # ]: 0 : if (!sring) {
525 : 0 : err = -errno;
526 : 0 : RING_ERR(td_blkif, "failed to map domain's grant references: %s\n",
527 : : strerror(-err));
528 : : goto fail;
529 : : }
530 : :
531 : : /*
532 : : * Size of the ring, in bytes.
533 : : */
534 : 0 : sz = (size_t)PAGE_SIZE << order;
535 : :
536 : : /*
537 : : * Initialize the mapped address into the shared ring.
538 : : *
539 : : * TODO Check for protocol support in the beginning of this function.
540 : : */
541 [ # # # # ]: 0 : switch (td_blkif->proto) {
542 : : case BLKIF_PROTOCOL_NATIVE:
543 : : {
544 : 0 : blkif_sring_t *__sring = sring;
545 [ # # ][ # # ]: 0 : BACK_RING_INIT(&td_blkif->rings.native, __sring, sz);
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
546 : 0 : break;
547 : : }
548 : : case BLKIF_PROTOCOL_X86_32:
549 : : {
550 : 0 : blkif_x86_32_sring_t *__sring = sring;
551 [ # # ][ # # ]: 0 : BACK_RING_INIT(&td_blkif->rings.x86_32, __sring, sz);
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
552 : 0 : break;
553 : : }
554 : : case BLKIF_PROTOCOL_X86_64:
555 : : {
556 : 0 : blkif_x86_64_sring_t *__sring = sring;
557 [ # # ][ # # ]: 0 : BACK_RING_INIT(&td_blkif->rings.x86_64, __sring, sz);
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
558 : 0 : break;
559 : : }
560 : : default:
561 : 0 : RING_ERR(td_blkif, "unsupported protocol 0x%x\n", td_blkif->proto);
562 : : err = -EPROTONOSUPPORT;
563 : : goto fail;
564 : : }
565 : :
566 : : /*
567 : : * Bind to the remote port.
568 : : * TODO elaborate
569 : : */
570 : 0 : td_blkif->port = xenevtchn_bind_interdomain(td_blkif->ctx->xce_handle,
571 : 0 : td_blkif->domid, port);
572 [ # # ]: 0 : if (td_blkif->port == -1) {
573 : 0 : err = -errno;
574 : 0 : RING_ERR(td_blkif, "failed to bind to event channel port %d: %s\n",
575 : : port, strerror(-err));
576 : : goto fail;
577 : : }
578 : :
579 : 0 : err = tapdisk_xenblkif_reqs_init(td_blkif);
580 [ # # ]: 0 : if (err) {
581 : : /* TODO log error */
582 : : goto fail;
583 : : }
584 : :
585 : 0 : td_blkif->chkrng_event = tapdisk_server_register_event(
586 : 0 : SCHEDULER_POLL_TIMEOUT, -1, TV_INF,
587 : : tapdisk_xenblkif_cb_chkrng, td_blkif);
588 [ # # ]: 0 : if (unlikely(td_blkif->chkrng_event < 0)) {
589 : 0 : err = td_blkif->chkrng_event;
590 : 0 : RING_ERR(td_blkif, "failed to register event: %s\n", strerror(-err));
591 : : goto fail;
592 : : }
593 : :
594 : 0 : err = td_metrics_vbd_start(td_blkif->domid, td_blkif->devid, &td_blkif->vbd_stats);
595 [ # # ]: 0 : if (unlikely(err))
596 : : goto fail;
597 : :
598 : 0 : td_blkif->stoppolling_event = tapdisk_server_register_event(
599 : 0 : SCHEDULER_POLL_TIMEOUT, -1, TV_INF,
600 : : tapdisk_xenblkif_cb_stoppolling, td_blkif);
601 [ # # ]: 0 : if (unlikely(td_blkif->stoppolling_event < 0)) {
602 : 0 : err = td_blkif->stoppolling_event;
603 : 0 : RING_ERR(td_blkif, "failed to register event: %s\n", strerror(-err));
604 : : goto fail;
605 : : }
606 : :
607 : 0 : err = tapdisk_xenblkif_stats_create(td_blkif);
608 [ # # ]: 0 : if (unlikely(err))
609 : : goto fail;
610 : :
611 : 0 : list_add_tail(&td_blkif->entry, &vbd->rings);
612 : 0 : list_add_tail(&td_blkif->entry_ctx, &td_ctx->blkifs);
613 : :
614 : : DPRINTF("ring %p connected\n", td_blkif);
615 : :
616 : : return 0;
617 : :
618 : : fail:
619 [ # # ]: 0 : if (td_blkif) {
620 : 0 : int err2 = tapdisk_xenblkif_destroy(td_blkif);
621 [ # # ]: 0 : if (err2)
622 : 0 : EPRINTF("failed to destroy the block interface: %s "
623 : : "(error ignored)\n", strerror(-err2));
624 : : }
625 : :
626 : 0 : return err;
627 : : }
628 : :
629 : :
630 : : event_id_t
631 : 0 : tapdisk_xenblkif_evtchn_event_id(const struct td_xenblkif *blkif)
632 : : {
633 : 0 : return blkif->ctx->ring_event;
634 : : }
635 : :
636 : :
637 : : event_id_t
638 : 0 : tapdisk_xenblkif_chkrng_event_id(const struct td_xenblkif *blkif)
639 : : {
640 : 0 : return blkif->chkrng_event;
641 : : }
642 : :
643 : :
644 : : event_id_t
645 : 0 : tapdisk_xenblkif_stoppolling_event_id(const struct td_xenblkif *blkif)
646 : : {
647 : 0 : return blkif->stoppolling_event;
648 : : }
649 : :
650 : :
651 : : int
652 : 0 : tapdisk_xenblkif_ring_stats_update(struct td_xenblkif *blkif)
653 : : {
654 : : time_t t;
655 : 0 : int err = 0, len;
656 : 0 : struct blkif_common_back_ring *ring = NULL;
657 : 0 : uLong *chksum = NULL;
658 : :
659 [ # # ]: 0 : if (!blkif)
660 : : return 0;
661 : :
662 [ # # ]: 0 : if (unlikely(blkif->dead))
663 : : return 0;
664 : :
665 : 0 : ring = &blkif->rings.common;
666 [ # # ]: 0 : if (!ring->sring)
667 : : return 0;
668 : :
669 [ # # ]: 0 : ASSERT(blkif->xenvbd_stats.io_ring.mem);
670 : :
671 : : /*
672 : : * Update the ring stats once every five seconds.
673 : : */
674 : 0 : t = time(NULL);
675 [ # # ]: 0 : if (t - blkif->xenvbd_stats.last < 5)
676 : : return 0;
677 : 0 : blkif->xenvbd_stats.last = t;
678 : :
679 : 0 : len = snprintf(blkif->xenvbd_stats.io_ring.mem + sizeof(*chksum),
680 : 0 : blkif->xenvbd_stats.io_ring.size,
681 : : "nr_ents %u\n"
682 : : "req prod %u cons %d event %u\n"
683 : : "rsp prod %u pvt %d event %u\n",
684 : : ring->nr_ents,
685 : : ring->sring->req_prod, ring->req_cons, ring->sring->req_event,
686 : 0 : ring->sring->rsp_prod, ring->rsp_prod_pvt, ring->sring->rsp_event);
687 [ # # ]: 0 : if (unlikely(len < 0))
688 : 0 : err = errno;
689 [ # # ]: 0 : else if (unlikely(len + sizeof(uLong) >= blkif->xenvbd_stats.io_ring.size))
690 : : err = ENOBUFS;
691 : : else {
692 : 0 : err = ftruncate(blkif->xenvbd_stats.io_ring.fd, len + sizeof(*chksum));
693 [ # # ]: 0 : if (unlikely(err)) {
694 : 0 : err = errno;
695 : 0 : EPRINTF("failed to truncate %s to %lu: %s\n",
696 : : blkif->xenvbd_stats.io_ring.path, len + sizeof(*chksum),
697 : : strerror(err));
698 : : }
699 : : }
700 : :
701 : 0 : chksum = blkif->xenvbd_stats.io_ring.mem;
702 : 0 : *chksum = crc32(0L, blkif->xenvbd_stats.io_ring.mem + sizeof(*chksum),
703 : : len);
704 : :
705 : 0 : return -err;
706 : : }
707 : :
708 : :
709 : : void
710 : 0 : tapdisk_xenblkif_suspend(struct td_xenblkif * const blkif)
711 : : {
712 [ # # ]: 0 : ASSERT(blkif);
713 : :
714 : 0 : tapdisk_server_mask_event(tapdisk_xenblkif_evtchn_event_id(blkif), 1);
715 : 0 : tapdisk_server_mask_event(tapdisk_xenblkif_chkrng_event_id(blkif), 1);
716 : 0 : }
717 : :
718 : :
719 : : void
720 : 0 : tapdisk_xenblkif_resume(struct td_xenblkif * const blkif)
721 : : {
722 [ # # ]: 0 : ASSERT(blkif);
723 : :
724 : 0 : tapdisk_server_mask_event(tapdisk_xenblkif_evtchn_event_id(blkif), 0);
725 : 0 : tapdisk_server_mask_event(tapdisk_xenblkif_chkrng_event_id(blkif), 0);
726 : 0 : }
727 : :
728 : :
729 : : bool
730 : 0 : tapdisk_xenblkif_barrier_should_complete(
731 : : const struct td_xenblkif * const blkif)
732 : : {
733 [ # # ]: 0 : ASSERT(blkif);
734 : :
735 [ # # ][ # # ]: 0 : return blkif->barrier.msg && 1 == tapdisk_xenblkif_reqs_pending(blkif) &&
[ # # ]
736 [ # # ]: 0 : (0 == blkif->barrier.msg->nr_segments || blkif->barrier.io_done);
737 : : }
|