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