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 <unistd.h>
38 : : #include <stdlib.h>
39 : : #include <limits.h>
40 : : #include <time.h>
41 : : #include <sys/ioctl.h>
42 : : #include <sys/signal.h>
43 : : #ifdef HAVE_EVENTFD
44 : : #include <sys/eventfd.h>
45 : : #else
46 : : #include <sys/syscall.h>
47 : : #endif
48 : : #include <fcntl.h>
49 : : #include <stdint.h>
50 : : #include <string.h>
51 : :
52 : : #include "tapdisk-syslog.h"
53 : : #include "tapdisk-server.h"
54 : : #include "tapdisk-driver.h"
55 : : #include "posixaio-backend.h"
56 : : #include "libaio-backend.h"
57 : : #include "tapdisk-interface.h"
58 : : #include "tapdisk-log.h"
59 : : #include "td-blkif.h"
60 : : #include "timeout-math.h"
61 : :
62 : : #include <sys/mman.h>
63 : : #include <sys/stat.h>
64 : : #include "../cpumond/cpumond.h"
65 : :
66 : : #define DBG(_level, _f, _a...) tlog_write(_level, _f, ##_a)
67 : : #define ERR(_err, _f, _a...) tlog_error(_err, _f, ##_a)
68 : :
69 : : #define TAPDISK_TIOCBS (TAPDISK_DATA_REQUESTS + 50)
70 : :
71 : : typedef struct tapdisk_server {
72 : : int run;
73 : : struct list_head vbds;
74 : : scheduler_t scheduler;
75 : : tqueue rw_queue;
76 : : tqueue ro_queue;
77 : : struct backend *ro_backend;
78 : : struct backend *rw_backend;
79 : : char *name;
80 : : char *ident;
81 : : int facility;
82 : :
83 : : /* CPU Utilisation Monitor client state */
84 : : struct {
85 : : int fd; /* shm fd */
86 : : cpumond_t *cpumon; /* mmap pointer */
87 : : } cpumond_state;
88 : :
89 : : event_id_t tlog_reopen_evid;
90 : : } tapdisk_server_t;
91 : :
92 : : static tapdisk_server_t server;
93 : :
94 : : unsigned int PAGE_SIZE;
95 : : unsigned int PAGE_MASK;
96 : : unsigned int PAGE_SHIFT;
97 : :
98 : : #define tapdisk_server_for_each_vbd(vbd, tmp) \
99 : : list_for_each_entry_safe(vbd, tmp, &server.vbds, next)
100 : :
101 : : td_image_t *
102 : 0 : tapdisk_server_get_shared_image(td_image_t *image)
103 : : {
104 : 0 : td_vbd_t *vbd, *tmpv;
105 : 0 : td_image_t *img, *tmpi;
106 : :
107 [ # # ]: 0 : if (!td_flag_test(image->flags, TD_OPEN_SHAREABLE))
108 : : return NULL;
109 : :
110 [ # # ]: 0 : tapdisk_server_for_each_vbd(vbd, tmpv)
111 [ # # # # ]: 0 : tapdisk_vbd_for_each_image(vbd, img, tmpi)
112 [ # # ]: 0 : if (img->type == image->type &&
113 [ # # ]: 0 : !strcmp(img->name, image->name))
114 : : return img;
115 : :
116 : : return NULL;
117 : : }
118 : :
119 : : struct list_head *
120 : 0 : tapdisk_server_get_all_vbds(void)
121 : : {
122 : 0 : return &server.vbds;
123 : : }
124 : :
125 : : td_vbd_t *
126 : 0 : tapdisk_server_get_vbd(uint16_t uuid)
127 : : {
128 : 0 : td_vbd_t *vbd, *tmp;
129 : :
130 [ # # # # ]: 0 : tapdisk_server_for_each_vbd(vbd, tmp)
131 [ # # ]: 0 : if (vbd->uuid == uuid)
132 : : return vbd;
133 : :
134 : : return NULL;
135 : : }
136 : :
137 : : void
138 : 0 : tapdisk_server_add_vbd(td_vbd_t *vbd)
139 : : {
140 : 0 : list_add_tail(&vbd->next, &server.vbds);
141 : 0 : }
142 : :
143 : : void
144 : 0 : tapdisk_server_remove_vbd(td_vbd_t *vbd)
145 : : {
146 [ # # ]: 0 : list_del(&vbd->next);
147 [ # # ]: 0 : INIT_LIST_HEAD(&vbd->next);
148 : 0 : tapdisk_server_check_state();
149 : 0 : }
150 : :
151 : : void
152 : 0 : tapdisk_server_prep_tiocb(struct tiocb *tiocb, int fd, int rw, char *buf, size_t size,
153 : : long long offset, td_queue_callback_t cb, void *arg)
154 : : {
155 : 0 : server.rw_backend->prep(tiocb, fd, rw, buf, size, offset, cb, arg);
156 : 0 : }
157 : :
158 : : void
159 : 0 : tapdisk_server_queue_tiocb(struct tiocb *tiocb)
160 : : {
161 : 0 : server.rw_backend->queue(server.rw_queue, tiocb);
162 : 0 : }
163 : :
164 : : void
165 : 0 : tapdisk_server_prep_tiocb_ro(struct tiocb *tiocb, int fd, int rw, char *buf, size_t size,
166 : : long long offset, td_queue_callback_t cb, void *arg)
167 : : {
168 : 0 : server.ro_backend->prep(tiocb, fd, rw, buf, size, offset, cb, arg);
169 : 0 : }
170 : :
171 : : void
172 : 0 : tapdisk_server_queue_tiocb_ro(struct tiocb *tiocb)
173 : : {
174 : 0 : server.ro_backend->queue(server.ro_queue, tiocb);
175 : 0 : }
176 : :
177 : : void
178 : 0 : tapdisk_server_debug(void)
179 : : {
180 : 0 : td_vbd_t *vbd, *tmp;
181 : :
182 [ # # ]: 0 : if (likely(server.rw_queue))
183 : 0 : server.rw_backend->debug(server.rw_queue);
184 [ # # ]: 0 : if (likely(server.ro_queue))
185 : 0 : server.ro_backend->debug(server.ro_queue);
186 : :
187 [ # # ]: 0 : tapdisk_server_for_each_vbd(vbd, tmp)
188 : 0 : tapdisk_vbd_debug(vbd);
189 : :
190 : 0 : DBG(TLOG_INFO, "debug log completed\n");
191 : 0 : tlog_precious(1);
192 : 0 : }
193 : :
194 : : void
195 : 0 : tapdisk_server_check_state(void)
196 : : {
197 [ # # # # ]: 0 : if (list_empty(&server.vbds))
198 : 0 : server.run = 0;
199 : 0 : }
200 : :
201 : : event_id_t
202 : 0 : tapdisk_server_register_event(char mode, int fd,
203 : : struct timeval timeout, event_cb_t cb, void *data)
204 : : {
205 : 0 : return scheduler_register_event(&server.scheduler,
206 : : mode, fd, timeout, cb, data);
207 : : }
208 : :
209 : : void
210 : 0 : tapdisk_server_unregister_event(event_id_t event)
211 : : {
212 : 0 : return scheduler_unregister_event(&server.scheduler, event);
213 : : }
214 : :
215 : : void
216 : 0 : tapdisk_server_mask_event(event_id_t event, int masked)
217 : : {
218 : 0 : return scheduler_mask_event(&server.scheduler, event, masked);
219 : : }
220 : :
221 : : void
222 : 0 : tapdisk_server_set_max_timeout(int seconds)
223 : : {
224 : 0 : scheduler_set_max_timeout(&server.scheduler, TV_SECS(seconds));
225 : 0 : }
226 : :
227 : : static void
228 : : tapdisk_server_assert_locks(void)
229 : : {
230 : :
231 : : }
232 : :
233 : : static void
234 : 0 : tapdisk_server_set_retry_timeout(void)
235 : : {
236 : 0 : td_vbd_t *vbd, *tmp;
237 : :
238 [ # # ]: 0 : tapdisk_server_for_each_vbd(vbd, tmp)
239 [ # # ]: 0 : if (tapdisk_vbd_retry_needed(vbd)) {
240 : 0 : tapdisk_server_set_max_timeout(TD_VBD_RETRY_INTERVAL);
241 : 0 : return;
242 : : }
243 : : }
244 : :
245 : : static void
246 : 0 : tapdisk_server_check_progress(void)
247 : : {
248 : 0 : struct timeval now;
249 : 0 : td_vbd_t *vbd, *tmp;
250 : :
251 : 0 : gettimeofday(&now, NULL);
252 : :
253 [ # # ]: 0 : tapdisk_server_for_each_vbd(vbd, tmp)
254 : 0 : tapdisk_vbd_check_progress(vbd);
255 : 0 : }
256 : :
257 : : static void
258 : 0 : tapdisk_server_submit_tiocbs(void)
259 : : {
260 : 0 : server.rw_backend->submit_all(server.rw_queue);
261 : 0 : server.ro_backend->submit_all(server.ro_queue);
262 : 0 : }
263 : :
264 : : static void
265 : 0 : tapdisk_server_kick_responses(void)
266 : : {
267 : 0 : td_vbd_t *vbd, *tmp;
268 : :
269 [ # # ]: 0 : tapdisk_server_for_each_vbd(vbd, tmp)
270 : 0 : tapdisk_vbd_kick(vbd);
271 : 0 : }
272 : :
273 : : static void
274 : 0 : tapdisk_server_check_vbds(void)
275 : : {
276 : 0 : td_vbd_t *vbd, *tmp;
277 : :
278 [ # # ]: 0 : tapdisk_server_for_each_vbd(vbd, tmp)
279 : 0 : tapdisk_vbd_check_state(vbd);
280 : 0 : }
281 : :
282 : : /**
283 : : * Issues new requests. Returns the number of VBDs that contained new requests
284 : : * which have been issued.
285 : : */
286 : : static int
287 : 0 : tapdisk_server_recheck_vbds(void)
288 : : {
289 : 0 : td_vbd_t *vbd, *tmp;
290 : 0 : int rv = 0;
291 : :
292 [ # # ]: 0 : tapdisk_server_for_each_vbd(vbd, tmp)
293 : 0 : rv += tapdisk_vbd_recheck_state(vbd);
294 : :
295 : 0 : return rv;
296 : : }
297 : :
298 : : static void
299 : 0 : tapdisk_server_stop_vbds(void)
300 : : {
301 : 0 : td_vbd_t *vbd, *tmp;
302 : :
303 [ # # ]: 0 : tapdisk_server_for_each_vbd(vbd, tmp)
304 : 0 : tapdisk_vbd_kill_queue(vbd);
305 : 0 : }
306 : :
307 : : static int
308 : 0 : tapdisk_server_init_aio(void)
309 : : {
310 : 0 : int err;
311 : 0 : err = server.ro_backend->init(&server.ro_queue, TAPDISK_TIOCBS,
312 : : TIO_DRV_LIO, NULL);
313 [ # # ]: 0 : if(err)
314 : : return err;
315 : :
316 : 0 : return server.rw_backend->init(&server.rw_queue, TAPDISK_TIOCBS,
317 : : TIO_DRV_LIO, NULL);
318 : : }
319 : :
320 : : static void
321 : 0 : tapdisk_server_close_aio(void)
322 : : {
323 : 0 : server.rw_backend->free_queue(&server.rw_queue);
324 : 0 : server.ro_backend->free_queue(&server.ro_queue);
325 : 0 : }
326 : :
327 : : int
328 : 0 : tapdisk_server_openlog(const char *name, int options, int facility)
329 : : {
330 : 0 : server.facility = facility;
331 : 0 : server.name = strdup(name);
332 : 0 : server.ident = tapdisk_syslog_ident(name);
333 : :
334 [ # # # # ]: 0 : if (!server.name || !server.ident)
335 : 0 : return -errno;
336 : :
337 : 0 : openlog(server.ident, options, facility);
338 : :
339 : 0 : return 0;
340 : : }
341 : :
342 : : void
343 : 0 : tapdisk_server_closelog(void)
344 : : {
345 : 0 : closelog();
346 : :
347 : 0 : free(server.name);
348 : 0 : server.name = NULL;
349 : :
350 : 0 : free(server.ident);
351 : 0 : server.ident = NULL;
352 : 0 : }
353 : :
354 : : static int
355 : 0 : tapdisk_server_open_tlog(void)
356 : : {
357 : 0 : int err = 0;
358 : :
359 [ # # ]: 0 : if (server.name)
360 : 0 : err = tlog_open(server.name, server.facility, TLOG_WARN);
361 : :
362 : 0 : return err;
363 : : }
364 : :
365 : : static void
366 : 0 : tapdisk_server_close_tlog(void)
367 : : {
368 : 0 : tlog_close();
369 : : }
370 : :
371 : : static void
372 : 0 : tapdisk_server_close(void)
373 : : {
374 [ # # ]: 0 : if (likely(server.tlog_reopen_evid >= 0))
375 : 0 : tapdisk_server_unregister_event(server.tlog_reopen_evid);
376 : :
377 : 0 : tapdisk_server_close_tlog();
378 : 0 : tapdisk_server_close_aio();
379 : 0 : }
380 : :
381 : : void
382 : 0 : tapdisk_server_iterate(void)
383 : : {
384 : 0 : int ret;
385 : :
386 : 0 : tapdisk_server_assert_locks();
387 : 0 : tapdisk_server_set_retry_timeout();
388 : 0 : tapdisk_server_check_progress();
389 : :
390 : 0 : ret = scheduler_wait_for_events(&server.scheduler);
391 [ # # ]: 0 : if (ret < 0)
392 : 0 : DBG(TLOG_WARN, "server wait returned %s\n", strerror(-ret));
393 : :
394 : 0 : tapdisk_server_check_vbds();
395 : 0 : do {
396 : 0 : tapdisk_server_submit_tiocbs();
397 : 0 : tapdisk_server_kick_responses();
398 : :
399 : 0 : ret = tapdisk_server_recheck_vbds();
400 [ # # ]: 0 : } while (ret); /* repeat until there are no new requests to issue */
401 : 0 : }
402 : :
403 : : static void
404 : 0 : __tapdisk_server_run(void)
405 : : {
406 [ # # ]: 0 : while (server.run)
407 : 0 : tapdisk_server_iterate();
408 : : }
409 : :
410 : : static void
411 : 0 : tapdisk_server_signal_handler(int signal)
412 : : {
413 : 0 : td_vbd_t *vbd, *tmp;
414 : 0 : struct td_xenblkif *blkif;
415 : 0 : static int xfsz_error_sent = 0;
416 : :
417 [ # # # # : 0 : switch (signal) {
# # ]
418 : : case SIGBUS:
419 : : case SIGINT:
420 [ # # ]: 0 : tapdisk_server_for_each_vbd(vbd, tmp)
421 : 0 : tapdisk_vbd_close(vbd);
422 : 0 : break;
423 : :
424 : 0 : case SIGXFSZ:
425 : 0 : ERR(EFBIG, "received SIGXFSZ");
426 : 0 : tapdisk_server_stop_vbds();
427 [ # # ]: 0 : if (xfsz_error_sent)
428 : : break;
429 : :
430 : 0 : xfsz_error_sent = 1;
431 : 0 : break;
432 : :
433 : 0 : case SIGUSR1:
434 : 0 : DBG(TLOG_INFO, "debugging on signal %d\n", signal);
435 : 0 : tapdisk_server_debug();
436 : : break;
437 : :
438 : 0 : case SIGUSR2:
439 : 0 : DBG(TLOG_INFO, "triggering polling on signal %d\n", signal);
440 [ # # ]: 0 : tapdisk_server_for_each_vbd(vbd, tmp)
441 [ # # ]: 0 : list_for_each_entry(blkif, &vbd->rings, entry)
442 : 0 : tapdisk_start_polling(blkif);
443 : : break;
444 : :
445 : 0 : case SIGHUP:
446 : 0 : tapdisk_server_event_set_timeout(server.tlog_reopen_evid, TV_ZERO);
447 : : break;
448 : : }
449 : 0 : }
450 : :
451 : :
452 : : static void
453 : 0 : tlog_reopen_cb(event_id_t id, char mode __attribute__((unused)), void *private)
454 : : {
455 : 0 : tlog_reopen();
456 : 0 : tapdisk_server_event_set_timeout(id, TV_INF);
457 : 0 : }
458 : :
459 : 0 : static void cpumond_state_init(void)
460 : : {
461 : 0 : server.cpumond_state.fd = -1;
462 : 0 : server.cpumond_state.cpumon = (cpumond_t *) 0;
463 : : }
464 : :
465 : 0 : static void cpumond_cleanup(void)
466 : : {
467 [ # # ]: 0 : if (server.cpumond_state.cpumon)
468 : 0 : munmap(server.cpumond_state.cpumon, sizeof(cpumond_t));
469 [ # # ]: 0 : if (server.cpumond_state.fd >= 0)
470 : 0 : close(server.cpumond_state.fd);
471 : :
472 : 0 : cpumond_state_init();
473 : 0 : }
474 : :
475 : : float
476 : 0 : tapdisk_server_system_idle_cpu(void)
477 : : {
478 [ # # ]: 0 : if (server.cpumond_state.cpumon > 0)
479 : 0 : return server.cpumond_state.cpumon->idle;
480 : : else
481 : : return 0.0;
482 : : }
483 : :
484 : : /* Create the CPU Utilisation Monitor client. */
485 : : static int
486 : 0 : tapdisk_server_initialize_cpumond_client(void)
487 : : {
488 : 0 : server.cpumond_state.fd = shm_open(CPUMOND_PATH, O_RDONLY, 0);
489 [ # # ]: 0 : if (server.cpumond_state.fd == -1)
490 : 0 : return -errno;
491 : :
492 : 0 : server.cpumond_state.cpumon = mmap(NULL, sizeof(cpumond_t), PROT_READ, MAP_PRIVATE, server.cpumond_state.fd, 0);
493 [ # # ]: 0 : if (server.cpumond_state.cpumon == (cpumond_t *) -1) {
494 : 0 : server.cpumond_state.cpumon = 0;
495 : 0 : return -errno;
496 : : }
497 : :
498 : : return 0;
499 : : }
500 : :
501 : : int
502 : 0 : tapdisk_server_init(void)
503 : : {
504 : 0 : int ret;
505 : 0 : unsigned int i = 0;
506 : :
507 : 0 : PAGE_SIZE = sysconf(_SC_PAGESIZE);
508 : 0 : PAGE_MASK = ~(PAGE_SIZE - 1);
509 : :
510 [ # # ]: 0 : for (i = PAGE_SIZE, PAGE_SHIFT = 0; i > 1; i >>= 1, PAGE_SHIFT++);
511 : :
512 : 0 : memset(&server, 0, sizeof(server));
513 : 0 : INIT_LIST_HEAD(&server.vbds);
514 : :
515 : 0 : scheduler_initialize(&server.scheduler);
516 : :
517 [ # # ]: 0 : if ((ret = tapdisk_server_initialize_cpumond_client()) < 0) {
518 : 0 : EPRINTF("Failed to connect to cpumond: %s\n",
519 : : strerror(-ret));
520 : 0 : cpumond_cleanup();
521 : : }
522 : :
523 : 0 : server.tlog_reopen_evid = -1;
524 : :
525 : 0 : return 0;
526 : : }
527 : :
528 : : int
529 : 0 : tapdisk_server_complete(void)
530 : : {
531 : 0 : int err;
532 : 0 : server.rw_backend = get_libaio_backend();
533 : 0 : server.ro_backend = get_libaio_backend();
534 : :
535 : 0 : server.rw_backend = get_libaio_backend();
536 : 0 : err = tapdisk_server_init_aio();
537 [ # # ]: 0 : if (err)
538 : 0 : goto fail;
539 : :
540 : 0 : err = tapdisk_server_open_tlog();
541 [ # # ]: 0 : if (err)
542 : 0 : goto fail;
543 : :
544 : 0 : server.run = 1;
545 : :
546 : 0 : return 0;
547 : :
548 : 0 : fail:
549 : 0 : tapdisk_server_close_tlog();
550 : 0 : tapdisk_server_close_aio();
551 : 0 : return err;
552 : : }
553 : :
554 : : int
555 : 0 : tapdisk_server_initialize(const char *read, const char *write)
556 : : {
557 : 0 : int err;
558 : :
559 : 0 : tapdisk_server_init();
560 : :
561 : 0 : err = tapdisk_server_complete();
562 [ # # ]: 0 : if (err)
563 : 0 : goto fail;
564 : :
565 : : return 0;
566 : :
567 : 0 : fail:
568 : 0 : tapdisk_server_close();
569 : 0 : return err;
570 : : }
571 : :
572 : : int
573 : 0 : tapdisk_server_run()
574 : : {
575 : 0 : int err;
576 : :
577 : 0 : err = tapdisk_set_resource_limits();
578 [ # # ]: 0 : if (err)
579 : 0 : return err;
580 : :
581 : 0 : signal(SIGBUS, tapdisk_server_signal_handler);
582 : 0 : signal(SIGINT, tapdisk_server_signal_handler);
583 : 0 : signal(SIGUSR1, tapdisk_server_signal_handler);
584 : 0 : signal(SIGUSR2, tapdisk_server_signal_handler);
585 : 0 : signal(SIGHUP, tapdisk_server_signal_handler);
586 : 0 : signal(SIGXFSZ, tapdisk_server_signal_handler);
587 : :
588 : 0 : err = tapdisk_server_register_event(SCHEDULER_POLL_TIMEOUT, -1, TV_INF,
589 : : tlog_reopen_cb, NULL);
590 [ # # ]: 0 : if (unlikely(err < 0)) {
591 : 0 : EPRINTF("failed to register reopen log event: %s\n", strerror(-err));
592 : 0 : goto out;
593 : : }
594 : :
595 : 0 : server.tlog_reopen_evid = err;
596 : :
597 : 0 : err = 0;
598 : :
599 : 0 : __tapdisk_server_run();
600 : :
601 : 0 : out:
602 : 0 : tapdisk_server_close();
603 : :
604 : : return err;
605 : : }
606 : :
607 : : int
608 : 0 : tapdisk_server_event_set_timeout(event_id_t event_id, struct timeval timeo) {
609 : 0 : return scheduler_event_set_timeout(&server.scheduler, event_id, timeo);
610 : : }
611 : :
|