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