Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #ifdef HAVE_CONFIG_H
32 : : #include "config.h"
33 : : #endif
34 : :
35 : : #include <stdio.h>
36 : : #include <errno.h>
37 : : #include <fcntl.h>
38 : : #include <unistd.h>
39 : : #include <stdlib.h>
40 : : #include <string.h>
41 : : #include <signal.h>
42 : : #include <sys/un.h>
43 : : #include <sys/stat.h>
44 : : #include <sys/types.h>
45 : : #include <sys/ioctl.h>
46 : : #include <sys/socket.h>
47 : : #include <sys/mman.h>
48 : : #include <stdbool.h>
49 : :
50 : : #include "debug.h"
51 : : #include "list.h"
52 : : #include "tapdisk.h"
53 : : #include "tapdisk-vbd.h"
54 : : #include "tapdisk-utils.h"
55 : : #include "tapdisk-server.h"
56 : : #include "tapdisk-message.h"
57 : : #include "tapdisk-disktype.h"
58 : : #include "tapdisk-stats.h"
59 : : #include "tapdisk-control.h"
60 : : #include "tapdisk-nbdserver.h"
61 : : #include "td-blkif.h"
62 : : #include "timeout-math.h"
63 : : #include "util.h"
64 : :
65 : : #define TD_CTL_MAX_CONNECTIONS 10
66 : : #define TD_CTL_SOCK_BACKLOG 32
67 : : #define TD_CTL_RECV_TIMEOUT 10
68 : : #define TD_CTL_SEND_TIMEOUT 10
69 : : #define TD_CTL_SEND_BUFSZ ((size_t)4096)
70 : :
71 : : #define DBG(_f, _a...) tlog_syslog(TLOG_DBG, _f, ##_a)
72 : : #define ERR(err, _f, _a...) tlog_error(err, _f, ##_a)
73 : : #define INFO(_f, _a...) tlog_syslog(TLOG_INFO, "control: " _f, ##_a)
74 : :
75 : : #define WARN_ON(_p) \
76 : : if (_p) { \
77 : : EPRINTF("%s:%d: WARNING: '%s'\n", \
78 : : __FILE__, __LINE__, #_p); \
79 : : }
80 : :
81 : : #define eintr_retry(res, op) \
82 : : do { \
83 : : res = op; \
84 : : } while (res == -1 && errno == EINTR);
85 : :
86 : : struct tapdisk_ctl_conn {
87 : : int fd;
88 : :
89 : : /**
90 : : * processing event
91 : : */
92 : : event_id_t event_id;
93 : :
94 : : /**
95 : : * output event
96 : : */
97 : : struct {
98 : : void *buf;
99 : : size_t bufsz;
100 : : int event_id;
101 : : int done;
102 : :
103 : : void *prod;
104 : : void *cons;
105 : : } out;
106 : :
107 : : /**
108 : : * input event
109 : : */
110 : : struct {
111 : : int event_id;
112 : : int busy;
113 : : } in;
114 : :
115 : : struct tapdisk_control_info *info;
116 : :
117 : : /**
118 : : * for linked lists
119 : : */
120 : : struct list_head entry;
121 : :
122 : : tapdisk_message_t request;
123 : : tapdisk_message_t response;
124 : : };
125 : :
126 : : #define TAPDISK_MSG_REENTER (1<<0) /* non-blocking, idempotent */
127 : : #define TAPDISK_MSG_VERBOSE (1<<1) /* tell syslog about it */
128 : :
129 : : struct tapdisk_control_info {
130 : : int (*handler)(struct tapdisk_ctl_conn *, tapdisk_message_t *,
131 : : tapdisk_message_t * const);
132 : : int flags;
133 : : };
134 : :
135 : : struct tapdisk_control {
136 : : char *path;
137 : : int uuid;
138 : : int socket;
139 : : int event_id;
140 : : int busy;
141 : :
142 : : int n_conn;
143 : : struct tapdisk_ctl_conn __conn[TD_CTL_MAX_CONNECTIONS];
144 : : struct tapdisk_ctl_conn *conn[TD_CTL_MAX_CONNECTIONS];
145 : :
146 : : /**
147 : : * List of connections whose processing has been temporarily paused
148 : : * (masked) because the current connection requires exclusive access.
149 : : */
150 : : struct list_head pending;
151 : : };
152 : :
153 : : static struct tapdisk_control td_control;
154 : :
155 : : static inline size_t
156 : 0 : page_align(size_t size)
157 : : {
158 : 0 : size_t page_size = sysconf(_SC_PAGE_SIZE);
159 : 0 : return (size + page_size - 1) & ~(page_size - 1);
160 : : }
161 : :
162 : : static void
163 : 0 : tapdisk_ctl_conn_uninit(struct tapdisk_ctl_conn *conn)
164 : : {
165 : 0 : if (conn->out.buf) {
166 : 0 : free(conn->out.buf);
167 : 0 : conn->out.buf = NULL;
168 : : }
169 : : }
170 : :
171 : : static int
172 : 0 : tapdisk_ctl_conn_init(struct tapdisk_ctl_conn *conn, size_t bufsz)
173 : : {
174 : 0 : int err;
175 : :
176 [ # # ]: 0 : memset(conn, 0, sizeof(*conn));
177 : 0 : conn->out.event_id = -1;
178 : 0 : conn->in.event_id = -1;
179 : 0 : conn->event_id = 0;
180 : :
181 : 0 : conn->out.buf = malloc(bufsz);
182 [ # # ]: 0 : if (!conn->out.buf) {
183 : 0 : err = -ENOMEM;
184 : 0 : goto fail;
185 : : }
186 : 0 : conn->out.bufsz = page_align(bufsz);
187 : :
188 : 0 : return 0;
189 : :
190 : 0 : fail:
191 : 0 : tapdisk_ctl_conn_uninit(conn);
192 : 0 : return err;
193 : : }
194 : :
195 : : static int
196 : 0 : tapdisk_ctl_conn_connected(struct tapdisk_ctl_conn *conn)
197 : : {
198 : 0 : return conn->fd >= 1;
199 : : }
200 : :
201 : : static void
202 : 0 : tapdisk_ctl_conn_free(struct tapdisk_ctl_conn *conn)
203 : : {
204 : 0 : struct tapdisk_ctl_conn *prev, *next;
205 : 0 : int i;
206 : :
207 : 0 : i = --td_control.n_conn;
208 : : /* NB. bubble the freed connection off the active list. */
209 : 0 : prev = conn;
210 : 0 : do {
211 [ # # ]: 0 : ASSERT(i >= 0);
212 : 0 : next = td_control.conn[i];
213 : 0 : td_control.conn[i] = prev;
214 : 0 : prev = next;
215 : 0 : i--;
216 [ # # ]: 0 : } while (next != conn);
217 : 0 : }
218 : :
219 : : static void
220 : 0 : tapdisk_ctl_conn_close(struct tapdisk_ctl_conn *conn)
221 : : {
222 [ # # ]: 0 : if (conn->out.event_id >= 0) {
223 : 0 : tapdisk_server_unregister_event(conn->out.event_id);
224 : 0 : conn->out.event_id = -1;
225 : : }
226 : :
227 [ # # ]: 0 : if (conn->fd >= 0) {
228 : 0 : close(conn->fd);
229 : 0 : conn->fd = -1;
230 : :
231 : 0 : tapdisk_ctl_conn_free(conn);
232 : 0 : tapdisk_server_mask_event(td_control.event_id, 0);
233 : : }
234 : 0 : }
235 : :
236 : : static void
237 : 0 : tapdisk_ctl_conn_mask_out(struct tapdisk_ctl_conn *conn)
238 : : {
239 : 0 : tapdisk_server_mask_event(conn->out.event_id, 1);
240 : 0 : }
241 : :
242 : : static void
243 : 0 : tapdisk_ctl_conn_unmask_out(struct tapdisk_ctl_conn *conn)
244 : : {
245 : 0 : tapdisk_server_mask_event(conn->out.event_id, 0);
246 : : }
247 : :
248 : : /**
249 : : * Returns number of bytes send, or a negative error code in case of failure.
250 : : */
251 : : static ssize_t
252 : 0 : tapdisk_ctl_conn_send_buf(struct tapdisk_ctl_conn *conn)
253 : : {
254 : 0 : ssize_t size;
255 : :
256 : 0 : size = conn->out.prod - conn->out.cons;
257 [ # # ]: 0 : if (!size)
258 : : return 0;
259 : :
260 : 0 : size = send(conn->fd, conn->out.cons, size, MSG_DONTWAIT);
261 [ # # ]: 0 : if (size < 0)
262 : 0 : return -errno;
263 : :
264 : 0 : conn->out.cons += size;
265 : :
266 : 0 : return size;
267 : : }
268 : :
269 : : static void
270 : 0 : tapdisk_ctl_conn_send_event(event_id_t id, char mode, void *private)
271 : : {
272 : 0 : struct tapdisk_ctl_conn *conn = private;
273 : 0 : ssize_t rv;
274 : :
275 : 0 : do {
276 : 0 : rv = tapdisk_ctl_conn_send_buf(conn);
277 [ # # ]: 0 : } while (rv > 0);
278 : :
279 [ # # ]: 0 : if (rv == -EAGAIN)
280 : : return;
281 : :
282 [ # # ]: 0 : if (rv < 0)
283 : 0 : ERR(rv, "%s: failure sending message at offset %td/%td\n",
284 : : tapdisk_message_name(conn->request.type),
285 : : conn->out.cons - conn->out.buf,
286 : : conn->out.prod - conn->out.buf);
287 : :
288 [ # # # # : 0 : if (rv || conn->out.done || mode & SCHEDULER_POLL_TIMEOUT)
# # ]
289 : 0 : tapdisk_ctl_conn_close(conn);
290 : : else
291 : 0 : tapdisk_ctl_conn_mask_out(conn);
292 : : }
293 : :
294 : : /*
295 : : * NB. the control interface is still not properly integrated into the
296 : : * server, therefore neither the scheduler. After the last close, the
297 : : * server will exit but we still have a pending close response in the
298 : : * output buffer.
299 : : */
300 : : static void
301 : 0 : tapdisk_ctl_conn_drain(struct tapdisk_ctl_conn *conn)
302 : : {
303 : 0 : struct timeval tv = { .tv_sec = TD_CTL_SEND_TIMEOUT,
304 : : .tv_usec = 0 };
305 : 0 : fd_set wfds;
306 : 0 : int n, mode;
307 : :
308 [ # # ]: 0 : if (!conn->out.done) {
309 : : /* we accepted this connection but haven't received the message
310 : : * body yet. Since this tapdisk is on its way out, just drop
311 : : * the connection. */
312 : 0 : tapdisk_ctl_conn_close(conn);
313 : 0 : return;
314 : : }
315 : :
316 [ # # ]: 0 : ASSERT(conn->fd >= 0);
317 : :
318 [ # # ]: 0 : while (tapdisk_ctl_conn_connected(conn)) {
319 [ # # ]: 0 : FD_ZERO(&wfds);
320 [ # # # # ]: 0 : FD_SET(conn->fd, &wfds);
321 : :
322 [ # # # # ]: 0 : eintr_retry(n, select(conn->fd + 1, NULL, &wfds, NULL, &tv))
323 [ # # ]: 0 : if (n < 0)
324 : : break;
325 : :
326 [ # # ]: 0 : if (n)
327 : : mode = SCHEDULER_POLL_WRITE_FD;
328 : : else
329 : 0 : mode = SCHEDULER_POLL_TIMEOUT;
330 : :
331 : 0 : tapdisk_ctl_conn_send_event(conn->out.event_id, mode, conn);
332 : : }
333 : : }
334 : :
335 : :
336 : : struct tapdisk_ctl_conn *
337 : 0 : tapdisk_ctl_conn_open(int fd)
338 : : {
339 : 0 : struct tapdisk_ctl_conn *conn;
340 : :
341 [ # # ]: 0 : if (td_control.n_conn >= TD_CTL_MAX_CONNECTIONS)
342 : 0 : return NULL;
343 : :
344 : 0 : conn = td_control.conn[td_control.n_conn++];
345 : :
346 : 0 : conn->out.event_id =
347 : 0 : tapdisk_server_register_event(SCHEDULER_POLL_WRITE_FD,
348 : 0 : fd, TV_SECS(TD_CTL_SEND_TIMEOUT),
349 : : tapdisk_ctl_conn_send_event,
350 : : conn);
351 [ # # ]: 0 : if (conn->out.event_id < 0)
352 : : return NULL;
353 : :
354 : 0 : conn->fd = fd;
355 : 0 : conn->out.prod = conn->out.buf;
356 : 0 : conn->out.cons = conn->out.buf;
357 : 0 : conn->out.done = 0;
358 : :
359 : 0 : tapdisk_ctl_conn_mask_out(conn);
360 : :
361 [ # # ]: 0 : if (td_control.n_conn >= TD_CTL_MAX_CONNECTIONS)
362 : 0 : tapdisk_server_mask_event(td_control.event_id, 1);
363 : :
364 : : return conn;
365 : : }
366 : :
367 : : static size_t
368 : 0 : tapdisk_ctl_conn_write(struct tapdisk_ctl_conn *conn, void *buf, size_t size)
369 : : {
370 : 0 : size_t rest;
371 : :
372 : 0 : rest = conn->out.buf + conn->out.bufsz - conn->out.prod;
373 : 0 : if (rest < size)
374 : : size = rest;
375 [ # # ]: 0 : if (!size)
376 : : return 0;
377 : :
378 : 0 : memcpy(conn->out.prod, buf, size);
379 : 0 : conn->out.prod += size;
380 : 0 : tapdisk_ctl_conn_unmask_out(conn);
381 : :
382 : 0 : return size;
383 : : }
384 : :
385 : : static void
386 : 0 : tapdisk_ctl_conn_release(struct tapdisk_ctl_conn *conn)
387 : : {
388 : 0 : conn->out.done = 1;
389 : :
390 [ # # ]: 0 : if (conn->out.prod == conn->out.cons)
391 : 0 : tapdisk_ctl_conn_close(conn);
392 : 0 : }
393 : :
394 : : static void
395 : 0 : tapdisk_control_initialize(void)
396 : : {
397 : 0 : struct tapdisk_ctl_conn *conn;
398 : 0 : int i;
399 : :
400 : 0 : td_control.socket = -1;
401 : 0 : td_control.event_id = -1;
402 : :
403 : 0 : signal(SIGPIPE, SIG_IGN);
404 : :
405 [ # # ]: 0 : for (i = 0; i < TD_CTL_MAX_CONNECTIONS; i++) {
406 : 0 : conn = &td_control.__conn[i];
407 : 0 : tapdisk_ctl_conn_init(conn, TD_CTL_SEND_BUFSZ);
408 : 0 : td_control.conn[i] = conn;
409 : : }
410 : :
411 : 0 : td_control.n_conn = 0;
412 : :
413 : 0 : INIT_LIST_HEAD(&td_control.pending);
414 : :
415 : 0 : DPRINTF("tapdisk-control: init, %d x %zuk buffers\n",
416 : : TD_CTL_MAX_CONNECTIONS, TD_CTL_SEND_BUFSZ >> 10);
417 : 0 : }
418 : :
419 : : void
420 : 0 : tapdisk_control_close(void)
421 : : {
422 : 0 : struct tapdisk_ctl_conn *conn;
423 : 0 : int i;
424 : :
425 : 0 : DPRINTF("tapdisk-control: draining %d connections\n",
426 : : td_control.n_conn);
427 : :
428 [ # # ]: 0 : while (td_control.n_conn) {
429 : 0 : conn = td_control.conn[td_control.n_conn-1];
430 : 0 : tapdisk_ctl_conn_drain(conn);
431 : : }
432 : :
433 [ # # ]: 0 : for (i = 0; i < TD_CTL_MAX_CONNECTIONS; i++) {
434 : 0 : conn = &td_control.__conn[i];
435 [ # # ]: 0 : tapdisk_ctl_conn_uninit(conn);
436 : : }
437 : :
438 : 0 : DPRINTF("tapdisk-control: done\n");
439 : :
440 [ # # ]: 0 : if (td_control.path) {
441 : 0 : unlink(td_control.path);
442 : 0 : free(td_control.path);
443 : 0 : td_control.path = NULL;
444 : : }
445 : :
446 [ # # ]: 0 : if (td_control.socket != -1) {
447 : 0 : close(td_control.socket);
448 : 0 : td_control.socket = -1;
449 : : }
450 : 0 : }
451 : :
452 : : static void
453 : 0 : tapdisk_control_release_connection(struct tapdisk_ctl_conn *conn)
454 : : {
455 [ # # ]: 0 : if (conn->in.event_id) {
456 : 0 : tapdisk_server_unregister_event(conn->in.event_id);
457 : 0 : conn->in.event_id = -1;
458 : : }
459 : :
460 : 0 : tapdisk_ctl_conn_release(conn);
461 : 0 : }
462 : :
463 : : static void
464 : 0 : tapdisk_control_close_connection(struct tapdisk_ctl_conn *conn)
465 : : {
466 : 0 : tapdisk_control_release_connection(conn);
467 : :
468 [ # # ]: 0 : if (tapdisk_ctl_conn_connected(conn))
469 : : /* NB. best effort for write/close sequences. */
470 : 0 : tapdisk_ctl_conn_send_buf(conn);
471 : :
472 : 0 : tapdisk_ctl_conn_close(conn);
473 : 0 : }
474 : :
475 : :
476 : : static int
477 : 0 : tapdisk_control_read_message(int fd, tapdisk_message_t *message, int timeout)
478 : : {
479 : 0 : const int len = sizeof(tapdisk_message_t);
480 : 0 : fd_set readfds;
481 : 0 : int ret, offset, err = 0;
482 : 0 : struct timeval tv, *t;
483 : :
484 : 0 : t = NULL;
485 : 0 : offset = 0;
486 : :
487 [ # # ]: 0 : if (timeout) {
488 : 0 : tv.tv_sec = timeout;
489 : 0 : tv.tv_usec = 0;
490 : 0 : t = &tv;
491 : : }
492 : :
493 : 0 : memset(message, 0, sizeof(tapdisk_message_t));
494 : :
495 [ # # ]: 0 : while (offset < len) {
496 [ # # ]: 0 : FD_ZERO(&readfds);
497 [ # # # # ]: 0 : FD_SET(fd, &readfds);
498 : :
499 [ # # # # ]: 0 : eintr_retry(ret, select(fd + 1, &readfds, NULL, NULL, t))
500 [ # # ]: 0 : if (ret == -1)
501 : : break;
502 [ # # # # : 0 : else if (FD_ISSET(fd, &readfds)) {
# # ]
503 [ # # # # : 0 : eintr_retry(ret, read(fd, message + offset, len - offset))
# # ]
504 [ # # ]: 0 : if (ret <= 0)
505 : : break;
506 : 0 : offset += ret;
507 : : } else
508 : : break;
509 : : }
510 : :
511 [ # # ]: 0 : if (ret < 0)
512 : 0 : err = -errno;
513 [ # # ]: 0 : else if (offset != len)
514 : : err = -EIO;
515 [ # # ]: 0 : if (err)
516 : 0 : ERR(err, "failure reading message at offset %d/%d\n",
517 : : offset, len);
518 : :
519 : :
520 : 0 : return err;
521 : : }
522 : :
523 : : static void
524 : 0 : tapdisk_control_write_message(struct tapdisk_ctl_conn *conn,
525 : : tapdisk_message_t *message)
526 : : {
527 : 0 : size_t size = sizeof(*message), count;
528 : :
529 [ # # # # ]: 0 : if (conn->info && conn->info->flags & TAPDISK_MSG_VERBOSE)
530 : 0 : DBG("sending '%s' message (uuid = %u)\n",
531 : : tapdisk_message_name(message->type), message->cookie);
532 : :
533 : 0 : count = tapdisk_ctl_conn_write(conn, message, size);
534 [ # # ]: 0 : WARN_ON(count != size);
535 : 0 : }
536 : :
537 : : static int
538 : 0 : tapdisk_control_validate_request(tapdisk_message_t *request)
539 : : {
540 : 0 : if (strnlen(request->u.params.path,
541 : : TAPDISK_MESSAGE_MAX_PATH_LENGTH) >=
542 : : TAPDISK_MESSAGE_MAX_PATH_LENGTH)
543 : 0 : return EINVAL;
544 : :
545 : : return 0;
546 : : }
547 : :
548 : : static int
549 : 0 : tapdisk_control_list(struct tapdisk_ctl_conn *conn,
550 : : tapdisk_message_t *request, tapdisk_message_t * const response)
551 : : {
552 : 0 : td_vbd_t *vbd;
553 : 0 : struct list_head *head;
554 : 0 : int count;
555 : :
556 [ # # ]: 0 : ASSERT(conn);
557 [ # # ]: 0 : ASSERT(request);
558 [ # # ]: 0 : ASSERT(response);
559 : :
560 : 0 : response->type = TAPDISK_MESSAGE_LIST_RSP;
561 : 0 : response->cookie = request->cookie;
562 : :
563 : 0 : head = tapdisk_server_get_all_vbds();
564 : :
565 : 0 : count = 0;
566 [ # # ]: 0 : list_for_each_entry(vbd, head, next)
567 : 0 : count++;
568 : :
569 [ # # ]: 0 : list_for_each_entry(vbd, head, next) {
570 : 0 : response->u.list.count = count--;
571 : 0 : response->u.list.minor = vbd->uuid;
572 : 0 : response->u.list.state = vbd->state;
573 : 0 : response->u.list.path[0] = 0;
574 : :
575 [ # # ]: 0 : if (vbd->name)
576 : 0 : safe_strncpy(response->u.list.path, vbd->name,
577 : : sizeof(response->u.list.path));
578 : :
579 : 0 : tapdisk_control_write_message(conn, response);
580 : : }
581 : :
582 : 0 : response->u.list.count = count;
583 : 0 : response->u.list.minor = -1;
584 : 0 : response->u.list.path[0] = 0;
585 : :
586 : 0 : return 0;
587 : : }
588 : :
589 : : static int
590 : 0 : tapdisk_control_get_pid(struct tapdisk_ctl_conn *conn,
591 : : tapdisk_message_t *request, tapdisk_message_t * const response)
592 : : {
593 [ # # ]: 0 : ASSERT(response);
594 : :
595 : 0 : response->type = TAPDISK_MESSAGE_PID_RSP;
596 : 0 : response->cookie = request->cookie;
597 : 0 : response->u.tapdisk_pid = getpid();
598 : :
599 : 0 : return 0;
600 : : }
601 : :
602 : : static int
603 : 0 : tapdisk_control_attach_vbd(struct tapdisk_ctl_conn *conn,
604 : : tapdisk_message_t *request, tapdisk_message_t * const response)
605 : : {
606 : 0 : td_vbd_t *vbd;
607 : 0 : int minor, err = 0;
608 : :
609 [ # # ]: 0 : ASSERT(conn);
610 [ # # ]: 0 : ASSERT(request);
611 [ # # ]: 0 : ASSERT(response);
612 : :
613 : : /*
614 : : * TODO: check for max vbds per process
615 : : */
616 : :
617 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
618 [ # # ]: 0 : if (vbd) {
619 : 0 : err = -EEXIST;
620 : 0 : goto out;
621 : : }
622 : :
623 : 0 : minor = request->cookie;
624 : 0 : if (minor < 0) {
625 : : err = -EINVAL;
626 : : goto out;
627 : : }
628 : :
629 : 0 : vbd = tapdisk_vbd_create(minor);
630 [ # # ]: 0 : if (!vbd) {
631 : 0 : err = -ENOMEM;
632 : 0 : goto out;
633 : : }
634 : :
635 : : /* Lock the marker file to prevent freeing in use*/
636 : 0 : err = tapdisk_vbd_lock(vbd);
637 [ # # ]: 0 : if (err) {
638 : 0 : ERR(err, "Failed to lock VBD marker file for %d\n", minor);
639 : 0 : goto fail_vbd;
640 : : }
641 : :
642 : 0 : tapdisk_server_add_vbd(vbd);
643 : :
644 : 0 : out:
645 [ # # ]: 0 : if (!err) {
646 : 0 : response->type = TAPDISK_MESSAGE_ATTACH_RSP;
647 : 0 : response->cookie = request->cookie;
648 : : }
649 : :
650 : 0 : return err;
651 : :
652 : 0 : fail_vbd:
653 : 0 : free(vbd);
654 : 0 : goto out;
655 : : }
656 : :
657 : : static int
658 : 0 : tapdisk_control_detach_vbd(struct tapdisk_ctl_conn *conn,
659 : : tapdisk_message_t *request, tapdisk_message_t * const response)
660 : : {
661 : 0 : td_vbd_t *vbd;
662 : 0 : int err = 0;
663 : :
664 [ # # ]: 0 : ASSERT(conn);
665 [ # # ]: 0 : ASSERT(request);
666 [ # # ]: 0 : ASSERT(response);
667 : :
668 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
669 [ # # ]: 0 : if (!vbd) {
670 : 0 : err = -ENODEV;
671 : 0 : goto out;
672 : : }
673 : :
674 [ # # ]: 0 : if (vbd->name) {
675 : 0 : err = -EBUSY;
676 : 0 : goto out;
677 : : }
678 : :
679 : : /* Unlock marker file */
680 : 0 : tapdisk_vbd_unlock(vbd);
681 : :
682 [ # # ]: 0 : if (list_empty(&vbd->images)) {
683 : 0 : tapdisk_server_remove_vbd(vbd);
684 : 0 : tapdisk_vbd_free(vbd);
685 : : }
686 : :
687 : 0 : out:
688 : 0 : if (!err) {
689 : 0 : response->type = TAPDISK_MESSAGE_DETACH_RSP;
690 : 0 : response->cookie = request->cookie;
691 : : }
692 : :
693 : 0 : return err;
694 : : }
695 : :
696 : : static int
697 : 0 : tapdisk_control_open_image(struct tapdisk_ctl_conn *conn,
698 : : tapdisk_message_t *request, tapdisk_message_t * const response)
699 : : {
700 : 0 : int err, ret;
701 : 0 : td_vbd_t *vbd;
702 : 0 : td_flag_t flags;
703 : :
704 [ # # ]: 0 : ASSERT(conn);
705 [ # # ]: 0 : ASSERT(request);
706 [ # # ]: 0 : ASSERT(response);
707 : :
708 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
709 [ # # ]: 0 : if (!vbd) {
710 : 0 : err = -EINVAL;
711 : 0 : goto out;
712 : : }
713 : :
714 [ # # ]: 0 : if (vbd->name) {
715 : 0 : err = -EALREADY;
716 : 0 : goto out;
717 : : }
718 : :
719 : 0 : flags = 0;
720 [ # # ]: 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_RDONLY)
721 : 0 : flags |= TD_OPEN_RDONLY;
722 [ # # ]: 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_NO_O_DIRECT)
723 : 0 : flags |= TD_OPEN_NO_O_DIRECT;
724 [ # # ]: 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SHARED)
725 : 0 : flags |= TD_OPEN_SHAREABLE;
726 [ # # ]: 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_CACHE)
727 : 0 : flags |= TD_OPEN_ADD_CACHE;
728 [ # # ]: 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_VHD_INDEX)
729 : 0 : flags |= TD_OPEN_VHD_INDEX;
730 [ # # ]: 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_LOG) {
731 : 0 : char *logpath = malloc(TAPDISK_MESSAGE_MAX_PATH_LENGTH + 1);
732 [ # # ]: 0 : if (!logpath) {
733 : 0 : err = -ENOMEM;
734 : 0 : goto out;
735 : : }
736 [ # # ]: 0 : ret = read(conn->fd, logpath, TAPDISK_MESSAGE_MAX_PATH_LENGTH);
737 [ # # ]: 0 : if (ret < 0) {
738 : 0 : err = -EIO;
739 : 0 : free(logpath);
740 : 0 : goto out;
741 : : }
742 : 0 : *(logpath + TAPDISK_MESSAGE_MAX_PATH_LENGTH) = '\0';
743 : 0 : vbd->logpath = logpath;
744 : 0 : flags |= TD_OPEN_ADD_LOG;
745 : : }
746 [ # # ]: 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_LCACHE)
747 : 0 : flags |= TD_OPEN_LOCAL_CACHE;
748 [ # # ]: 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_REUSE_PRT)
749 : 0 : flags |= TD_OPEN_REUSE_PARENT;
750 [ # # ]: 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_STANDBY)
751 : 0 : flags |= TD_OPEN_STANDBY;
752 [ # # ]: 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SECONDARY) {
753 : 0 : char *name = strdup(request->u.params.secondary);
754 [ # # ]: 0 : if (!name) {
755 : 0 : err = -errno;
756 : 0 : goto out;
757 : : }
758 : 0 : vbd->secondary_name = name;
759 : 0 : flags |= TD_OPEN_SECONDARY;
760 : : }
761 [ # # ]: 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_OPEN_ENCRYPTED) {
762 : 0 : uint8_t key_size;
763 : 0 : uint8_t *encryption_key;
764 : 0 : ssize_t ret;
765 : :
766 : 0 : DPRINTF("Reading encryption key for VHD\n");
767 : :
768 : 0 : ret = read(conn->fd, &key_size, sizeof(key_size));
769 [ # # ]: 0 : if (ret != sizeof(key_size)) {
770 : 0 : err = -EIO;
771 : 0 : goto out;
772 : : }
773 : :
774 [ # # ]: 0 : if (key_size == 0 || key_size > 128) {
775 : 0 : EPRINTF("Invalid encryption key size %d\n", key_size * 8);
776 : 0 : err = -EINVAL;
777 : 0 : goto out;
778 : : }
779 : :
780 : 0 : DPRINTF("Encryption key for VHD is %d bits\n", key_size * 8);
781 : 0 : encryption_key = malloc(key_size);
782 [ # # ]: 0 : if (!encryption_key) {
783 : 0 : err = -ENOMEM;
784 : 0 : goto out;
785 : : }
786 [ # # ]: 0 : ret = read(conn->fd, encryption_key, key_size);
787 [ # # ]: 0 : if (ret != key_size) {
788 : 0 : err = -EIO;
789 : 0 : free(encryption_key);
790 : 0 : goto out;
791 : : }
792 : 0 : DPRINTF("Read encryption key for VHD\n");
793 : 0 : vbd->encryption.key_size = key_size;
794 : 0 : vbd->encryption.encryption_key = encryption_key;
795 : : }
796 : :
797 : 0 : err = tapdisk_vbd_open_vdi(vbd, request->u.params.path, flags,
798 : 0 : request->u.params.prt_devnum);
799 [ # # ]: 0 : if (err)
800 : 0 : goto out;
801 : :
802 : 0 : err = tapdisk_vbd_get_disk_info(vbd, &vbd->disk_info);
803 [ # # ]: 0 : if (err) {
804 : 0 : EPRINTF("VBD %d failed to get disk info: %s\n", vbd->uuid,
805 : : strerror(-err));
806 : 0 : goto fail_close;
807 : : }
808 : :
809 [ # # ]: 0 : if (request->u.params.req_timeout > 0) {
810 : 0 : vbd->req_timeout = request->u.params.req_timeout;
811 : 0 : DPRINTF("Set request timeout to %d s\n", vbd->req_timeout);
812 : : }
813 : :
814 : : /*
815 : : * For now, let's do this automatically on all 'open' calls. In the
816 : : * future, we'll probably want a separate call to start the NBD server
817 : : */
818 : 0 : err = tapdisk_vbd_start_nbdservers(vbd);
819 [ # # ]: 0 : if (err) {
820 : 0 : EPRINTF("failed to start NBD server: %d\n", err);
821 : 0 : goto fail_close;
822 : : }
823 : :
824 : : err = 0;
825 : :
826 : 0 : out:
827 [ # # ]: 0 : if (!err) {
828 : 0 : response->u.image.sectors = vbd->disk_info.size;
829 : 0 : response->u.image.sector_size = vbd->disk_info.sector_size;
830 : 0 : response->u.image.info = vbd->disk_info.info;
831 : 0 : response->type = TAPDISK_MESSAGE_OPEN_RSP;
832 : : }
833 : 0 : return err;
834 : :
835 : 0 : fail_close:
836 : 0 : tapdisk_vbd_close_vdi(vbd);
837 : :
838 [ # # ]: 0 : if (vbd->name) {
839 : 0 : free(vbd->name);
840 : 0 : vbd->name = NULL;
841 : : }
842 : :
843 : 0 : goto out;
844 : : }
845 : :
846 : : static int
847 : 0 : tapdisk_control_close_image(struct tapdisk_ctl_conn *conn,
848 : : tapdisk_message_t *request, tapdisk_message_t * const response)
849 : : {
850 : 0 : td_vbd_t *vbd;
851 : 0 : int err = 0;
852 : 0 : struct td_xenblkif *blkif, *_blkif;
853 : :
854 [ # # ]: 0 : ASSERT(conn);
855 [ # # ]: 0 : ASSERT(request);
856 [ # # ]: 0 : ASSERT(response);
857 : :
858 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
859 [ # # ]: 0 : if (!vbd) {
860 : 0 : EPRINTF("VBD %d does not exist", request->cookie);
861 : 0 : err = -ENODEV;
862 : 0 : goto out;
863 : : }
864 : :
865 [ # # ]: 0 : if (td_flag_test(vbd->state, TD_VBD_PAUSED))
866 : 0 : EPRINTF("closing paused VBD %d", request->cookie);
867 : :
868 [ # # ]: 0 : if (!list_empty(&vbd->failed_requests))
869 : 0 : EPRINTF("closing VBD %d with failed requests\n", request->cookie);
870 : :
871 [ # # ]: 0 : if (vbd->nbdserver)
872 : 0 : tapdisk_nbdserver_pause(vbd->nbdserver, true);
873 [ # # ]: 0 : if (vbd->nbdserver_new)
874 : 0 : tapdisk_nbdserver_pause(vbd->nbdserver_new, true);
875 : :
876 : 0 : err = 0;
877 [ # # ]: 0 : list_for_each_entry_safe(blkif, _blkif, &vbd->rings, entry) {
878 : 0 : DPRINTF("implicitly disconnecting ring %p domid=%d, devid=%d\n",
879 : : blkif, blkif->domid, blkif->devid);
880 : :
881 : 0 : err = tapdisk_xenblkif_disconnect(blkif->domid, blkif->devid);
882 [ # # ]: 0 : if (unlikely(err)) {
883 : 0 : EPRINTF("failed to disconnect ring %p: %s\n",
884 : : blkif, strerror(-err));
885 : 0 : break;
886 : : }
887 : : }
888 : :
889 [ # # ]: 0 : if (unlikely(err))
890 : 0 : goto out;
891 : :
892 [ # # ]: 0 : if(request->type != TAPDISK_MESSAGE_FORCE_SHUTDOWN) {
893 : :
894 : : /*
895 : : * Wait for requests against dead rings to complete, otherwise, if we
896 : : * proceed with tearing down the VBD, we will free memory that will later
897 : : * be accessed by these requests, and this will lead to a crash.
898 : : */
899 [ # # ]: 0 : while (unlikely(tapdisk_vbd_contains_dead_rings(vbd)))
900 : 0 : tapdisk_server_iterate();
901 : : }
902 : : else {
903 : 0 : DPRINTF("Ignoring dead rings in forced shutdown mode\n");
904 : : }
905 : :
906 : 0 : if (err)
907 : : ERR(err, "failure closing image\n");
908 : :
909 : 0 : if (err == -ENOTTY) {
910 : :
911 : : while (!list_empty(&vbd->pending_requests))
912 : : tapdisk_server_iterate();
913 : :
914 : : err = 0;
915 : : }
916 : :
917 : 0 : if (err)
918 : : goto out;
919 : :
920 [ # # ]: 0 : if (vbd->nbdserver) {
921 : 0 : tapdisk_nbdserver_free(vbd->nbdserver);
922 : 0 : vbd->nbdserver = NULL;
923 : : }
924 [ # # ]: 0 : if (vbd->nbdserver_new) {
925 : 0 : tapdisk_nbdserver_free(vbd->nbdserver_new);
926 : 0 : vbd->nbdserver_new = NULL;
927 : : }
928 : :
929 : 0 : tapdisk_vbd_close_vdi(vbd);
930 : :
931 : : /*
932 : : * NB: vbd->name free should probably belong into close_vdi, but the
933 : : * current blktap1 reopen-stuff likely depends on a lifetime extended
934 : : * until shutdown
935 : : */
936 : 0 : free(vbd->name);
937 : 0 : vbd->name = NULL;
938 : :
939 : 0 : out:
940 : 0 : response->cookie = request->cookie;
941 [ # # ]: 0 : if (!err)
942 : 0 : response->type = TAPDISK_MESSAGE_CLOSE_RSP;
943 : 0 : return err;
944 : : }
945 : :
946 : : static int
947 : 0 : tapdisk_control_pause_vbd(struct tapdisk_ctl_conn *conn,
948 : : tapdisk_message_t *request, tapdisk_message_t * const response)
949 : : {
950 : 0 : struct timeval now, next = { 0, 0 }, interval = { 0, 10000 };
951 : 0 : int err = 0;
952 : 0 : td_vbd_t *vbd;
953 : :
954 [ # # ]: 0 : ASSERT(conn);
955 [ # # ]: 0 : ASSERT(request);
956 [ # # ]: 0 : ASSERT(response);
957 : :
958 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
959 [ # # ]: 0 : if (!vbd) {
960 : : /* TODO log error */
961 : 0 : err = -ENODEV;
962 : 0 : goto out;
963 : : }
964 : :
965 : 0 : INFO("pause requested\n");
966 : 0 : do {
967 : 0 : gettimeofday(&now, NULL);
968 [ # # # # ]: 0 : if (TV_AFTER(now, next)) {
969 : 0 : err = tapdisk_vbd_pause(vbd);
970 : :
971 [ # # ]: 0 : if (!err || err != -EAGAIN)
972 : : break;
973 : :
974 : : /*
975 : : * Squash the logging so that we don't fill the logs
976 : : * partition
977 : : */
978 : 0 : tapdisk_vbd_squash_pause_logging(true);
979 [ # # ]: 0 : TV_ADD(now, interval, next);
980 : : }
981 : :
982 : 0 : tapdisk_server_iterate();
983 : :
984 [ # # ]: 0 : } while (conn->fd >= 0);
985 : 0 : tapdisk_vbd_squash_pause_logging(false);
986 : :
987 : 0 : out:
988 : 0 : response->cookie = request->cookie;
989 [ # # ]: 0 : if (!err)
990 : 0 : response->type = TAPDISK_MESSAGE_PAUSE_RSP;
991 : 0 : return err;
992 : : }
993 : :
994 : : static int
995 : 0 : tapdisk_control_resume_vbd(struct tapdisk_ctl_conn *conn,
996 : : tapdisk_message_t *request, tapdisk_message_t * const response)
997 : : {
998 : 0 : int err, ret;
999 : 0 : td_vbd_t *vbd;
1000 : 0 : const char *desc = NULL;
1001 : :
1002 [ # # ]: 0 : ASSERT(conn);
1003 [ # # ]: 0 : ASSERT(request);
1004 [ # # ]: 0 : ASSERT(response);
1005 : :
1006 : : /* TODO validate secondary */
1007 : :
1008 : 0 : INFO("resuming VBD %d, flags=0x%08x, secondary=%p\n", request->cookie,
1009 : : request->u.resume.flags, request->u.resume.secondary);
1010 : :
1011 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
1012 [ # # ]: 0 : if (!vbd) {
1013 : : /* TODO log error */
1014 : 0 : err = -ENODEV;
1015 : 0 : goto out;
1016 : : }
1017 : :
1018 [ # # ]: 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SECONDARY) {
1019 : 0 : char *name = strdup(request->u.params.secondary);
1020 [ # # ]: 0 : if (!name) {
1021 : 0 : err = -errno;
1022 : 0 : goto out;
1023 : : }
1024 : 0 : INFO("resuming VBD %d with secondary '%s'\n", request->cookie, name);
1025 : 0 : vbd->secondary_name = name;
1026 : 0 : vbd->flags |= TD_OPEN_SECONDARY;
1027 : :
1028 : : /* TODO If an error occurs below we're not undoing this. */
1029 : : }
1030 : :
1031 [ # # ]: 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_LOG) {
1032 : 0 : char *logpath = malloc(TAPDISK_MESSAGE_MAX_PATH_LENGTH + 1);
1033 [ # # ]: 0 : if (!logpath) {
1034 : 0 : err = -ENOMEM;
1035 : 0 : goto out;
1036 : : }
1037 [ # # ]: 0 : ret = read(conn->fd, logpath, TAPDISK_MESSAGE_MAX_PATH_LENGTH);
1038 [ # # ]: 0 : if (ret < 0) {
1039 : 0 : err = -EIO;
1040 : 0 : free(logpath);
1041 : 0 : goto out;
1042 : : }
1043 : 0 : *(logpath + TAPDISK_MESSAGE_MAX_PATH_LENGTH) = '\0';
1044 : 0 : vbd->logpath = logpath;
1045 : 0 : vbd->flags |= TD_OPEN_ADD_LOG;
1046 : : } else {
1047 [ # # ]: 0 : if (vbd->logpath) {
1048 : 0 : free (vbd->logpath);
1049 : 0 : vbd->logpath = NULL;
1050 : : }
1051 : 0 : vbd->flags &= ~TD_OPEN_ADD_LOG;
1052 : : }
1053 : :
1054 [ # # ]: 0 : if (request->u.params.path[0])
1055 : 0 : desc = request->u.params.path;
1056 : :
1057 : 0 : err = tapdisk_vbd_resume(vbd, desc);
1058 : 0 : out:
1059 : 0 : response->cookie = request->cookie;
1060 [ # # ]: 0 : if (!err)
1061 : 0 : response->type = TAPDISK_MESSAGE_RESUME_RSP;
1062 : 0 : return err;
1063 : : }
1064 : :
1065 : : static int
1066 : 0 : tapdisk_control_stats(struct tapdisk_ctl_conn *conn,
1067 : : tapdisk_message_t *request, tapdisk_message_t * const response)
1068 : : {
1069 : 0 : td_stats_t _st, *st = &_st;
1070 : 0 : td_vbd_t *vbd;
1071 : 0 : ssize_t rv;
1072 : 0 : void *buf;
1073 : 0 : int new_size;
1074 : :
1075 [ # # ]: 0 : ASSERT(conn);
1076 [ # # ]: 0 : ASSERT(request);
1077 [ # # ]: 0 : ASSERT(response);
1078 : :
1079 : 0 : buf = malloc(TD_CTL_SEND_BUFSZ);
1080 [ # # ]: 0 : if (!buf) {
1081 : 0 : rv = -ENOMEM;
1082 : 0 : return rv;
1083 : : }
1084 : :
1085 : 0 : tapdisk_stats_init(st, buf, TD_CTL_SEND_BUFSZ);
1086 : :
1087 [ # # ]: 0 : if (request->cookie != (uint16_t)-1) {
1088 : :
1089 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
1090 [ # # ]: 0 : if (!vbd) {
1091 : 0 : rv = -ENODEV;
1092 : 0 : goto out;
1093 : : }
1094 : :
1095 : 0 : tapdisk_vbd_stats(vbd, st);
1096 : :
1097 : : } else {
1098 : 0 : struct list_head *list = tapdisk_server_get_all_vbds();
1099 : :
1100 : 0 : tapdisk_stats_enter(st, '[');
1101 : :
1102 [ # # ]: 0 : list_for_each_entry(vbd, list, next)
1103 : 0 : tapdisk_vbd_stats(vbd, st);
1104 : :
1105 : 0 : tapdisk_stats_leave(st, ']');
1106 : : }
1107 : :
1108 [ # # ]: 0 : rv = tapdisk_stats_length(st);
1109 [ # # ]: 0 : if (rv < 0) {
1110 : 0 : goto out;
1111 : : }
1112 : :
1113 [ # # ]: 0 : if (rv > conn->out.bufsz - sizeof(*response)) {
1114 [ # # ]: 0 : ASSERT(conn->out.prod == conn->out.buf);
1115 [ # # ]: 0 : ASSERT(conn->out.cons == conn->out.buf);
1116 : 0 : new_size = rv + sizeof(*response);
1117 : 0 : buf = realloc(conn->out.buf, new_size);
1118 [ # # ]: 0 : if (!buf) {
1119 : 0 : rv = -ENOMEM;
1120 : 0 : goto out;
1121 : : }
1122 : 0 : conn->out.buf = buf;
1123 : 0 : conn->out.bufsz = new_size;
1124 : 0 : conn->out.prod = buf;
1125 : 0 : conn->out.cons = buf;
1126 : : }
1127 [ # # ]: 0 : if (rv > 0) {
1128 : 0 : memcpy(conn->out.buf + sizeof(*response), st->buf, rv);
1129 : : }
1130 : 0 : out:
1131 : 0 : free(st->buf);
1132 [ # # ]: 0 : if (rv > 0) {
1133 : 0 : response->type = TAPDISK_MESSAGE_STATS_RSP;
1134 : 0 : response->u.info.length = rv;
1135 : 0 : tapdisk_control_write_message(conn, response);
1136 : 0 : conn->out.prod += rv;
1137 : 0 : return 0;
1138 : : } else
1139 : 0 : return rv;
1140 : : }
1141 : :
1142 : : /**
1143 : : * Message handler executed for TAPDISK_MESSAGE_XENBLKIF_CONNECT.
1144 : : *
1145 : : * This is the entry point for connecting the tapdisk to the shared ring. It
1146 : : * also sets up the necessary structures/descriptors (TODO explain).
1147 : : */
1148 : : static int
1149 : 0 : tapdisk_control_xenblkif_connect(
1150 : : struct tapdisk_ctl_conn *conn __attribute__((unused)),
1151 : : tapdisk_message_t *request, tapdisk_message_t * const response)
1152 : : {
1153 : : /*
1154 : : * Get the block interface parameters (domain ID, device ID, etc.).
1155 : : */
1156 : 0 : tapdisk_message_blkif_t *blkif;
1157 : :
1158 : 0 : td_vbd_t *vbd = NULL;
1159 : 0 : const char *pool;
1160 : 0 : size_t len;
1161 : 0 : int err;
1162 : 0 : int minor = -1;
1163 : :
1164 [ # # ]: 0 : ASSERT(conn);
1165 [ # # ]: 0 : ASSERT(request);
1166 [ # # ]: 0 : ASSERT(response);
1167 : :
1168 : 0 : minor = request->cookie;
1169 : :
1170 : 0 : vbd = tapdisk_server_get_vbd(minor);
1171 [ # # ]: 0 : if (!vbd) {
1172 : 0 : err = -ENODEV;
1173 : 0 : goto out;
1174 : : }
1175 : :
1176 : 0 : blkif = &request->u.blkif;
1177 : 0 : len = strnlen(blkif->pool, sizeof(blkif->pool));
1178 [ # # ]: 0 : if (!len)
1179 : : pool = NULL;
1180 [ # # ]: 0 : else if (len >= sizeof(blkif->pool)) {
1181 : 0 : err = -EINVAL;
1182 : 0 : goto out;
1183 : : } else
1184 : : pool = blkif->pool;
1185 : :
1186 : 0 : DPRINTF("connecting VBD %d domid=%d, devid=%d, pool %s, evt %d, poll duration %d, poll idle threshold %d\n",
1187 : : vbd->uuid, blkif->domid, blkif->devid, pool, blkif->port, blkif->poll_duration, blkif->poll_idle_threshold);
1188 : :
1189 : 0 : err = tapdisk_xenblkif_connect(blkif->domid, blkif->devid, blkif->gref,
1190 : 0 : blkif->order, blkif->port, blkif->proto, blkif->poll_duration, blkif->poll_idle_threshold, pool, vbd);
1191 : :
1192 : 0 : out:
1193 : 0 : response->cookie = request->cookie;
1194 [ # # ]: 0 : if (!err)
1195 : 0 : response->type = TAPDISK_MESSAGE_XENBLKIF_CONNECT_RSP;
1196 : : else
1197 : 0 : EPRINTF("VBD %d failed to connect to the shared ring: %s\n",
1198 : : minor, strerror(-err));
1199 : :
1200 : 0 : return err;
1201 : : }
1202 : :
1203 : : static int
1204 : 0 : tapdisk_control_xenblkif_disconnect(
1205 : : struct tapdisk_ctl_conn *conn __attribute__((unused)),
1206 : : tapdisk_message_t * request, tapdisk_message_t * const response)
1207 : : {
1208 : 0 : tapdisk_message_blkif_t *blkif_msg;
1209 : 0 : int err;
1210 : :
1211 [ # # ]: 0 : ASSERT(request);
1212 [ # # ]: 0 : ASSERT(response);
1213 : :
1214 : 0 : blkif_msg = &request->u.blkif;
1215 : :
1216 : 0 : ASSERT(blkif_msg);
1217 : :
1218 : 0 : DPRINTF("disconnecting domid=%d, devid=%d\n", blkif_msg->domid,
1219 : : blkif_msg->devid);
1220 : :
1221 : 0 : err = tapdisk_xenblkif_disconnect(blkif_msg->domid, blkif_msg->devid);
1222 [ # # ]: 0 : if (!err)
1223 : 0 : response->type = TAPDISK_MESSAGE_XENBLKIF_DISCONNECT_RSP;
1224 : : else
1225 : 0 : EPRINTF("failed to disconnect domid=%d, devid=%d from the "
1226 : : "ring: %s\n", blkif_msg->domid, blkif_msg->devid,
1227 : : strerror(-err));
1228 : 0 : return err;
1229 : : }
1230 : :
1231 : : static int
1232 : 0 : tapdisk_control_disk_info(
1233 : : struct tapdisk_ctl_conn *conn __attribute__((unused)),
1234 : : tapdisk_message_t * request, tapdisk_message_t * const response)
1235 : : {
1236 : 0 : tapdisk_message_image_t *image;
1237 : 0 : int err = 0;
1238 : 0 : td_vbd_t *vbd = NULL;
1239 : :
1240 [ # # ]: 0 : ASSERT(conn);
1241 [ # # ]: 0 : ASSERT(request);
1242 [ # # ]: 0 : ASSERT(response);
1243 : :
1244 : 0 : image = &response->u.image;
1245 : :
1246 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
1247 [ # # ]: 0 : if (!vbd) {
1248 : 0 : err = -ENODEV;
1249 : 0 : goto out;
1250 : : }
1251 : :
1252 : 0 : DPRINTF("VBD %d got disk info: sectors=%llu sector size=%ld, info=%d\n",
1253 : : vbd->uuid, (unsigned long long)vbd->disk_info.size,
1254 : : vbd->disk_info.sector_size, vbd->disk_info.info);
1255 : : out:
1256 : 0 : if (!err) {
1257 : 0 : response->type = TAPDISK_MESSAGE_DISK_INFO_RSP;
1258 : 0 : image->sectors = vbd->disk_info.size;
1259 : 0 : image->sector_size = vbd->disk_info.sector_size;
1260 : 0 : image->info = vbd->disk_info.info;
1261 : : }
1262 : 0 : return err;
1263 : : }
1264 : :
1265 : :
1266 : : struct tapdisk_control_info message_infos[] = {
1267 : : [TAPDISK_MESSAGE_PID] = {
1268 : : .handler = tapdisk_control_get_pid,
1269 : : .flags = TAPDISK_MSG_REENTER,
1270 : : },
1271 : : [TAPDISK_MESSAGE_LIST] = {
1272 : : .handler = tapdisk_control_list,
1273 : : .flags = TAPDISK_MSG_REENTER,
1274 : : },
1275 : : [TAPDISK_MESSAGE_ATTACH] = {
1276 : : .handler = tapdisk_control_attach_vbd,
1277 : : .flags = TAPDISK_MSG_VERBOSE,
1278 : : },
1279 : : [TAPDISK_MESSAGE_DETACH] = {
1280 : : .handler = tapdisk_control_detach_vbd,
1281 : : .flags = TAPDISK_MSG_VERBOSE,
1282 : : },
1283 : : [TAPDISK_MESSAGE_XENBLKIF_CONNECT] = {
1284 : : .handler = tapdisk_control_xenblkif_connect,
1285 : : .flags = TAPDISK_MSG_VERBOSE
1286 : : },
1287 : : [TAPDISK_MESSAGE_XENBLKIF_DISCONNECT] = {
1288 : : .handler = tapdisk_control_xenblkif_disconnect,
1289 : : .flags = TAPDISK_MSG_VERBOSE
1290 : : },
1291 : : [TAPDISK_MESSAGE_DISK_INFO] = {
1292 : : .handler = tapdisk_control_disk_info,
1293 : : .flags = TAPDISK_MSG_VERBOSE
1294 : : },
1295 : : [TAPDISK_MESSAGE_OPEN] = {
1296 : : .handler = tapdisk_control_open_image,
1297 : : .flags = TAPDISK_MSG_VERBOSE,
1298 : : },
1299 : : [TAPDISK_MESSAGE_PAUSE] = {
1300 : : .handler = tapdisk_control_pause_vbd,
1301 : : .flags = TAPDISK_MSG_VERBOSE,
1302 : : },
1303 : : [TAPDISK_MESSAGE_RESUME] = {
1304 : : .handler = tapdisk_control_resume_vbd,
1305 : : .flags = TAPDISK_MSG_VERBOSE,
1306 : : },
1307 : : [TAPDISK_MESSAGE_CLOSE] = {
1308 : : .handler = tapdisk_control_close_image,
1309 : : .flags = TAPDISK_MSG_VERBOSE,
1310 : : },
1311 : : [TAPDISK_MESSAGE_FORCE_SHUTDOWN] = {
1312 : : .handler = tapdisk_control_close_image,
1313 : : .flags = TAPDISK_MSG_VERBOSE,
1314 : : },
1315 : : [TAPDISK_MESSAGE_STATS] = {
1316 : : .handler = tapdisk_control_stats,
1317 : : .flags = TAPDISK_MSG_REENTER,
1318 : : },
1319 : : [TAPDISK_MESSAGE_EXIT] = {
1320 : : .handler = NULL,
1321 : : .flags = 0
1322 : : }
1323 : : };
1324 : :
1325 : : static int
1326 : 0 : tapdisk_control_receive_request(struct tapdisk_ctl_conn *conn)
1327 : : {
1328 : 0 : int err;
1329 : :
1330 [ # # ]: 0 : ASSERT(conn);
1331 : :
1332 : 0 : err = tapdisk_control_read_message(conn->fd, &conn->request, 2);
1333 [ # # ]: 0 : if (err)
1334 : 0 : goto close;
1335 : :
1336 [ # # ]: 0 : err = tapdisk_control_validate_request(&conn->request);
1337 : 0 : if (err)
1338 : 0 : goto invalid;
1339 : :
1340 [ # # ]: 0 : if (conn->request.type > TAPDISK_MESSAGE_EXIT)
1341 : 0 : goto invalid;
1342 : :
1343 : 0 : conn->info = &message_infos[conn->request.type];
1344 : :
1345 [ # # ]: 0 : if (!conn->info->handler)
1346 : 0 : goto invalid;
1347 : :
1348 [ # # ]: 0 : if (conn->info->flags & TAPDISK_MSG_VERBOSE)
1349 : 0 : DBG("received '%s' message (uuid = %u)\n",
1350 : : tapdisk_message_name(conn->request.type), conn->request.cookie);
1351 : :
1352 [ # # ]: 0 : if (conn->in.busy)
1353 : 0 : goto busy;
1354 : :
1355 : : return 0;
1356 : :
1357 : 0 : error:
1358 : 0 : memset(&conn->response, 0, sizeof(conn->response));
1359 : 0 : conn->response.type = TAPDISK_MESSAGE_ERROR;
1360 : 0 : conn->response.u.response.error = -err;
1361 : 0 : tapdisk_control_write_message(conn, &conn->response);
1362 : :
1363 : 0 : close:
1364 : 0 : tapdisk_control_close_connection(conn);
1365 : 0 : return err;
1366 : :
1367 : 0 : busy:
1368 : 0 : err = -EBUSY;
1369 : 0 : ERR(err, "rejecting message '%s' while busy\n",
1370 : : tapdisk_message_name(conn->request.type));
1371 : 0 : goto error;
1372 : :
1373 : 0 : invalid:
1374 : 0 : err = -EINVAL;
1375 : 0 : ERR(err, "rejecting unsupported message '%s'\n",
1376 : : tapdisk_message_name(conn->request.type));
1377 : 0 : goto error;
1378 : : }
1379 : :
1380 : : static void
1381 : 0 : tapdisk_control_process_request(event_id_t event_id,
1382 : : char mode __attribute__((unused)), void *private)
1383 : : {
1384 : 0 : int err, excl;
1385 : 0 : struct tapdisk_ctl_conn *conn = private;
1386 : :
1387 [ # # ]: 0 : ASSERT(conn);
1388 [ # # ]: 0 : ASSERT(event_id == conn->event_id);
1389 : :
1390 [ # # ]: 0 : if (conn->event_id)
1391 : 0 : tapdisk_server_unregister_event(conn->event_id);
1392 : :
1393 : 0 : excl = !(conn->info->flags & TAPDISK_MSG_REENTER);
1394 : :
1395 [ # # ]: 0 : if (excl)
1396 : 0 : td_control.busy = 1;
1397 : 0 : conn->in.busy = 1;
1398 : :
1399 : 0 : memset(&conn->response, 0, sizeof(conn->response));
1400 : 0 : conn->response.cookie = conn->request.cookie;
1401 : :
1402 : 0 : err = conn->info->handler(conn, &conn->request, &conn->response);
1403 [ # # ]: 0 : if (err) {
1404 : 0 : conn->response.type = TAPDISK_MESSAGE_ERROR;
1405 : 0 : conn->response.u.response.error = -err;
1406 : : }
1407 [ # # ]: 0 : if (err || conn->response.type != TAPDISK_MESSAGE_STATS_RSP)
1408 : 0 : tapdisk_control_write_message(conn, &conn->response);
1409 : :
1410 : 0 : conn->in.busy = 0;
1411 [ # # ]: 0 : if (excl) {
1412 [ # # ]: 0 : if (!list_empty(&td_control.pending)) {
1413 : 0 : struct tapdisk_ctl_conn *cur = list_first_entry(
1414 : : &td_control.pending, struct tapdisk_ctl_conn, entry);
1415 : 0 : list_del(&cur->entry);
1416 : 0 : err = tapdisk_server_event_set_timeout(cur->event_id, TV_ZERO);
1417 [ # # ]: 0 : ASSERT(!err);
1418 : 0 : tapdisk_server_mask_event(cur->event_id, 0);
1419 : : }
1420 : 0 : td_control.busy = 0;
1421 : : }
1422 : :
1423 : 0 : tapdisk_control_release_connection(conn);
1424 : 0 : }
1425 : :
1426 : :
1427 : : static void
1428 : 0 : tapdisk_control_handle_request(event_id_t id, char mode, void *private)
1429 : : {
1430 : 0 : int err;
1431 : 0 : struct tapdisk_ctl_conn *conn = private;
1432 : :
1433 : 0 : conn->info = NULL;
1434 : :
1435 : 0 : err = tapdisk_control_receive_request(conn);
1436 [ # # ]: 0 : if (err)
1437 : : return;
1438 : :
1439 : 0 : conn->event_id = 0;
1440 : :
1441 [ # # # # ]: 0 : if (!(conn->info->flags & TAPDISK_MSG_REENTER) && td_control.busy) {
1442 : :
1443 : 0 : err = tapdisk_server_register_event(SCHEDULER_POLL_TIMEOUT, -1,
1444 : 0 : TV_INF, tapdisk_control_process_request,
1445 : : conn);
1446 : :
1447 [ # # ]: 0 : if (err == -1) {
1448 : 0 : memset(&conn->response, 0, sizeof(conn->response));
1449 : 0 : conn->response.type = TAPDISK_MESSAGE_ERROR;
1450 : 0 : conn->response.u.response.error = EINVAL;
1451 : 0 : tapdisk_control_write_message(conn, &conn->response);
1452 : 0 : tapdisk_control_close_connection(conn);
1453 : 0 : ERR(err, "failed to register request process event\n");
1454 : 0 : return;
1455 : : }
1456 : :
1457 : 0 : tapdisk_server_unregister_event(conn->in.event_id);
1458 : 0 : conn->in.event_id = 0;
1459 : :
1460 : 0 : conn->event_id = err;
1461 : 0 : tapdisk_server_mask_event(conn->event_id, 1);
1462 : 0 : list_add_tail(&conn->entry, &td_control.pending);
1463 : :
1464 : 0 : return;
1465 : : }
1466 : :
1467 : 0 : tapdisk_control_process_request(0, 0, conn);
1468 : : }
1469 : :
1470 : : static void
1471 : 0 : tapdisk_control_accept(event_id_t id, char mode, void *private)
1472 : : {
1473 : 0 : int err, fd;
1474 : 0 : struct tapdisk_ctl_conn *conn;
1475 : :
1476 : 0 : fd = accept(td_control.socket, NULL, NULL);
1477 [ # # ]: 0 : if (fd == -1) {
1478 : 0 : ERR(-errno, "failed to accept new control connection: %d\n", errno);
1479 : 0 : return;
1480 : : }
1481 : :
1482 : 0 : conn = tapdisk_ctl_conn_open(fd);
1483 [ # # ]: 0 : if (!conn) {
1484 : 0 : close(fd);
1485 : 0 : ERR(-ENOMEM, "failed to allocate new control connection\n");
1486 : 0 : return;
1487 : : }
1488 : :
1489 : 0 : err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
1490 : 0 : conn->fd, TV_SECS(TD_CTL_RECV_TIMEOUT),
1491 : : tapdisk_control_handle_request,
1492 : : conn);
1493 [ # # ]: 0 : if (err == -1) {
1494 : 0 : tapdisk_control_close_connection(conn);
1495 : 0 : ERR(err, "failed to register new control event\n");
1496 : 0 : return;
1497 : : }
1498 : :
1499 : 0 : conn->in.event_id = err;
1500 : : }
1501 : :
1502 : : static int
1503 : 0 : tapdisk_control_mkdir(const char *dir)
1504 : : {
1505 : 0 : int err;
1506 : 0 : char *ptr, *name, *start;
1507 : :
1508 : 0 : err = access(dir, W_OK | R_OK);
1509 [ # # ]: 0 : if (!err)
1510 : : return 0;
1511 : :
1512 : 0 : name = strdup(dir);
1513 [ # # ]: 0 : if (!name)
1514 : : return -ENOMEM;
1515 : :
1516 : : start = name;
1517 : :
1518 : 0 : for (;;) {
1519 : 0 : ptr = strchr(start + 1, '/');
1520 [ # # ]: 0 : if (ptr)
1521 : 0 : *ptr = '\0';
1522 : :
1523 : 0 : err = mkdir(name, 0755);
1524 [ # # ]: 0 : if (err) {
1525 [ # # ]: 0 : if (errno != EEXIST) {
1526 : 0 : err = -errno;
1527 : 0 : EPRINTF("failed to create directory %s: %d\n",
1528 : : name, err);
1529 : 0 : break;
1530 : : }
1531 : : else
1532 : : err = 0;
1533 : : }
1534 : :
1535 [ # # ]: 0 : if (!ptr)
1536 : : break;
1537 : : else {
1538 : 0 : *ptr = '/';
1539 : 0 : start = ptr + 1;
1540 : : }
1541 : : }
1542 : :
1543 : 0 : free(name);
1544 : 0 : return err;
1545 : : }
1546 : :
1547 : : static int
1548 : 0 : tapdisk_control_create_socket(char **socket_path)
1549 : : {
1550 : 0 : struct sockaddr_un saddr;
1551 : 0 : int err;
1552 : :
1553 : 0 : err = tapdisk_control_mkdir(BLKTAP2_CONTROL_DIR);
1554 [ # # ]: 0 : if (err) {
1555 : 0 : EPRINTF("failed to create directory %s: %d\n",
1556 : : BLKTAP2_CONTROL_DIR, err);
1557 : 0 : return err;
1558 : : }
1559 : :
1560 : 0 : err = asprintf(&td_control.path, "%s/%s%d",
1561 : : BLKTAP2_CONTROL_DIR, BLKTAP2_CONTROL_SOCKET, getpid());
1562 [ # # ]: 0 : if (err == -1) {
1563 : 0 : td_control.path = NULL;
1564 [ # # ]: 0 : err = (errno ? : ENOMEM);
1565 : 0 : goto fail;
1566 : : }
1567 : :
1568 [ # # # # ]: 0 : if (unlink(td_control.path) && errno != ENOENT) {
1569 : 0 : err = errno;
1570 : 0 : EPRINTF("failed to unlink %s: %d\n", td_control.path, errno);
1571 : 0 : goto fail;
1572 : : }
1573 : :
1574 : 0 : td_control.socket = socket(AF_UNIX, SOCK_STREAM, 0);
1575 [ # # ]: 0 : if (td_control.socket == -1) {
1576 : 0 : err = errno;
1577 : 0 : EPRINTF("failed to create control socket: %d\n", err);
1578 : 0 : goto fail;
1579 : : }
1580 : :
1581 [ # # ]: 0 : memset(&saddr, 0, sizeof(saddr));
1582 : :
1583 [ # # ]: 0 : if (unlikely(strlen(td_control.path) >= sizeof(saddr.sun_path))) {
1584 : 0 : EPRINTF("socket name too long: %s\n", td_control.path);
1585 : 0 : err = ENAMETOOLONG;
1586 : 0 : goto fail;
1587 : : }
1588 : 0 : safe_strncpy(saddr.sun_path, td_control.path, sizeof(saddr.sun_path));
1589 : :
1590 : 0 : saddr.sun_family = AF_UNIX;
1591 : :
1592 : 0 : err = bind(td_control.socket,
1593 : : (const struct sockaddr *)&saddr, sizeof(saddr));
1594 [ # # ]: 0 : if (err == -1) {
1595 : 0 : err = errno;
1596 : 0 : EPRINTF("failed to bind to %s: %d\n", saddr.sun_path, err);
1597 : 0 : goto fail;
1598 : : }
1599 : :
1600 : 0 : err = listen(td_control.socket, TD_CTL_SOCK_BACKLOG);
1601 [ # # ]: 0 : if (err == -1) {
1602 : 0 : err = errno;
1603 : 0 : EPRINTF("failed to listen: %d\n", err);
1604 : 0 : goto fail;
1605 : : }
1606 : :
1607 : 0 : err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
1608 : 0 : td_control.socket, TV_ZERO,
1609 : : tapdisk_control_accept, NULL);
1610 [ # # ]: 0 : if (err < 0) {
1611 : 0 : EPRINTF("failed to add watch: %d\n", err);
1612 : 0 : goto fail;
1613 : : }
1614 : :
1615 : 0 : td_control.event_id = err;
1616 : 0 : *socket_path = td_control.path;
1617 : :
1618 : 0 : return 0;
1619 : :
1620 : 0 : fail:
1621 : 0 : tapdisk_control_close();
1622 : : return err;
1623 : : }
1624 : :
1625 : : int
1626 : 0 : tapdisk_control_open(char **path)
1627 : : {
1628 : 0 : tapdisk_control_initialize();
1629 : :
1630 : 0 : return tapdisk_control_create_socket(path);
1631 : : }
|