root / drivers / tapdisk-control.c @ abdb293f
History | View | Annotate | Download (25.6 kB)
1 |
/*
|
---|---|
2 |
* Copyright (c) 2008, XenSource Inc.
|
3 |
* All rights reserved.
|
4 |
*
|
5 |
* Redistribution and use in source and binary forms, with or without
|
6 |
* modification, are permitted provided that the following conditions are met:
|
7 |
* * Redistributions of source code must retain the above copyright
|
8 |
* notice, this list of conditions and the following disclaimer.
|
9 |
* * Redistributions in binary form must reproduce the above copyright
|
10 |
* notice, this list of conditions and the following disclaimer in the
|
11 |
* documentation and/or other materials provided with the distribution.
|
12 |
* * Neither the name of XenSource Inc. nor the names of its contributors
|
13 |
* may be used to endorse or promote products derived from this software
|
14 |
* without specific prior written permission.
|
15 |
*
|
16 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
17 |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
18 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
19 |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
20 |
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
21 |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
22 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
23 |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
24 |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
25 |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
26 |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27 |
*/
|
28 |
|
29 |
#ifdef HAVE_CONFIG_H
|
30 |
#include "config.h" |
31 |
#endif
|
32 |
|
33 |
#include <stdio.h> |
34 |
#include <errno.h> |
35 |
#include <fcntl.h> |
36 |
#include <unistd.h> |
37 |
#include <stdlib.h> |
38 |
#include <string.h> |
39 |
#include <signal.h> |
40 |
#include <sys/un.h> |
41 |
#include <sys/stat.h> |
42 |
#include <sys/types.h> |
43 |
#include <sys/ioctl.h> |
44 |
#include <sys/socket.h> |
45 |
#include <sys/mman.h> |
46 |
|
47 |
#include "list.h" |
48 |
#include "tapdisk.h" |
49 |
#include "tapdisk-vbd.h" |
50 |
#include "tapdisk-blktap.h" |
51 |
#include "tapdisk-utils.h" |
52 |
#include "tapdisk-server.h" |
53 |
#include "tapdisk-message.h" |
54 |
#include "tapdisk-disktype.h" |
55 |
#include "tapdisk-stats.h" |
56 |
#include "tapdisk-control.h" |
57 |
|
58 |
#define TD_CTL_MAX_CONNECTIONS 10 |
59 |
#define TD_CTL_SOCK_BACKLOG 32 |
60 |
#define TD_CTL_RECV_TIMEOUT 10 |
61 |
#define TD_CTL_SEND_TIMEOUT 10 |
62 |
#define TD_CTL_SEND_BUFSZ ((size_t)4096) |
63 |
|
64 |
#define DBG(_f, _a...) tlog_syslog(LOG_DEBUG, _f, ##_a) |
65 |
#define ERR(err, _f, _a...) tlog_error(err, _f, ##_a) |
66 |
|
67 |
#define ASSERT(_p) \
|
68 |
if (!(_p)) { \
|
69 |
EPRINTF("%s:%d: FAILED ASSERTION: '%s'\n", \
|
70 |
__FILE__, __LINE__, #_p); \
|
71 |
td_panic(); \ |
72 |
} |
73 |
|
74 |
#define WARN_ON(_p) \
|
75 |
if (_p) { \
|
76 |
EPRINTF("%s:%d: WARNING: '%s'\n", \
|
77 |
__FILE__, __LINE__, #_p); \
|
78 |
} |
79 |
|
80 |
struct tapdisk_ctl_conn {
|
81 |
int fd;
|
82 |
|
83 |
struct {
|
84 |
void *buf;
|
85 |
size_t bufsz; |
86 |
int event_id;
|
87 |
int done;
|
88 |
|
89 |
void *prod;
|
90 |
void *cons;
|
91 |
} out; |
92 |
|
93 |
struct {
|
94 |
int event_id;
|
95 |
int busy;
|
96 |
} in; |
97 |
|
98 |
struct tapdisk_control_info *info;
|
99 |
}; |
100 |
|
101 |
#define TAPDISK_MSG_REENTER (1<<0) /* non-blocking, idempotent */ |
102 |
#define TAPDISK_MSG_VERBOSE (1<<1) /* tell syslog about it */ |
103 |
|
104 |
struct tapdisk_control_info {
|
105 |
void (*handler)(struct tapdisk_ctl_conn *, tapdisk_message_t *); |
106 |
int flags;
|
107 |
}; |
108 |
|
109 |
struct tapdisk_control {
|
110 |
char *path;
|
111 |
int uuid;
|
112 |
int socket;
|
113 |
int event_id;
|
114 |
int busy;
|
115 |
|
116 |
int n_conn;
|
117 |
struct tapdisk_ctl_conn __conn[TD_CTL_MAX_CONNECTIONS];
|
118 |
struct tapdisk_ctl_conn *conn[TD_CTL_MAX_CONNECTIONS];
|
119 |
}; |
120 |
|
121 |
static struct tapdisk_control td_control; |
122 |
|
123 |
static inline size_t |
124 |
page_align(size_t size) |
125 |
{ |
126 |
size_t page_size = sysconf(_SC_PAGE_SIZE); |
127 |
return (size + page_size - 1) & ~(page_size - 1); |
128 |
} |
129 |
|
130 |
static void |
131 |
tapdisk_ctl_conn_uninit(struct tapdisk_ctl_conn *conn)
|
132 |
{ |
133 |
if (conn->out.buf) {
|
134 |
munmap(conn->out.buf, conn->out.bufsz); |
135 |
conn->out.buf = NULL;
|
136 |
} |
137 |
} |
138 |
|
139 |
static int |
140 |
tapdisk_ctl_conn_init(struct tapdisk_ctl_conn *conn, size_t bufsz)
|
141 |
{ |
142 |
int prot, flags, err;
|
143 |
|
144 |
memset(conn, 0, sizeof(*conn)); |
145 |
conn->out.event_id = -1;
|
146 |
conn->in.event_id = -1;
|
147 |
|
148 |
prot = PROT_READ|PROT_WRITE; |
149 |
flags = MAP_ANONYMOUS|MAP_PRIVATE; |
150 |
|
151 |
conn->out.buf = mmap(NULL, bufsz, prot, flags, -1, 0); |
152 |
if (conn->out.buf == MAP_FAILED) {
|
153 |
conn->out.buf = NULL;
|
154 |
err = -ENOMEM; |
155 |
goto fail;
|
156 |
} |
157 |
conn->out.bufsz = page_align(bufsz); |
158 |
|
159 |
return 0; |
160 |
|
161 |
fail:
|
162 |
tapdisk_ctl_conn_uninit(conn); |
163 |
return err;
|
164 |
} |
165 |
|
166 |
static int |
167 |
tapdisk_ctl_conn_connected(struct tapdisk_ctl_conn *conn)
|
168 |
{ |
169 |
return conn->fd >= 1; |
170 |
} |
171 |
|
172 |
static void |
173 |
tapdisk_ctl_conn_free(struct tapdisk_ctl_conn *conn)
|
174 |
{ |
175 |
struct tapdisk_ctl_conn *prev, *next;
|
176 |
int i;
|
177 |
|
178 |
i = --td_control.n_conn; |
179 |
/* NB. bubble the freed connection off the active list. */
|
180 |
prev = conn; |
181 |
do {
|
182 |
ASSERT(i >= 0);
|
183 |
next = td_control.conn[i]; |
184 |
td_control.conn[i] = prev; |
185 |
prev = next; |
186 |
i--; |
187 |
} while (next != conn);
|
188 |
} |
189 |
|
190 |
static void |
191 |
tapdisk_ctl_conn_close(struct tapdisk_ctl_conn *conn)
|
192 |
{ |
193 |
if (conn->out.event_id >= 0) { |
194 |
tapdisk_server_unregister_event(conn->out.event_id); |
195 |
conn->out.event_id = -1;
|
196 |
} |
197 |
|
198 |
if (conn->fd >= 0) { |
199 |
close(conn->fd); |
200 |
conn->fd = -1;
|
201 |
|
202 |
tapdisk_ctl_conn_free(conn); |
203 |
tapdisk_server_mask_event(td_control.event_id, 0);
|
204 |
} |
205 |
} |
206 |
|
207 |
static void |
208 |
tapdisk_ctl_conn_mask_out(struct tapdisk_ctl_conn *conn)
|
209 |
{ |
210 |
tapdisk_server_mask_event(conn->out.event_id, 1);
|
211 |
} |
212 |
|
213 |
static void |
214 |
tapdisk_ctl_conn_unmask_out(struct tapdisk_ctl_conn *conn)
|
215 |
{ |
216 |
tapdisk_server_mask_event(conn->out.event_id, 0);
|
217 |
} |
218 |
|
219 |
static ssize_t
|
220 |
tapdisk_ctl_conn_send_buf(struct tapdisk_ctl_conn *conn)
|
221 |
{ |
222 |
ssize_t size; |
223 |
|
224 |
size = conn->out.prod - conn->out.cons; |
225 |
if (!size)
|
226 |
return 0; |
227 |
|
228 |
size = send(conn->fd, conn->out.cons, size, MSG_DONTWAIT); |
229 |
if (size < 0) |
230 |
return -errno;
|
231 |
|
232 |
conn->out.cons += size; |
233 |
|
234 |
return size;
|
235 |
} |
236 |
|
237 |
static void |
238 |
tapdisk_ctl_conn_send_event(event_id_t id, char mode, void *private) |
239 |
{ |
240 |
struct tapdisk_ctl_conn *conn = private;
|
241 |
ssize_t rv; |
242 |
|
243 |
do {
|
244 |
rv = tapdisk_ctl_conn_send_buf(conn); |
245 |
} while (rv > 0); |
246 |
|
247 |
if (rv == -EAGAIN)
|
248 |
return;
|
249 |
|
250 |
if (rv < 0) |
251 |
ERR(rv, "failure sending message at offset %td/%td\n",
|
252 |
conn->out.cons - conn->out.buf, |
253 |
conn->out.prod - conn->out.buf); |
254 |
|
255 |
if (rv || conn->out.done || mode & SCHEDULER_POLL_TIMEOUT)
|
256 |
tapdisk_ctl_conn_close(conn); |
257 |
else
|
258 |
tapdisk_ctl_conn_mask_out(conn); |
259 |
} |
260 |
|
261 |
/*
|
262 |
* NB. the control interface is still not properly integrated into the
|
263 |
* server, therefore neither the scheduler. After the last close, the
|
264 |
* server will exit but we still have a pending close response in the
|
265 |
* output buffer.
|
266 |
*/
|
267 |
static void |
268 |
tapdisk_ctl_conn_drain(struct tapdisk_ctl_conn *conn)
|
269 |
{ |
270 |
struct timeval tv = { .tv_sec = TD_CTL_SEND_TIMEOUT,
|
271 |
.tv_usec = 0 };
|
272 |
fd_set wfds; |
273 |
int n, mode;
|
274 |
|
275 |
ASSERT(conn->out.done); |
276 |
ASSERT(conn->fd >= 0);
|
277 |
|
278 |
while (tapdisk_ctl_conn_connected(conn)) {
|
279 |
FD_ZERO(&wfds); |
280 |
FD_SET(conn->fd, &wfds); |
281 |
|
282 |
n = select(conn->fd + 1, NULL, &wfds, NULL, &tv); |
283 |
if (n < 0) |
284 |
break;
|
285 |
|
286 |
if (n)
|
287 |
mode = SCHEDULER_POLL_WRITE_FD; |
288 |
else
|
289 |
mode = SCHEDULER_POLL_TIMEOUT; |
290 |
|
291 |
tapdisk_ctl_conn_send_event(conn->out.event_id, mode, conn); |
292 |
} |
293 |
} |
294 |
|
295 |
|
296 |
struct tapdisk_ctl_conn *
|
297 |
tapdisk_ctl_conn_open(int fd)
|
298 |
{ |
299 |
struct tapdisk_ctl_conn *conn;
|
300 |
|
301 |
if (td_control.n_conn >= TD_CTL_MAX_CONNECTIONS)
|
302 |
return NULL; |
303 |
|
304 |
conn = td_control.conn[td_control.n_conn++]; |
305 |
|
306 |
conn->out.event_id = |
307 |
tapdisk_server_register_event(SCHEDULER_POLL_WRITE_FD, |
308 |
fd, TD_CTL_SEND_TIMEOUT, |
309 |
tapdisk_ctl_conn_send_event, |
310 |
conn); |
311 |
if (conn->out.event_id < 0) |
312 |
return NULL; |
313 |
|
314 |
conn->fd = fd; |
315 |
conn->out.prod = conn->out.buf; |
316 |
conn->out.cons = conn->out.buf; |
317 |
|
318 |
tapdisk_ctl_conn_mask_out(conn); |
319 |
|
320 |
if (td_control.n_conn >= TD_CTL_MAX_CONNECTIONS)
|
321 |
tapdisk_server_mask_event(td_control.event_id, 1);
|
322 |
|
323 |
return conn;
|
324 |
} |
325 |
|
326 |
static size_t
|
327 |
tapdisk_ctl_conn_write(struct tapdisk_ctl_conn *conn, void *buf, size_t size) |
328 |
{ |
329 |
size_t rest; |
330 |
|
331 |
rest = conn->out.buf + conn->out.bufsz - conn->out.prod; |
332 |
if (rest < size)
|
333 |
size = rest; |
334 |
if (!size)
|
335 |
return 0; |
336 |
|
337 |
memcpy(conn->out.prod, buf, size); |
338 |
conn->out.prod += size; |
339 |
tapdisk_ctl_conn_unmask_out(conn); |
340 |
|
341 |
return size;
|
342 |
} |
343 |
|
344 |
static void |
345 |
tapdisk_ctl_conn_release(struct tapdisk_ctl_conn *conn)
|
346 |
{ |
347 |
conn->out.done = 1;
|
348 |
|
349 |
if (conn->out.prod == conn->out.cons)
|
350 |
tapdisk_ctl_conn_close(conn); |
351 |
} |
352 |
|
353 |
static void |
354 |
tapdisk_control_initialize(void)
|
355 |
{ |
356 |
struct tapdisk_ctl_conn *conn;
|
357 |
int i;
|
358 |
|
359 |
td_control.socket = -1;
|
360 |
td_control.event_id = -1;
|
361 |
|
362 |
signal(SIGPIPE, SIG_IGN); |
363 |
|
364 |
for (i = 0; i < TD_CTL_MAX_CONNECTIONS; i++) { |
365 |
conn = &td_control.__conn[i]; |
366 |
tapdisk_ctl_conn_init(conn, TD_CTL_SEND_BUFSZ); |
367 |
td_control.conn[i] = conn; |
368 |
} |
369 |
|
370 |
td_control.n_conn = 0;
|
371 |
|
372 |
DPRINTF("tapdisk-control: init, %d x %zuk buffers\n",
|
373 |
TD_CTL_MAX_CONNECTIONS, TD_CTL_SEND_BUFSZ >> 10);
|
374 |
} |
375 |
|
376 |
void
|
377 |
tapdisk_control_close(void)
|
378 |
{ |
379 |
struct tapdisk_ctl_conn *conn;
|
380 |
int i;
|
381 |
|
382 |
DPRINTF("tapdisk-control: draining %d connections\n",
|
383 |
td_control.n_conn); |
384 |
|
385 |
while (td_control.n_conn) {
|
386 |
conn = td_control.conn[td_control.n_conn-1];
|
387 |
tapdisk_ctl_conn_drain(conn); |
388 |
} |
389 |
|
390 |
for (i = 0; i < TD_CTL_MAX_CONNECTIONS; i++) { |
391 |
conn = &td_control.__conn[i]; |
392 |
tapdisk_ctl_conn_uninit(conn); |
393 |
} |
394 |
|
395 |
DPRINTF("tapdisk-control: done\n");
|
396 |
|
397 |
if (td_control.path) {
|
398 |
unlink(td_control.path); |
399 |
free(td_control.path); |
400 |
td_control.path = NULL;
|
401 |
} |
402 |
|
403 |
if (td_control.socket != -1) { |
404 |
close(td_control.socket); |
405 |
td_control.socket = -1;
|
406 |
} |
407 |
} |
408 |
|
409 |
static void |
410 |
tapdisk_control_release_connection(struct tapdisk_ctl_conn *conn)
|
411 |
{ |
412 |
if (conn->in.event_id) {
|
413 |
tapdisk_server_unregister_event(conn->in.event_id); |
414 |
conn->in.event_id = -1;
|
415 |
} |
416 |
|
417 |
tapdisk_ctl_conn_release(conn); |
418 |
} |
419 |
|
420 |
static void |
421 |
tapdisk_control_close_connection(struct tapdisk_ctl_conn *conn)
|
422 |
{ |
423 |
tapdisk_control_release_connection(conn); |
424 |
|
425 |
if (tapdisk_ctl_conn_connected(conn))
|
426 |
/* NB. best effort for write/close sequences. */
|
427 |
tapdisk_ctl_conn_send_buf(conn); |
428 |
|
429 |
tapdisk_ctl_conn_close(conn); |
430 |
} |
431 |
|
432 |
|
433 |
static int |
434 |
tapdisk_control_read_message(int fd, tapdisk_message_t *message, int timeout) |
435 |
{ |
436 |
const int len = sizeof(tapdisk_message_t); |
437 |
fd_set readfds; |
438 |
int ret, offset, err = 0; |
439 |
struct timeval tv, *t;
|
440 |
|
441 |
t = NULL;
|
442 |
offset = 0;
|
443 |
|
444 |
if (timeout) {
|
445 |
tv.tv_sec = timeout; |
446 |
tv.tv_usec = 0;
|
447 |
t = &tv; |
448 |
} |
449 |
|
450 |
memset(message, 0, sizeof(tapdisk_message_t)); |
451 |
|
452 |
while (offset < len) {
|
453 |
FD_ZERO(&readfds); |
454 |
FD_SET(fd, &readfds); |
455 |
|
456 |
ret = select(fd + 1, &readfds, NULL, NULL, t); |
457 |
if (ret == -1) |
458 |
break;
|
459 |
else if (FD_ISSET(fd, &readfds)) { |
460 |
ret = read(fd, message + offset, len - offset); |
461 |
if (ret <= 0) |
462 |
break;
|
463 |
offset += ret; |
464 |
} else
|
465 |
break;
|
466 |
} |
467 |
|
468 |
if (ret < 0) |
469 |
err = -errno; |
470 |
else if (offset != len) |
471 |
err = -EIO; |
472 |
if (err)
|
473 |
ERR(err, "failure reading message at offset %d/%d\n",
|
474 |
offset, len); |
475 |
|
476 |
|
477 |
return err;
|
478 |
} |
479 |
|
480 |
static void |
481 |
tapdisk_control_write_message(struct tapdisk_ctl_conn *conn,
|
482 |
tapdisk_message_t *message) |
483 |
{ |
484 |
size_t size = sizeof(*message), count;
|
485 |
|
486 |
if (conn->info->flags & TAPDISK_MSG_VERBOSE)
|
487 |
DBG("sending '%s' message (uuid = %u)\n",
|
488 |
tapdisk_message_name(message->type), message->cookie); |
489 |
|
490 |
count = tapdisk_ctl_conn_write(conn, message, size); |
491 |
WARN_ON(count != size); |
492 |
} |
493 |
|
494 |
static int |
495 |
tapdisk_control_validate_request(tapdisk_message_t *request) |
496 |
{ |
497 |
if (strnlen(request->u.params.path,
|
498 |
TAPDISK_MESSAGE_MAX_PATH_LENGTH) >= |
499 |
TAPDISK_MESSAGE_MAX_PATH_LENGTH) |
500 |
return EINVAL;
|
501 |
|
502 |
return 0; |
503 |
} |
504 |
|
505 |
#if 0
|
506 |
static void
|
507 |
tapdisk_control_list_minors(struct tapdisk_ctl_conn *conn,
|
508 |
tapdisk_message_t *request)
|
509 |
{
|
510 |
int i;
|
511 |
td_vbd_t *vbd;
|
512 |
struct list_head *head;
|
513 |
tapdisk_message_t response;
|
514 |
|
515 |
i = 0;
|
516 |
memset(&response, 0, sizeof(response));
|
517 |
response.type = TAPDISK_MESSAGE_LIST_MINORS_RSP;
|
518 |
response.cookie = request->cookie;
|
519 |
|
520 |
head = tapdisk_server_get_all_vbds();
|
521 |
|
522 |
list_for_each_entry(vbd, head, next) {
|
523 |
td_blktap_t *tap = vbd->tap;
|
524 |
if (!tap)
|
525 |
continue;
|
526 |
|
527 |
response.u.minors.list[i++] = tap->minor;
|
528 |
if (i >= TAPDISK_MESSAGE_MAX_MINORS) {
|
529 |
response.type = TAPDISK_MESSAGE_ERROR;
|
530 |
response.u.response.error = ERANGE;
|
531 |
break;
|
532 |
}
|
533 |
}
|
534 |
|
535 |
response.u.minors.count = i;
|
536 |
tapdisk_ctl_conn_write(conn, &response, 2);
|
537 |
}
|
538 |
#endif
|
539 |
|
540 |
static void |
541 |
tapdisk_control_list(struct tapdisk_ctl_conn *conn, tapdisk_message_t *request)
|
542 |
{ |
543 |
td_vbd_t *vbd; |
544 |
struct list_head *head;
|
545 |
tapdisk_message_t response; |
546 |
int count;
|
547 |
|
548 |
memset(&response, 0, sizeof(response)); |
549 |
response.type = TAPDISK_MESSAGE_LIST_RSP; |
550 |
response.cookie = request->cookie; |
551 |
|
552 |
head = tapdisk_server_get_all_vbds(); |
553 |
|
554 |
count = 0;
|
555 |
list_for_each_entry(vbd, head, next) |
556 |
count++; |
557 |
|
558 |
list_for_each_entry(vbd, head, next) { |
559 |
response.u.list.count = count--; |
560 |
response.u.list.minor = vbd->tap ? vbd->tap->minor : -1;
|
561 |
response.u.list.state = vbd->state; |
562 |
response.u.list.path[0] = 0; |
563 |
|
564 |
if (vbd->name)
|
565 |
strncpy(response.u.list.path, vbd->name, |
566 |
sizeof(response.u.list.path));
|
567 |
|
568 |
tapdisk_control_write_message(conn, &response); |
569 |
} |
570 |
|
571 |
response.u.list.count = count; |
572 |
response.u.list.minor = -1;
|
573 |
response.u.list.path[0] = 0; |
574 |
|
575 |
tapdisk_control_write_message(conn, &response); |
576 |
} |
577 |
|
578 |
static void |
579 |
tapdisk_control_get_pid(struct tapdisk_ctl_conn *conn,
|
580 |
tapdisk_message_t *request) |
581 |
{ |
582 |
tapdisk_message_t response; |
583 |
|
584 |
memset(&response, 0, sizeof(response)); |
585 |
response.type = TAPDISK_MESSAGE_PID_RSP; |
586 |
response.cookie = request->cookie; |
587 |
response.u.tapdisk_pid = getpid(); |
588 |
|
589 |
tapdisk_control_write_message(conn, &response); |
590 |
} |
591 |
|
592 |
static void |
593 |
tapdisk_control_attach_vbd(struct tapdisk_ctl_conn *conn,
|
594 |
tapdisk_message_t *request) |
595 |
{ |
596 |
tapdisk_message_t response; |
597 |
char *devname = NULL; |
598 |
td_vbd_t *vbd; |
599 |
int minor, err;
|
600 |
|
601 |
/*
|
602 |
* TODO: check for max vbds per process
|
603 |
*/
|
604 |
|
605 |
vbd = tapdisk_server_get_vbd(request->cookie); |
606 |
if (vbd) {
|
607 |
err = -EEXIST; |
608 |
goto out;
|
609 |
} |
610 |
|
611 |
minor = request->cookie; |
612 |
if (minor < 0) { |
613 |
err = -EINVAL; |
614 |
goto out;
|
615 |
} |
616 |
|
617 |
vbd = tapdisk_vbd_create(minor); |
618 |
if (!vbd) {
|
619 |
err = -ENOMEM; |
620 |
goto out;
|
621 |
} |
622 |
|
623 |
err = asprintf(&devname, BLKTAP2_RING_DEVICE"%d", minor);
|
624 |
if (err == -1) { |
625 |
devname = NULL;
|
626 |
err = -ENOMEM; |
627 |
goto fail_vbd;
|
628 |
} |
629 |
|
630 |
err = tapdisk_vbd_attach(vbd, devname, minor); |
631 |
if (err) {
|
632 |
ERR(err, "failure attaching to %s", devname);
|
633 |
goto fail_vbd;
|
634 |
} |
635 |
|
636 |
tapdisk_server_add_vbd(vbd); |
637 |
|
638 |
out:
|
639 |
if (devname)
|
640 |
free(devname); |
641 |
|
642 |
memset(&response, 0, sizeof(response)); |
643 |
response.type = TAPDISK_MESSAGE_ATTACH_RSP; |
644 |
response.cookie = request->cookie; |
645 |
response.u.response.error = -err; |
646 |
|
647 |
tapdisk_control_write_message(conn, &response); |
648 |
|
649 |
return;
|
650 |
|
651 |
fail_vbd:
|
652 |
tapdisk_vbd_detach(vbd); |
653 |
free(vbd); |
654 |
goto out;
|
655 |
} |
656 |
|
657 |
|
658 |
static void |
659 |
tapdisk_control_detach_vbd(struct tapdisk_ctl_conn *conn,
|
660 |
tapdisk_message_t *request) |
661 |
{ |
662 |
tapdisk_message_t response; |
663 |
td_vbd_t *vbd; |
664 |
int err;
|
665 |
|
666 |
vbd = tapdisk_server_get_vbd(request->cookie); |
667 |
if (!vbd) {
|
668 |
err = -EINVAL; |
669 |
goto out;
|
670 |
} |
671 |
|
672 |
if (vbd->name) {
|
673 |
err = -EBUSY; |
674 |
goto out;
|
675 |
} |
676 |
|
677 |
tapdisk_vbd_detach(vbd); |
678 |
|
679 |
if (list_empty(&vbd->images)) {
|
680 |
tapdisk_server_remove_vbd(vbd); |
681 |
free(vbd); |
682 |
} |
683 |
|
684 |
err = 0;
|
685 |
out:
|
686 |
memset(&response, 0, sizeof(response)); |
687 |
response.type = TAPDISK_MESSAGE_DETACH_RSP; |
688 |
response.cookie = request->cookie; |
689 |
response.u.response.error = -err; |
690 |
|
691 |
tapdisk_control_write_message(conn, &response); |
692 |
} |
693 |
|
694 |
static void |
695 |
tapdisk_control_open_image(struct tapdisk_ctl_conn *conn,
|
696 |
tapdisk_message_t *request) |
697 |
{ |
698 |
int err;
|
699 |
td_vbd_t *vbd; |
700 |
td_flag_t flags; |
701 |
tapdisk_message_t response; |
702 |
td_disk_info_t info; |
703 |
|
704 |
vbd = tapdisk_server_get_vbd(request->cookie); |
705 |
if (!vbd) {
|
706 |
err = -EINVAL; |
707 |
goto out;
|
708 |
} |
709 |
|
710 |
if (!vbd->tap) {
|
711 |
err = -EINVAL; |
712 |
goto out;
|
713 |
} |
714 |
|
715 |
if (vbd->name) {
|
716 |
err = -EALREADY; |
717 |
goto out;
|
718 |
} |
719 |
|
720 |
flags = 0;
|
721 |
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_RDONLY)
|
722 |
flags |= TD_OPEN_RDONLY; |
723 |
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SHARED)
|
724 |
flags |= TD_OPEN_SHAREABLE; |
725 |
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_CACHE)
|
726 |
flags |= TD_OPEN_ADD_CACHE; |
727 |
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_VHD_INDEX)
|
728 |
flags |= TD_OPEN_VHD_INDEX; |
729 |
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_LOG_DIRTY)
|
730 |
flags |= TD_OPEN_LOG_DIRTY; |
731 |
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_LCACHE)
|
732 |
flags |= TD_OPEN_LOCAL_CACHE; |
733 |
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_REUSE_PRT)
|
734 |
flags |= TD_OPEN_REUSE_PARENT; |
735 |
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_STANDBY)
|
736 |
flags |= TD_OPEN_STANDBY; |
737 |
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SECONDARY) {
|
738 |
char *name = strdup(request->u.params.secondary);
|
739 |
if (!name) {
|
740 |
err = -errno; |
741 |
goto out;
|
742 |
} |
743 |
vbd->secondary_name = name; |
744 |
flags |= TD_OPEN_SECONDARY; |
745 |
} |
746 |
|
747 |
err = tapdisk_vbd_open_vdi(vbd, request->u.params.path, flags, |
748 |
request->u.params.prt_devnum); |
749 |
if (err)
|
750 |
goto out;
|
751 |
|
752 |
err = tapdisk_vbd_get_disk_info(vbd, &info); |
753 |
if (err)
|
754 |
goto fail_close;
|
755 |
|
756 |
err = tapdisk_blktap_create_device(vbd->tap, &info, |
757 |
!!(flags & TD_OPEN_RDONLY)); |
758 |
if (err && err != -EEXIST) {
|
759 |
err = -errno; |
760 |
EPRINTF("create device failed: %d\n", err);
|
761 |
goto fail_close;
|
762 |
} |
763 |
|
764 |
err = 0;
|
765 |
|
766 |
out:
|
767 |
memset(&response, 0, sizeof(response)); |
768 |
response.cookie = request->cookie; |
769 |
|
770 |
if (err) {
|
771 |
response.type = TAPDISK_MESSAGE_ERROR; |
772 |
response.u.response.error = -err; |
773 |
} else {
|
774 |
response.u.image.sectors = info.size; |
775 |
response.u.image.sector_size = info.sector_size; |
776 |
response.u.image.info = info.info; |
777 |
response.type = TAPDISK_MESSAGE_OPEN_RSP; |
778 |
} |
779 |
|
780 |
tapdisk_control_write_message(conn, &response); |
781 |
|
782 |
return;
|
783 |
|
784 |
fail_close:
|
785 |
tapdisk_vbd_close_vdi(vbd); |
786 |
|
787 |
if (vbd->name) {
|
788 |
free(vbd->name); |
789 |
vbd->name = NULL;
|
790 |
} |
791 |
|
792 |
goto out;
|
793 |
} |
794 |
|
795 |
static void |
796 |
tapdisk_control_close_image(struct tapdisk_ctl_conn *conn,
|
797 |
tapdisk_message_t *request) |
798 |
{ |
799 |
tapdisk_message_t response; |
800 |
td_vbd_t *vbd; |
801 |
int err;
|
802 |
|
803 |
vbd = tapdisk_server_get_vbd(request->cookie); |
804 |
if (!vbd) {
|
805 |
err = -ENODEV; |
806 |
goto out;
|
807 |
} |
808 |
|
809 |
do {
|
810 |
err = tapdisk_blktap_remove_device(vbd->tap); |
811 |
|
812 |
if (!err || err != -EBUSY)
|
813 |
break;
|
814 |
|
815 |
tapdisk_server_iterate(); |
816 |
|
817 |
} while (conn->fd >= 0); |
818 |
|
819 |
if (err)
|
820 |
ERR(err, "failure closing image\n");
|
821 |
|
822 |
if (err == -ENOTTY) {
|
823 |
|
824 |
while (!list_empty(&vbd->pending_requests))
|
825 |
tapdisk_server_iterate(); |
826 |
|
827 |
err = 0;
|
828 |
} |
829 |
|
830 |
if (err)
|
831 |
goto out;
|
832 |
|
833 |
tapdisk_vbd_close_vdi(vbd); |
834 |
|
835 |
/* NB. vbd->name free should probably belong into close_vdi,
|
836 |
but the current blktap1 reopen-stuff likely depends on a
|
837 |
lifetime extended until shutdown. */
|
838 |
free(vbd->name); |
839 |
vbd->name = NULL;
|
840 |
|
841 |
if (!vbd->tap) {
|
842 |
tapdisk_server_remove_vbd(vbd); |
843 |
free(vbd); |
844 |
} |
845 |
|
846 |
out:
|
847 |
memset(&response, 0, sizeof(response)); |
848 |
response.type = TAPDISK_MESSAGE_CLOSE_RSP; |
849 |
response.cookie = request->cookie; |
850 |
response.u.response.error = -err; |
851 |
|
852 |
tapdisk_control_write_message(conn, &response); |
853 |
} |
854 |
|
855 |
static void |
856 |
tapdisk_control_pause_vbd(struct tapdisk_ctl_conn *conn,
|
857 |
tapdisk_message_t *request) |
858 |
{ |
859 |
int err;
|
860 |
td_vbd_t *vbd; |
861 |
tapdisk_message_t response; |
862 |
|
863 |
memset(&response, 0, sizeof(response)); |
864 |
|
865 |
response.type = TAPDISK_MESSAGE_PAUSE_RSP; |
866 |
|
867 |
vbd = tapdisk_server_get_vbd(request->cookie); |
868 |
if (!vbd) {
|
869 |
err = -EINVAL; |
870 |
goto out;
|
871 |
} |
872 |
|
873 |
do {
|
874 |
err = tapdisk_vbd_pause(vbd); |
875 |
|
876 |
if (!err || err != -EAGAIN)
|
877 |
break;
|
878 |
|
879 |
tapdisk_server_iterate(); |
880 |
|
881 |
} while (conn->fd >= 0); |
882 |
|
883 |
out:
|
884 |
response.cookie = request->cookie; |
885 |
response.u.response.error = -err; |
886 |
tapdisk_control_write_message(conn, &response); |
887 |
} |
888 |
|
889 |
static void |
890 |
tapdisk_control_resume_vbd(struct tapdisk_ctl_conn *conn,
|
891 |
tapdisk_message_t *request) |
892 |
{ |
893 |
int err;
|
894 |
td_vbd_t *vbd; |
895 |
tapdisk_message_t response; |
896 |
const char *desc = NULL; |
897 |
|
898 |
memset(&response, 0, sizeof(response)); |
899 |
|
900 |
response.type = TAPDISK_MESSAGE_RESUME_RSP; |
901 |
|
902 |
vbd = tapdisk_server_get_vbd(request->cookie); |
903 |
if (!vbd) {
|
904 |
err = -EINVAL; |
905 |
goto out;
|
906 |
} |
907 |
|
908 |
if (request->u.params.path[0]) |
909 |
desc = request->u.params.path; |
910 |
|
911 |
err = tapdisk_vbd_resume(vbd, desc); |
912 |
out:
|
913 |
response.cookie = request->cookie; |
914 |
response.u.response.error = -err; |
915 |
tapdisk_control_write_message(conn, &response); |
916 |
} |
917 |
|
918 |
static void |
919 |
tapdisk_control_stats(struct tapdisk_ctl_conn *conn,
|
920 |
tapdisk_message_t *request) |
921 |
{ |
922 |
tapdisk_message_t response; |
923 |
td_stats_t _st, *st = &_st; |
924 |
td_vbd_t *vbd; |
925 |
size_t rv; |
926 |
|
927 |
tapdisk_stats_init(st, |
928 |
conn->out.buf + sizeof(response),
|
929 |
conn->out.bufsz - sizeof(response));
|
930 |
|
931 |
if (request->cookie != (uint16_t)-1) { |
932 |
|
933 |
vbd = tapdisk_server_get_vbd(request->cookie); |
934 |
if (!vbd) {
|
935 |
rv = -ENODEV; |
936 |
goto out;
|
937 |
} |
938 |
|
939 |
tapdisk_vbd_stats(vbd, st); |
940 |
|
941 |
} else {
|
942 |
struct list_head *list = tapdisk_server_get_all_vbds();
|
943 |
|
944 |
tapdisk_stats_enter(st, '[');
|
945 |
|
946 |
list_for_each_entry(vbd, list, next) |
947 |
tapdisk_vbd_stats(vbd, st); |
948 |
|
949 |
tapdisk_stats_leave(st, ']');
|
950 |
} |
951 |
|
952 |
rv = tapdisk_stats_length(st); |
953 |
out:
|
954 |
memset(&response, 0, sizeof(response)); |
955 |
response.type = TAPDISK_MESSAGE_STATS_RSP; |
956 |
response.cookie = request->cookie; |
957 |
response.u.info.length = rv; |
958 |
|
959 |
tapdisk_control_write_message(conn, &response); |
960 |
if (rv > 0) |
961 |
conn->out.prod += rv; |
962 |
} |
963 |
|
964 |
struct tapdisk_control_info message_infos[] = {
|
965 |
[TAPDISK_MESSAGE_PID] = { |
966 |
.handler = tapdisk_control_get_pid, |
967 |
.flags = TAPDISK_MSG_REENTER, |
968 |
}, |
969 |
[TAPDISK_MESSAGE_LIST] = { |
970 |
.handler = tapdisk_control_list, |
971 |
.flags = TAPDISK_MSG_REENTER, |
972 |
}, |
973 |
[TAPDISK_MESSAGE_ATTACH] = { |
974 |
.handler = tapdisk_control_attach_vbd, |
975 |
.flags = TAPDISK_MSG_VERBOSE, |
976 |
}, |
977 |
[TAPDISK_MESSAGE_DETACH] = { |
978 |
.handler = tapdisk_control_detach_vbd, |
979 |
.flags = TAPDISK_MSG_VERBOSE, |
980 |
}, |
981 |
[TAPDISK_MESSAGE_OPEN] = { |
982 |
.handler = tapdisk_control_open_image, |
983 |
.flags = TAPDISK_MSG_VERBOSE, |
984 |
}, |
985 |
[TAPDISK_MESSAGE_PAUSE] = { |
986 |
.handler = tapdisk_control_pause_vbd, |
987 |
.flags = TAPDISK_MSG_VERBOSE, |
988 |
}, |
989 |
[TAPDISK_MESSAGE_RESUME] = { |
990 |
.handler = tapdisk_control_resume_vbd, |
991 |
.flags = TAPDISK_MSG_VERBOSE, |
992 |
}, |
993 |
[TAPDISK_MESSAGE_CLOSE] = { |
994 |
.handler = tapdisk_control_close_image, |
995 |
.flags = TAPDISK_MSG_VERBOSE, |
996 |
}, |
997 |
[TAPDISK_MESSAGE_STATS] = { |
998 |
.handler = tapdisk_control_stats, |
999 |
.flags = TAPDISK_MSG_REENTER, |
1000 |
}, |
1001 |
}; |
1002 |
|
1003 |
|
1004 |
static void |
1005 |
tapdisk_control_handle_request(event_id_t id, char mode, void *private) |
1006 |
{ |
1007 |
int err, excl;
|
1008 |
tapdisk_message_t message, response; |
1009 |
struct tapdisk_ctl_conn *conn = private;
|
1010 |
struct tapdisk_control_info *info;
|
1011 |
|
1012 |
err = tapdisk_control_read_message(conn->fd, &message, 2);
|
1013 |
if (err)
|
1014 |
goto close;
|
1015 |
|
1016 |
if (conn->in.busy)
|
1017 |
goto busy;
|
1018 |
|
1019 |
err = tapdisk_control_validate_request(&message); |
1020 |
if (err)
|
1021 |
goto invalid;
|
1022 |
|
1023 |
if (message.type > TAPDISK_MESSAGE_EXIT)
|
1024 |
goto invalid;
|
1025 |
|
1026 |
info = &message_infos[message.type]; |
1027 |
|
1028 |
if (!info->handler)
|
1029 |
goto invalid;
|
1030 |
|
1031 |
if (info->flags & TAPDISK_MSG_VERBOSE)
|
1032 |
DBG("received '%s' message (uuid = %u)\n",
|
1033 |
tapdisk_message_name(message.type), message.cookie); |
1034 |
|
1035 |
excl = !(info->flags & TAPDISK_MSG_REENTER); |
1036 |
if (excl) {
|
1037 |
if (td_control.busy)
|
1038 |
goto busy;
|
1039 |
|
1040 |
td_control.busy = 1;
|
1041 |
} |
1042 |
conn->in.busy = 1;
|
1043 |
conn->info = info; |
1044 |
|
1045 |
info->handler(conn, &message); |
1046 |
|
1047 |
conn->in.busy = 0;
|
1048 |
if (excl)
|
1049 |
td_control.busy = 0;
|
1050 |
|
1051 |
tapdisk_control_release_connection(conn); |
1052 |
return;
|
1053 |
|
1054 |
error:
|
1055 |
memset(&response, 0, sizeof(response)); |
1056 |
response.type = TAPDISK_MESSAGE_ERROR; |
1057 |
response.u.response.error = (err ? -err : EINVAL); |
1058 |
tapdisk_control_write_message(conn, &response); |
1059 |
|
1060 |
close:
|
1061 |
tapdisk_control_close_connection(conn); |
1062 |
return;
|
1063 |
|
1064 |
busy:
|
1065 |
err = -EBUSY; |
1066 |
ERR(err, "rejecting message '%s' while busy\n",
|
1067 |
tapdisk_message_name(message.type)); |
1068 |
goto error;
|
1069 |
|
1070 |
invalid:
|
1071 |
err = -EINVAL; |
1072 |
ERR(err, "rejecting unsupported message '%s'\n",
|
1073 |
tapdisk_message_name(message.type)); |
1074 |
goto error;
|
1075 |
} |
1076 |
|
1077 |
static void |
1078 |
tapdisk_control_accept(event_id_t id, char mode, void *private) |
1079 |
{ |
1080 |
int err, fd;
|
1081 |
struct tapdisk_ctl_conn *conn;
|
1082 |
|
1083 |
fd = accept(td_control.socket, NULL, NULL); |
1084 |
if (fd == -1) { |
1085 |
ERR(-errno, "failed to accept new control connection: %d\n", errno);
|
1086 |
return;
|
1087 |
} |
1088 |
|
1089 |
conn = tapdisk_ctl_conn_open(fd); |
1090 |
if (!conn) {
|
1091 |
close(fd); |
1092 |
ERR(-ENOMEM, "failed to allocate new control connection\n");
|
1093 |
return;
|
1094 |
} |
1095 |
|
1096 |
err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD, |
1097 |
conn->fd, TD_CTL_RECV_TIMEOUT, |
1098 |
tapdisk_control_handle_request, |
1099 |
conn); |
1100 |
if (err == -1) { |
1101 |
tapdisk_control_close_connection(conn); |
1102 |
ERR(err, "failed to register new control event\n");
|
1103 |
return;
|
1104 |
} |
1105 |
|
1106 |
conn->in.event_id = err; |
1107 |
} |
1108 |
|
1109 |
static int |
1110 |
tapdisk_control_mkdir(const char *dir) |
1111 |
{ |
1112 |
int err;
|
1113 |
char *ptr, *name, *start;
|
1114 |
|
1115 |
err = access(dir, W_OK | R_OK); |
1116 |
if (!err)
|
1117 |
return 0; |
1118 |
|
1119 |
name = strdup(dir); |
1120 |
if (!name)
|
1121 |
return -ENOMEM;
|
1122 |
|
1123 |
start = name; |
1124 |
|
1125 |
for (;;) {
|
1126 |
ptr = strchr(start + 1, '/'); |
1127 |
if (ptr)
|
1128 |
*ptr = '\0';
|
1129 |
|
1130 |
err = mkdir(name, 0755);
|
1131 |
if (err && errno != EEXIST) {
|
1132 |
err = -errno; |
1133 |
EPRINTF("failed to create directory %s: %d\n",
|
1134 |
name, err); |
1135 |
break;
|
1136 |
} |
1137 |
|
1138 |
if (!ptr)
|
1139 |
break;
|
1140 |
else {
|
1141 |
*ptr = '/';
|
1142 |
start = ptr + 1;
|
1143 |
} |
1144 |
} |
1145 |
|
1146 |
free(name); |
1147 |
return err;
|
1148 |
} |
1149 |
|
1150 |
static int |
1151 |
tapdisk_control_create_socket(char **socket_path)
|
1152 |
{ |
1153 |
struct sockaddr_un saddr;
|
1154 |
int err;
|
1155 |
|
1156 |
err = tapdisk_control_mkdir(BLKTAP2_CONTROL_DIR); |
1157 |
if (err) {
|
1158 |
EPRINTF("failed to create directory %s: %d\n",
|
1159 |
BLKTAP2_CONTROL_DIR, err); |
1160 |
return err;
|
1161 |
} |
1162 |
|
1163 |
err = asprintf(&td_control.path, "%s/%s%d",
|
1164 |
BLKTAP2_CONTROL_DIR, BLKTAP2_CONTROL_SOCKET, getpid()); |
1165 |
if (err == -1) { |
1166 |
td_control.path = NULL;
|
1167 |
err = (errno ? : ENOMEM); |
1168 |
goto fail;
|
1169 |
} |
1170 |
|
1171 |
if (unlink(td_control.path) && errno != ENOENT) {
|
1172 |
err = errno; |
1173 |
EPRINTF("failed to unlink %s: %d\n", td_control.path, errno);
|
1174 |
goto fail;
|
1175 |
} |
1176 |
|
1177 |
td_control.socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
1178 |
if (td_control.socket == -1) { |
1179 |
err = errno; |
1180 |
EPRINTF("failed to create control socket: %d\n", err);
|
1181 |
goto fail;
|
1182 |
} |
1183 |
|
1184 |
memset(&saddr, 0, sizeof(saddr)); |
1185 |
strncpy(saddr.sun_path, td_control.path, sizeof(saddr.sun_path));
|
1186 |
saddr.sun_family = AF_UNIX; |
1187 |
|
1188 |
err = bind(td_control.socket, |
1189 |
(const struct sockaddr *)&saddr, sizeof(saddr)); |
1190 |
if (err == -1) { |
1191 |
err = errno; |
1192 |
EPRINTF("failed to bind to %s: %d\n", saddr.sun_path, err);
|
1193 |
goto fail;
|
1194 |
} |
1195 |
|
1196 |
err = listen(td_control.socket, TD_CTL_SOCK_BACKLOG); |
1197 |
if (err == -1) { |
1198 |
err = errno; |
1199 |
EPRINTF("failed to listen: %d\n", err);
|
1200 |
goto fail;
|
1201 |
} |
1202 |
|
1203 |
err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD, |
1204 |
td_control.socket, 0,
|
1205 |
tapdisk_control_accept, NULL);
|
1206 |
if (err < 0) { |
1207 |
EPRINTF("failed to add watch: %d\n", err);
|
1208 |
goto fail;
|
1209 |
} |
1210 |
|
1211 |
td_control.event_id = err; |
1212 |
*socket_path = td_control.path; |
1213 |
|
1214 |
return 0; |
1215 |
|
1216 |
fail:
|
1217 |
tapdisk_control_close(); |
1218 |
return err;
|
1219 |
} |
1220 |
|
1221 |
int
|
1222 |
tapdisk_control_open(char **path)
|
1223 |
{ |
1224 |
tapdisk_control_initialize(); |
1225 |
|
1226 |
return tapdisk_control_create_socket(path);
|
1227 |
} |