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 <stdio.h>
33 : : #include <errno.h>
34 : : #include <syslog.h>
35 : : #include <inttypes.h>
36 : : #include <sys/mman.h>
37 : : #include <unistd.h>
38 : : #include <sys/ioctl.h>
39 : :
40 : : #ifdef __linux__
41 : : #include <linux/version.h>
42 : : #endif
43 : :
44 : : #include "blktap-xenif.h"
45 : : #include "debug.h"
46 : : #include "td-req.h"
47 : : #include "td-blkif.h"
48 : : #include "td-ctx.h"
49 : : #include "tapdisk-server.h"
50 : : #include "tapdisk-metrics.h"
51 : : #include "tapdisk-vbd.h"
52 : : #include "tapdisk-log.h"
53 : : #include "tapdisk.h"
54 : : #include "timeout-math.h"
55 : : #include "util.h"
56 : :
57 : : #ifdef DEBUG
58 : : #define BLKIF_MSG_POISON 0xdeadbeef
59 : : #endif
60 : :
61 : : #define ERR(blkif, fmt, args...) \
62 : : EPRINTF("%d/%d: "fmt, (blkif)->domid, (blkif)->devid, ##args);
63 : :
64 : : #define TD_REQS_BUFCACHE_EXPIRE 3 // time in seconds
65 : : #define TD_REQS_BUFCACHE_MIN 1 // buffers to always keep in the cache
66 : :
67 : : static void
68 : : td_xenblkif_bufcache_free(struct td_xenblkif * const blkif);
69 : : static inline void
70 : : td_xenblkif_bufcache_evt_unreg(struct td_xenblkif * const blkif);
71 : :
72 : : static void
73 : 0 : td_xenblkif_bufcache_event(event_id_t id, char mode, void *private)
74 : : {
75 : 0 : struct td_xenblkif *blkif = private;
76 : :
77 : 0 : td_xenblkif_bufcache_free(blkif);
78 : :
79 : 0 : td_xenblkif_bufcache_evt_unreg(blkif);
80 : 0 : }
81 : :
82 : : /**
83 : : * Unregister the event to expire the request buffer cache.
84 : : *
85 : : * @param blkif the block interface
86 : : */
87 : : static inline void
88 : 0 : td_xenblkif_bufcache_evt_unreg(struct td_xenblkif * const blkif)
89 : : {
90 [ # # ]: 0 : if (blkif->reqs_bufcache_evtid > 0){
91 : 0 : tapdisk_server_unregister_event(blkif->reqs_bufcache_evtid);
92 : : }
93 : 0 : blkif->reqs_bufcache_evtid = 0;
94 : : }
95 : :
96 : : /**
97 : : * Register the event to expire the request buffer cache.
98 : : *
99 : : * @param blkif the block interface
100 : : */
101 : : static inline void
102 : 0 : td_xenblkif_bufcache_evt_reg(struct td_xenblkif * const blkif)
103 : : {
104 : 0 : blkif->reqs_bufcache_evtid =
105 : 0 : tapdisk_server_register_event(SCHEDULER_POLL_TIMEOUT,
106 : : -1, /* dummy fd */
107 : 0 : TV_SECS(TD_REQS_BUFCACHE_EXPIRE),
108 : : td_xenblkif_bufcache_event,
109 : : blkif);
110 : 0 : }
111 : :
112 : : /**
113 : : * Free request buffer cache.
114 : : *
115 : : * @param blkif the block interface
116 : : */
117 : : static void
118 : 0 : td_xenblkif_bufcache_free(struct td_xenblkif * const blkif)
119 : : {
120 [ # # ]: 0 : ASSERT(blkif);
121 : :
122 [ # # ]: 0 : while (blkif->n_reqs_bufcache_free > TD_REQS_BUFCACHE_MIN){
123 : 0 : munmap(blkif->reqs_bufcache[--blkif->n_reqs_bufcache_free],
124 : : (size_t)BLKIF_MAX_BUFFER_SEGMENTS_PER_REQUEST << PAGE_SHIFT);
125 : : }
126 : 0 : }
127 : :
128 : : /**
129 : : * Get buffer for a request. From cache if available or newly allocated.
130 : : *
131 : : * @param blkif the block interface
132 : : */
133 : : static void *
134 : 0 : td_xenblkif_bufcache_get(struct td_xenblkif * const blkif)
135 : : {
136 : 0 : void *buf;
137 : :
138 [ # # ]: 0 : ASSERT(blkif);
139 : :
140 [ # # ]: 0 : if (!blkif->n_reqs_bufcache_free) {
141 : 0 : buf = mmap(NULL, (size_t)TD_REQ_BUFFER_SIZE,
142 : : PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
143 [ # # ]: 0 : if (unlikely(buf == MAP_FAILED))
144 : 0 : buf = NULL;
145 : : } else
146 : 0 : buf = blkif->reqs_bufcache[--blkif->n_reqs_bufcache_free];
147 : :
148 : : // If we just got a request, we cancel the cache expire timer
149 [ # # ]: 0 : td_xenblkif_bufcache_evt_unreg(blkif);
150 : :
151 : 0 : return buf;
152 : : }
153 : :
154 : : static void
155 : 0 : td_xenblkif_bufcache_put(struct td_xenblkif * const blkif, void *buf)
156 : : {
157 [ # # ]: 0 : ASSERT(blkif);
158 : :
159 [ # # ]: 0 : if (unlikely(!buf))
160 : : return;
161 : :
162 : : #ifdef DEBUG
163 : : {
164 : : int i;
165 : :
166 : : for (i = 0; i < blkif->n_reqs_bufcache_free; i++)
167 : : ASSERT(blkif->reqs_bufcache[i] != buf);
168 : : }
169 : : #endif
170 : :
171 : 0 : blkif->reqs_bufcache[blkif->n_reqs_bufcache_free++] = buf;
172 : :
173 : : // We only set the expire event when no requests are inflight
174 [ # # ]: 0 : if (blkif->n_reqs_free == blkif->ring_size)
175 : 0 : td_xenblkif_bufcache_evt_reg(blkif);
176 : :
177 : : }
178 : :
179 : : /**
180 : : * Puts the request back to the free list of this block interface.
181 : : *
182 : : * @param blkif the block interface
183 : : * @param tapreq the request to give back
184 : : */
185 : : static void
186 : 0 : tapdisk_xenblkif_free_request(struct td_xenblkif * const blkif,
187 : : struct td_xenblkif_req * const tapreq)
188 : : {
189 [ # # ]: 0 : ASSERT(blkif);
190 [ # # ]: 0 : ASSERT(tapreq);
191 [ # # ]: 0 : ASSERT(blkif->n_reqs_free < blkif->ring_size);
192 : :
193 : : #ifdef DEBUG
194 : : memset(&tapreq->msg, BLKIF_MSG_POISON, sizeof(tapreq->msg));
195 : : #endif
196 : :
197 : 0 : blkif->reqs_free[blkif->ring_size - (++blkif->n_reqs_free)] = &tapreq->msg;
198 : :
199 [ # # ]: 0 : if (likely(tapreq->msg.nr_segments))
200 : 0 : td_xenblkif_bufcache_put(blkif, tapreq->vma);
201 : 0 : }
202 : :
203 : : /**
204 : : * Returns the size, in request descriptors, of the shared ring
205 : : *
206 : : * @param blkif the block interface
207 : : * @returns the size, in request descriptors, of the shared ring
208 : : */
209 : : static int
210 : 0 : td_blkif_ring_size(const struct td_xenblkif * const blkif)
211 : : {
212 [ # # ]: 0 : ASSERT(blkif);
213 : :
214 [ # # # # ]: 0 : switch (blkif->proto) {
215 : 0 : case BLKIF_PROTOCOL_NATIVE:
216 : 0 : return RING_SIZE(&blkif->rings.native);
217 : :
218 : 0 : case BLKIF_PROTOCOL_X86_32:
219 : 0 : return RING_SIZE(&blkif->rings.x86_32);
220 : :
221 : 0 : case BLKIF_PROTOCOL_X86_64:
222 : 0 : return RING_SIZE(&blkif->rings.x86_64);
223 : :
224 : : default:
225 : : return -EPROTONOSUPPORT;
226 : : }
227 : : }
228 : :
229 : : /**
230 : : * Get the response that corresponds to the specified ring index in a H/W
231 : : * independent way.
232 : : *
233 : : * @returns a pointer to the response, NULL on error, sets errno
234 : : *
235 : : * TODO use function pointers instead of switch
236 : : * XXX only called by xenio_blkif_put_response
237 : : */
238 : : static inline blkif_response_t *
239 : 0 : xenio_blkif_get_response(struct td_xenblkif* const blkif, const RING_IDX rp)
240 : : {
241 : 0 : blkif_back_rings_t * const rings = &blkif->rings;
242 : 0 : blkif_response_t * p = NULL;
243 : :
244 [ # # # # ]: 0 : switch (blkif->proto) {
245 : 0 : case BLKIF_PROTOCOL_NATIVE:
246 : 0 : p = (blkif_response_t *) RING_GET_RESPONSE(&rings->native, rp);
247 : 0 : break;
248 : 0 : case BLKIF_PROTOCOL_X86_32:
249 : 0 : p = (blkif_response_t *) RING_GET_RESPONSE(&rings->x86_32, rp);
250 : 0 : break;
251 : 0 : case BLKIF_PROTOCOL_X86_64:
252 : 0 : p = (blkif_response_t *) RING_GET_RESPONSE(&rings->x86_64, rp);
253 : 0 : break;
254 : 0 : default:
255 : 0 : errno = EPROTONOSUPPORT;
256 : 0 : return NULL;
257 : : }
258 : :
259 : : return p;
260 : : }
261 : :
262 : : /**
263 : : * Puts a response in the ring.
264 : : *
265 : : * @param blkif the VBD
266 : : * @param req the request for which the response should be put
267 : : * @param status the status of the response (success or an error code)
268 : : * @param final controls whether the front-end will be notified, if necessary
269 : : *
270 : : * TODO @req can be NULL so the function will only notify the other end. This
271 : : * is used in the error path of tapdisk_xenblkif_queue_requests. The point is
272 : : * that the other will just be notified, does this make sense?
273 : : */
274 : : static int
275 : 0 : xenio_blkif_put_response(struct td_xenblkif * const blkif,
276 : : struct td_xenblkif_req *req, int const status, int const final)
277 : : {
278 : 0 : blkif_common_back_ring_t * const ring = &blkif->rings.common;
279 : :
280 [ # # ]: 0 : if (req) {
281 : 0 : blkif_response_t * msg = xenio_blkif_get_response(blkif,
282 : : ring->rsp_prod_pvt);
283 [ # # ]: 0 : if (!msg)
284 : 0 : return -errno;
285 : :
286 [ # # # # ]: 0 : ASSERT(status == BLKIF_RSP_EOPNOTSUPP || status == BLKIF_RSP_ERROR
287 : 0 : || status == BLKIF_RSP_OKAY);
288 : :
289 : 0 : msg->id = req->msg.id;
290 : :
291 : 0 : msg->operation = req->msg.operation;
292 : :
293 : 0 : msg->status = status;
294 : :
295 : 0 : ring->rsp_prod_pvt++;
296 : : }
297 : :
298 [ # # ]: 0 : if (final) {
299 : 0 : int notify;
300 : 0 : RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(ring, notify);
301 [ # # ]: 0 : if (notify) {
302 : 0 : int err = xenevtchn_notify(blkif->ctx->xce_handle, blkif->port);
303 [ # # ]: 0 : if (err < 0) {
304 : 0 : err = -errno;
305 [ # # ]: 0 : if (req) {
306 : 0 : RING_ERR(blkif, "req %lu: failed to notify event channel: "
307 : 0 : "%s\n", req->msg.id, strerror(-err));
308 : : } else {
309 : 0 : RING_ERR(blkif, "failed to notify event channel: %s\n",
310 : 0 : strerror(-err));
311 : : }
312 : 0 : return err;
313 : : }
314 : : }
315 : : }
316 : :
317 : : return 0;
318 : : }
319 : :
320 : :
321 : : /**
322 : : * Tells whether the request requires data to be read.
323 : : */
324 : : static inline bool
325 : 0 : blkif_rq_rd(blkif_request_t const * const msg)
326 : : {
327 : 0 : return BLKIF_OP_READ == msg->operation;
328 : : }
329 : :
330 : :
331 : : /**
332 : : * Tells whether the request requires data to be written.
333 : : */
334 : : static inline bool
335 : 0 : blkif_rq_wr(blkif_request_t const * const msg)
336 : : {
337 [ # # # # : 0 : return BLKIF_OP_WRITE == msg->operation ||
# # # # #
# ]
338 [ # # # # : 0 : (BLKIF_OP_WRITE_BARRIER == msg->operation && msg->nr_segments);
# # ]
339 : : }
340 : :
341 : :
342 : : /**
343 : : * Tells whether the request requires data to transferred.
344 : : */
345 : : static inline bool
346 : 0 : blkif_rq_data(blkif_request_t const * const msg)
347 : : {
348 : 0 : return blkif_rq_rd(msg) || blkif_rq_wr(msg);
349 : : }
350 : :
351 : :
352 : : static int
353 : 0 : guest_copy2(struct td_xenblkif * const blkif,
354 : : struct td_xenblkif_req * const tapreq /* TODO rename to req */) {
355 : :
356 : 0 : int i = 0;
357 : 0 : long err = 0;
358 : 0 : struct ioctl_gntdev_grant_copy gcopy;
359 : :
360 [ # # ]: 0 : ASSERT(blkif);
361 [ # # ]: 0 : ASSERT(blkif->ctx);
362 [ # # ]: 0 : ASSERT(tapreq);
363 [ # # ]: 0 : ASSERT(blkif_rq_data(&tapreq->msg));
364 [ # # ]: 0 : ASSERT(tapreq->msg.nr_segments > 0);
365 [ # # ]: 0 : ASSERT(tapreq->msg.nr_segments <= ARRAY_SIZE(tapreq->gcopy_segs));
366 : :
367 [ # # ]: 0 : for (i = 0; i < tapreq->msg.nr_segments; i++) {
368 : 0 : struct blkif_request_segment *blkif_seg = &tapreq->msg.seg[i];
369 : 0 : struct gntdev_grant_copy_segment *gcopy_seg = &tapreq->gcopy_segs[i];
370 : : #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
371 [ # # ]: 0 : if (blkif_rq_wr(&tapreq->msg)) {
372 : : /* copy from guest */
373 : 0 : gcopy_seg->dest.virt = tapreq->vma + (i << PAGE_SHIFT)
374 : 0 : + (blkif_seg->first_sect << SECTOR_SHIFT);
375 : 0 : gcopy_seg->source.foreign.ref = blkif_seg->gref;
376 : 0 : gcopy_seg->source.foreign.offset = blkif_seg->first_sect << SECTOR_SHIFT;
377 : 0 : gcopy_seg->source.foreign.domid = blkif->domid;
378 : 0 : gcopy_seg->flags = GNTCOPY_source_gref;
379 : : } else {
380 : : /* copy to guest */
381 : 0 : gcopy_seg->source.virt = tapreq->vma + (i << PAGE_SHIFT)
382 : 0 : + (blkif_seg->first_sect << SECTOR_SHIFT);
383 : 0 : gcopy_seg->dest.foreign.ref = blkif_seg->gref;
384 : 0 : gcopy_seg->dest.foreign.offset = blkif_seg->first_sect << SECTOR_SHIFT;
385 : 0 : gcopy_seg->dest.foreign.domid = blkif->domid;
386 : 0 : gcopy_seg->flags = GNTCOPY_dest_gref;
387 : : }
388 : :
389 : 0 : gcopy_seg->len = (blkif_seg->last_sect
390 : 0 : - blkif_seg->first_sect
391 : 0 : + 1)
392 : 0 : << SECTOR_SHIFT;
393 : : }
394 : : #else
395 : : gcopy_seg->iov.iov_base = tapreq->vma + (i << PAGE_SHIFT)
396 : : + (blkif_seg->first_sect << SECTOR_SHIFT);
397 : : gcopy_seg->iov.iov_len = (blkif_seg->last_sect
398 : : - blkif_seg->first_sect
399 : : + 1)
400 : : << SECTOR_SHIFT;
401 : : gcopy_seg->ref = blkif_seg->gref;
402 : : gcopy_seg->offset = blkif_seg->first_sect << SECTOR_SHIFT;
403 : : }
404 : :
405 : : gcopy.dir = blkif_rq_wr(&tapreq->msg);
406 : : gcopy.domid = blkif->domid;
407 : : #endif
408 : 0 : gcopy.count = tapreq->msg.nr_segments;
409 : 0 : gcopy.segments = tapreq->gcopy_segs;
410 : :
411 : 0 : err = -ioctl(blkif->ctx->gntdev_fd, IOCTL_GNTDEV_GRANT_COPY, &gcopy);
412 [ # # ]: 0 : if (err) {
413 : 0 : err = -errno;
414 : 0 : RING_ERR(blkif, "failed to grant-copy request %"PRIu64" "
415 : : "(%d segments): %s\n", tapreq->msg.id,
416 : 0 : tapreq->msg.nr_segments, strerror(-err));
417 : 0 : goto out;
418 : : }
419 : :
420 [ # # ]: 0 : for (i = 0; i < tapreq->msg.nr_segments; i++) {
421 : 0 : struct gntdev_grant_copy_segment *gcopy_seg = &tapreq->gcopy_segs[i];
422 [ # # ]: 0 : if (gcopy_seg->status != GNTST_okay) {
423 : : /*
424 : : * TODO use gnttabop_error for reporting errors, defined in
425 : : * xen/extras/mini-os/include/gnttab.h (header not available to
426 : : * user space)
427 : : */
428 : 0 : RING_ERR(blkif, "req %lu: failed to grant-copy segment %d: %d\n",
429 : 0 : tapreq->msg.id, i, gcopy_seg->status);
430 : 0 : err = -EIO;
431 : 0 : goto out;
432 : : }
433 : : }
434 : :
435 : 0 : out:
436 : 0 : return err;
437 : : }
438 : :
439 : :
440 : : /**
441 : : * Completes a request. If this is the last pending request of a dead block
442 : : * interface, the block interface is destroyed, the caller must not access it
443 : : * any more.
444 : : *
445 : : * @blkif the VBD the request belongs belongs to
446 : : * @tapreq the request to complete TODO rename to req
447 : : * @error completion status of the request
448 : : * @final controls whether the other end should be notified
449 : : */
450 : : void
451 : 0 : tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif,
452 : : struct td_xenblkif_req* tapreq, int err, const int final)
453 : : {
454 : 0 : int _err;
455 : 0 : long long *max = NULL, *sum = NULL, *cnt = NULL;
456 : 0 : static int depth = 0;
457 : 0 : bool processing_barrier_message;
458 : 0 : uint64_t *ticks = NULL;
459 : :
460 [ # # ]: 0 : ASSERT(blkif);
461 [ # # ]: 0 : ASSERT(tapreq);
462 [ # # ]: 0 : ASSERT(depth >= 0);
463 : :
464 : 0 : depth++;
465 : :
466 : 0 : processing_barrier_message =
467 : 0 : tapreq->msg.operation == BLKIF_OP_WRITE_BARRIER;
468 : :
469 : : /*
470 : : * If a barrier request completes, check whether it's an I/O completion
471 : : * (the barrier carries write I/O data), or a completion because the last
472 : : * pending non-barrier request completed. If the former case is true, we
473 : : * need to check again whether the latter is true and proceed with the
474 : : * completion, otherwise simply note the fact that I/O is done and when
475 : : * the last pending non-barrier requests completes, this function will be
476 : : * called again passing the barrier request.
477 : : */
478 [ # # ]: 0 : if (unlikely(processing_barrier_message)) {
479 [ # # ]: 0 : ASSERT(blkif->barrier.msg == &tapreq->msg);
480 [ # # # # ]: 0 : if (tapreq->msg.nr_segments && !blkif->barrier.io_done) {
481 : 0 : blkif->barrier.io_err = err;
482 : 0 : blkif->barrier.io_done = true;
483 : : }
484 [ # # ]: 0 : if (!tapdisk_xenblkif_barrier_should_complete(blkif))
485 : 0 : goto out;
486 : : }
487 : :
488 [ # # ]: 0 : if (likely(!blkif->dead)) {
489 [ # # ]: 0 : if (blkif_rq_rd(&tapreq->msg)) {
490 : : /*
491 : : * TODO stats should be collected after grant-copy for better
492 : : * accuracy
493 : : */
494 [ # # ]: 0 : if (likely(blkif->stats.xenvbd)) {
495 : 0 : cnt = &blkif->stats.xenvbd->st_rd_cnt;
496 : 0 : sum = &blkif->stats.xenvbd->st_rd_sum_usecs;
497 : 0 : max = &blkif->stats.xenvbd->st_rd_max_usecs;
498 : : }
499 : 0 : blkif->vbd_stats.stats->read_reqs_completed++;
500 : 0 : ticks = &blkif->vbd_stats.stats->read_total_ticks;
501 [ # # ]: 0 : if (likely(!err)) {
502 : 0 : _err = guest_copy2(blkif, tapreq);
503 [ # # ]: 0 : if (unlikely(_err)) {
504 : 0 : err = _err;
505 : 0 : RING_ERR(blkif, "req %lu: failed to copy from/to guest: "
506 : : "%s\n", tapreq->msg.id, strerror(-err));
507 : : }
508 : : }
509 [ # # ]: 0 : } else if (blkif_rq_wr(&tapreq->msg)) {
510 [ # # ]: 0 : if (likely(blkif->stats.xenvbd)) {
511 : 0 : cnt = &blkif->stats.xenvbd->st_wr_cnt;
512 : 0 : sum = &blkif->stats.xenvbd->st_wr_sum_usecs;
513 : 0 : max = &blkif->stats.xenvbd->st_wr_max_usecs;
514 : : }
515 : 0 : blkif->vbd_stats.stats->write_reqs_completed++;
516 : 0 : ticks = &blkif->vbd_stats.stats->write_total_ticks;
517 : : }
518 : :
519 [ # # ]: 0 : if (likely(cnt)) {
520 : 0 : struct timeval now;
521 : 0 : long long interval;
522 : 0 : gettimeofday(&now, NULL);
523 [ # # ]: 0 : interval = timeval_to_us(&now) - timeval_to_us(&tapreq->ts);
524 : 0 : *ticks += interval;
525 [ # # ]: 0 : if (interval > *max)
526 : 0 : *max = interval;
527 : :
528 : 0 : *sum += interval;
529 : 0 : *cnt += 1;
530 : : }
531 : :
532 [ # # ]: 0 : if (likely(err == 0))
533 : : _err = BLKIF_RSP_OKAY;
534 [ # # ]: 0 : else if (err == EOPNOTSUPP)
535 : : _err = BLKIF_RSP_EOPNOTSUPP;
536 : : else
537 : 0 : _err = BLKIF_RSP_ERROR;
538 : :
539 : 0 : xenio_blkif_put_response(blkif, tapreq, _err, final);
540 : : }
541 : :
542 : 0 : tapdisk_xenblkif_free_request(blkif, tapreq);
543 : :
544 : 0 : blkif->stats.reqs.out++;
545 [ # # ]: 0 : if (final)
546 : 0 : blkif->stats.kicks.out++;
547 : :
548 [ # # ]: 0 : if (unlikely(processing_barrier_message))
549 : 0 : blkif->barrier.msg = NULL;
550 : :
551 : : /*
552 : : * Schedule a ring check in case we left requests in it due to lack of
553 : : * memory or in case we stopped processing it because of a barrier.
554 : : *
555 : : * FIXME we should decide whether a ring check is necessary more
556 : : * intelligently.
557 : : */
558 [ # # ]: 0 : if (!blkif->barrier.msg) {
559 [ # # ]: 0 : if (likely(!blkif->dead))
560 : 0 : tapdisk_xenblkif_sched_chkrng(blkif);
561 : : } else {
562 : : /*
563 : : * If this is the last request, complete the barrier request.
564 : : */
565 [ # # ]: 0 : if (tapdisk_xenblkif_barrier_should_complete(blkif))
566 : 0 : tapdisk_xenblkif_complete_request(blkif,
567 : 0 : msg_to_tapreq(blkif->barrier.msg), 0, 1);
568 : : }
569 : :
570 : : /*
571 : : * Last request of a dead ring completes, destroy the ring.
572 : : */
573 [ # # # # : 0 : if (unlikely(1 == depth
# # ]
574 : : && blkif->dead
575 : : && !tapdisk_xenblkif_reqs_pending(blkif))) {
576 : :
577 : 0 : RING_DEBUG(blkif, "destroying dead ring\n");
578 : 0 : tapdisk_xenblkif_destroy(blkif);
579 : : }
580 : :
581 : 0 : out:
582 : 0 : depth--;
583 : 0 : }
584 : :
585 : : /**
586 : : * Request completion callback, executed when the tapdisk has finished
587 : : * processing the request.
588 : : *
589 : : * @param vreq the completed request
590 : : * @param error status of the request
591 : : * @param token token previously associated with this request
592 : : * @param final TODO ?
593 : : */
594 : : static inline void
595 : 0 : __tapdisk_xenblkif_request_cb(struct td_vbd_request * const vreq,
596 : : const int error, void * const token, const int final)
597 : : {
598 : 0 : struct td_xenblkif_req *tapreq;
599 : 0 : struct td_xenblkif * const blkif = token;
600 : :
601 [ # # ]: 0 : ASSERT(vreq);
602 [ # # ]: 0 : ASSERT(blkif);
603 : :
604 : 0 : tapreq = container_of(vreq, struct td_xenblkif_req, vreq);
605 : :
606 [ # # ]: 0 : if (error) {
607 [ # # ]: 0 : if (likely(!blkif->dead)) {
608 : 0 : blkif->stats.errors.img++;
609 : 0 : blkif->vbd_stats.stats->io_errors++;
610 : : }
611 : : }
612 : :
613 : 0 : tapdisk_xenblkif_complete_request(blkif, tapreq, error, final);
614 : 0 : }
615 : :
616 : :
617 : : static inline int
618 : 0 : tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif,
619 : : struct td_xenblkif_req * const req)
620 : : {
621 : 0 : td_vbd_request_t *vreq;
622 : 0 : int i;
623 : 0 : struct td_iovec *iov;
624 : 0 : void *page, *next, *last;
625 : 0 : int err = 0;
626 : 0 : unsigned nr_sect = 0;
627 : :
628 [ # # ]: 0 : ASSERT(blkif);
629 [ # # ]: 0 : ASSERT(req);
630 : :
631 : 0 : vreq = &req->vreq;
632 : 0 : ASSERT(vreq);
633 : :
634 : 0 : req->vma = td_xenblkif_bufcache_get(blkif);
635 [ # # ]: 0 : if (unlikely(!req->vma)) {
636 : 0 : err = errno;
637 : 0 : goto out;
638 : : }
639 : :
640 [ # # ]: 0 : for (i = 0; i < req->msg.nr_segments; i++) {
641 : 0 : struct blkif_request_segment *seg = &req->msg.seg[i];
642 : 0 : req->gref[i] = seg->gref;
643 : :
644 : : /*
645 : : * Note that first and last may be equal, which means only one sector
646 : : * must be transferred.
647 : : */
648 [ # # ]: 0 : if (seg->last_sect < seg->first_sect) {
649 : 0 : RING_ERR(blkif, "req %lu: invalid sectors %d-%d\n",
650 : 0 : req->msg.id, seg->first_sect, seg->last_sect);
651 : 0 : err = EINVAL;
652 : 0 : goto out;
653 : : }
654 : : }
655 : :
656 : : /*
657 : : * Vectorises the request: creates the struct iovec (in tapreq->iov) that
658 : : * describes each segment to be transferred. Also, merges consecutive
659 : : * segments.
660 : : *
661 : : * In each loop, iov points to the previous scatter/gather element in
662 : : * order to reuse it if the current and previous segments are
663 : : * consecutive.
664 : : */
665 : 0 : iov = req->iov - 1;
666 : 0 : last = NULL;
667 : 0 : page = req->vma;
668 : :
669 [ # # ]: 0 : for (i = 0; i < req->msg.nr_segments; i++) { /* for each segment */
670 : 0 : struct blkif_request_segment *seg = &req->msg.seg[i];
671 : 0 : size_t size;
672 : :
673 : : /* TODO check that first_sect/last_sect are within page */
674 : :
675 : 0 : next = page + (seg->first_sect << SECTOR_SHIFT);
676 : 0 : size = seg->last_sect - seg->first_sect + 1;
677 : :
678 [ # # ]: 0 : if (next != last) {
679 : 0 : iov++;
680 : 0 : iov->base = next;
681 : 0 : iov->secs = size;
682 : : } else /* The "else" is true if fist_sect is 0. */
683 : 0 : iov->secs += size;
684 : :
685 : 0 : last = iov->base + (iov->secs << SECTOR_SHIFT);
686 : 0 : page += PAGE_SIZE;
687 : 0 : nr_sect += size;
688 : : }
689 : :
690 : 0 : vreq->iov = req->iov;
691 : 0 : vreq->iovcnt = iov - req->iov + 1;
692 : 0 : vreq->sec = req->msg.sector_number;
693 : :
694 [ # # ]: 0 : if (blkif_rq_wr(&req->msg)) {
695 : 0 : err = guest_copy2(blkif, req);
696 [ # # ]: 0 : if (err) {
697 : 0 : RING_ERR(blkif, "req %lu: failed to copy from guest: %s\n",
698 : 0 : req->msg.id, strerror(-err));
699 : 0 : goto out;
700 : : }
701 [ # # ]: 0 : if (likely(blkif->stats.xenvbd))
702 : 0 : blkif->stats.xenvbd->st_wr_sect += nr_sect;
703 [ # # ]: 0 : if (likely(blkif->vbd_stats.stats))
704 : 0 : blkif->vbd_stats.stats->write_sectors += nr_sect;
705 : : } else {
706 [ # # ]: 0 : if (likely(blkif->stats.xenvbd))
707 : 0 : blkif->stats.xenvbd->st_rd_sect += nr_sect;
708 [ # # ]: 0 : if (likely(blkif->vbd_stats.stats))
709 : 0 : blkif->vbd_stats.stats->read_sectors += nr_sect;
710 : : }
711 : :
712 : : /*
713 : : * TODO Isn't this kind of expensive to do for each requests? Why does
714 : : * the tapdisk need this in the first place?
715 : : */
716 : 0 : snprintf(req->name, sizeof(req->name), "xenvbd-%d-%d.%"SCNx64"",
717 : : blkif->domid, blkif->devid, req->msg.id);
718 : :
719 : 0 : vreq->name = req->name;
720 : 0 : vreq->token = blkif;
721 : 0 : vreq->cb = __tapdisk_xenblkif_request_cb;
722 : :
723 : 0 : out:
724 : 0 : return err;
725 : : }
726 : :
727 : :
728 : : /**
729 : : * Initialises the standard tapdisk request (td_vbd_request_t) from the
730 : : * intermediate ring request (td_xenblkif_req) in order to prepare it
731 : : * processing.
732 : : *
733 : : * @param blkif the block interface
734 : : * @param tapreq the request to prepare TODO rename to req
735 : : * @returns 0 on success
736 : : *
737 : : * XXX only called by tapdisk_xenblkif_queue_request
738 : : */
739 : : static inline int
740 : 0 : tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif,
741 : : struct td_xenblkif_req * const tapreq)
742 : : {
743 : 0 : int err = 0;
744 : 0 : td_vbd_request_t *vreq;
745 : :
746 [ # # ]: 0 : ASSERT(tapreq);
747 : :
748 : 0 : vreq = &tapreq->vreq;
749 : 0 : ASSERT(vreq);
750 [ # # # ]: 0 : memset(vreq, 0, sizeof(*vreq));
751 : :
752 : 0 : tapreq->vma = NULL;
753 [ # # # ]: 0 : switch (tapreq->msg.operation) {
754 : 0 : case BLKIF_OP_READ:
755 [ # # ]: 0 : if (likely(blkif->stats.xenvbd))
756 : 0 : blkif->stats.xenvbd->st_rd_req++;
757 [ # # ]: 0 : if (likely(blkif->vbd_stats.stats))
758 : 0 : blkif->vbd_stats.stats->read_reqs_submitted++;
759 : 0 : tapreq->prot = PROT_WRITE;
760 : 0 : vreq->op = TD_OP_READ;
761 : 0 : break;
762 : 0 : case BLKIF_OP_WRITE:
763 : : case BLKIF_OP_WRITE_BARRIER:
764 [ # # ]: 0 : if (likely(blkif->stats.xenvbd))
765 : 0 : blkif->stats.xenvbd->st_wr_req++;
766 [ # # ]: 0 : if (likely(blkif->vbd_stats.stats))
767 : 0 : blkif->vbd_stats.stats->write_reqs_submitted++;
768 : 0 : tapreq->prot = PROT_READ;
769 : 0 : vreq->op = TD_OP_WRITE;
770 : 0 : break;
771 : 0 : default:
772 : 0 : RING_ERR(blkif, "req %lu: invalid request type %d\n",
773 : 0 : tapreq->msg.id, tapreq->msg.operation);
774 : 0 : err = EOPNOTSUPP;
775 : 0 : goto out;
776 : : }
777 : : /* Timestamp before the requests leave the blkif layer */
778 : 0 : gettimeofday(&tapreq->ts, NULL);
779 : :
780 : : /*
781 : : * Check that the number of segments is sane.
782 : : */
783 [ # # # # : 0 : if (unlikely((tapreq->msg.nr_segments == 0 &&
# # ]
784 : : tapreq->msg.operation != BLKIF_OP_WRITE_BARRIER) ||
785 : : tapreq->msg.nr_segments > BLKIF_MAX_BUFFER_SEGMENTS_PER_REQUEST)) {
786 : 0 : RING_ERR(blkif, "req %lu: bad number of segments in request (%d)\n",
787 : 0 : tapreq->msg.id, tapreq->msg.nr_segments);
788 : 0 : err = EINVAL;
789 : 0 : goto out;
790 : : }
791 : :
792 [ # # ]: 0 : if (likely(tapreq->msg.nr_segments))
793 : 0 : err = tapdisk_xenblkif_parse_request(blkif, tapreq);
794 : : /*
795 : : * If we only got one request from the ring and that was a barrier one,
796 : : * check whether the barrier requests completion conditions are satisfied
797 : : * and if they are, complete the barrier request.
798 : : *
799 : : * It could be that there are more requests in the ring after the barrier
800 : : * request, tapdisk_xenblkif_complete_request() will schedule a ring check.
801 : : */
802 [ # # ]: 0 : else if (tapdisk_xenblkif_barrier_should_complete(blkif)) {
803 : 0 : tapdisk_xenblkif_complete_request(blkif,
804 : 0 : msg_to_tapreq(blkif->barrier.msg), 0, 1);
805 : 0 : err = 0;
806 : : }
807 : 0 : out:
808 : 0 : return err;
809 : : }
810 : :
811 : :
812 : : /**
813 : : * Queues a ring request, after it prepares it, to the standard taodisk queue
814 : : * for processing.
815 : : *
816 : : * @param blkif the block interface
817 : : * @param msg the ring request
818 : : * @param tapreq the intermediate request TODO rename to req
819 : : *
820 : : * TODO don't really need to supply the ring request since it's either way
821 : : * contained in the tapreq
822 : : *
823 : : * XXX only called by tapdisk_xenblkif_queue_requests
824 : : */
825 : : static inline int
826 : 0 : tapdisk_xenblkif_queue_request(struct td_xenblkif * const blkif,
827 : : blkif_request_t *msg, struct td_xenblkif_req *tapreq)
828 : : {
829 : 0 : int err;
830 : :
831 [ # # ]: 0 : ASSERT(blkif);
832 [ # # ]: 0 : ASSERT(msg);
833 [ # # ]: 0 : ASSERT(tapreq);
834 : :
835 : 0 : err = tapdisk_xenblkif_make_vbd_request(blkif, tapreq);
836 [ # # ]: 0 : if (unlikely(err)) {
837 : : /* TODO log error */
838 : 0 : blkif->stats.errors.map++;
839 : 0 : return err;
840 : : }
841 : :
842 [ # # ]: 0 : if (likely(tapreq->msg.nr_segments)) {
843 : 0 : err = tapdisk_vbd_queue_request(blkif->vbd, &tapreq->vreq);
844 [ # # ]: 0 : if (unlikely(err)) {
845 : : /* TODO log error */
846 : 0 : blkif->stats.errors.vbd++;
847 : 0 : return err;
848 : : }
849 : : }
850 : :
851 : : return 0;
852 : : }
853 : :
854 : :
855 : : void
856 : 0 : tapdisk_xenblkif_queue_requests(struct td_xenblkif * const blkif,
857 : : blkif_request_t *reqs[], const int nr_reqs)
858 : : {
859 : 0 : int i;
860 : 0 : int err;
861 : 0 : int nr_errors = 0;
862 : :
863 [ # # ]: 0 : ASSERT(blkif);
864 [ # # ]: 0 : ASSERT(reqs);
865 [ # # ]: 0 : ASSERT(nr_reqs >= 0);
866 : :
867 [ # # ]: 0 : for (i = 0; i < nr_reqs; i++) { /* for each request in the ring... */
868 : 0 : blkif_request_t *msg = reqs[i];
869 : 0 : struct td_xenblkif_req *tapreq;
870 : :
871 [ # # ]: 0 : ASSERT(msg);
872 : :
873 : 0 : tapreq = msg_to_tapreq(msg);
874 : :
875 : 0 : ASSERT(tapreq);
876 : :
877 : 0 : err = tapdisk_xenblkif_queue_request(blkif, msg, tapreq);
878 [ # # ]: 0 : if (err) {
879 : : /* TODO log error */
880 : 0 : nr_errors++;
881 : 0 : tapdisk_xenblkif_complete_request(blkif, tapreq, err, 1);
882 : : }
883 : : }
884 : :
885 : : /* there is a possibility of blkif getting freed if ring is
886 : : dead and current request is the last one, hence adding
887 : : this check to avoid seg fault */
888 : :
889 [ # # ]: 0 : if (nr_errors && blkif)
890 : 0 : xenio_blkif_put_response(blkif, NULL, 0, 1);
891 : 0 : }
892 : :
893 : : void
894 : 0 : tapdisk_xenblkif_reqs_free(struct td_xenblkif * const blkif)
895 : : {
896 [ # # ]: 0 : ASSERT(blkif);
897 : :
898 : 0 : td_xenblkif_bufcache_free(blkif);
899 [ # # ]: 0 : td_xenblkif_bufcache_evt_unreg(blkif);
900 : :
901 : 0 : free(blkif->reqs_bufcache);
902 : 0 : blkif->reqs_bufcache = NULL;
903 : :
904 : 0 : free(blkif->reqs);
905 : 0 : blkif->reqs = NULL;
906 : :
907 : 0 : free(blkif->reqs_free);
908 : 0 : blkif->reqs_free = NULL;
909 : :
910 : 0 : }
911 : :
912 : : int
913 : 0 : tapdisk_xenblkif_reqs_init(struct td_xenblkif *td_blkif)
914 : : {
915 : 0 : void *buf;
916 : 0 : int i = 0;
917 : 0 : int err = 0;
918 : :
919 [ # # ]: 0 : ASSERT(td_blkif);
920 : :
921 : 0 : td_blkif->ring_size = td_blkif_ring_size(td_blkif);
922 [ # # ]: 0 : ASSERT(td_blkif->ring_size > 0);
923 : :
924 : 0 : td_blkif->reqs =
925 : 0 : calloc(td_blkif->ring_size, sizeof(struct td_xenblkif_req));
926 [ # # ]: 0 : if (!td_blkif->reqs) {
927 : 0 : err = -errno;
928 : 0 : goto fail;
929 : : }
930 : :
931 : 0 : td_blkif->reqs_free =
932 : 0 : malloc(td_blkif->ring_size * sizeof(struct xenio_blkif_req *));
933 [ # # ]: 0 : if (!td_blkif->reqs_free) {
934 : 0 : err = -errno;
935 : 0 : goto fail;
936 : : }
937 : :
938 : 0 : td_blkif->n_reqs_free = 0;
939 [ # # ]: 0 : for (i = 0; i < td_blkif->ring_size; i++)
940 : 0 : tapdisk_xenblkif_free_request(td_blkif, &td_blkif->reqs[i]);
941 : :
942 : : // Allocate the buffer cache
943 : 0 : td_blkif->reqs_bufcache = malloc(sizeof(void*) * td_blkif->ring_size);
944 [ # # ]: 0 : if (!td_blkif->reqs_bufcache) {
945 : 0 : err = -errno;
946 : 0 : goto fail;
947 : : }
948 : 0 : td_blkif->n_reqs_bufcache_free = 0;
949 : 0 : td_blkif->reqs_bufcache_evtid = 0;
950 : :
951 : : // Populate cache with one buffer
952 : 0 : buf = td_xenblkif_bufcache_get(td_blkif);
953 : 0 : td_xenblkif_bufcache_put(td_blkif, buf);
954 [ # # ]: 0 : td_xenblkif_bufcache_evt_unreg(td_blkif);
955 : :
956 : 0 : return 0;
957 : :
958 : 0 : fail:
959 : 0 : tapdisk_xenblkif_reqs_free(td_blkif);
960 : 0 : return err;
961 : : }
|