debuggers.hg

view tools/blktap/lib/xenbus.c @ 20889:e1d61c5a008d

blktap: fix blktapctrl abort

On rebooting a hvm, the blktapctrl daemon has died.

gdb shows the following call trace:
(gdb) where
#0 0x00000039d1830155 in raise () from /lib64/libc.so.6
#1 0x00000039d1831bf0 in abort () from /lib64/libc.so.6
#2 0x00000039d186a38b in __libc_message () from /lib64/libc.so.6
#3 0x00000039d1871634 in _int_free () from /lib64/libc.so.6
#4 0x00000039d1874c5c in free () from /lib64/libc.so.6
#5 0x0000003320a01bdd in ueblktap_probe (h=3D0x6073b0,=20
w=<value optimized out>, bepath_im=<value optimized out>) at
xenbus.c:270
#6 0x0000003320a020e0 in xs_fire_next_watch (h=3D0x6073b0) at
xs_api.c:355
#7 0x0000000000401785 in main (argc=3D<value optimized out>,
argv=<value optimized out>) at blktapctrl.c:907

There is a case that "/local/domain/0/backend/tap/<dom_id>" exists but
"/local/domain/<dom_id>/vm" is not in the xenstore.

Signed-off-by: Kouya Shimura <kouya@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jan 22 11:00:45 2010 +0000 (2010-01-22)
parents c7381d04e2b3
children
line source
1 /*
2 * xenbus.c
3 *
4 * xenbus interface to the blocktap.
5 *
6 * this handles the top-half of integration with block devices through the
7 * store -- the tap driver negotiates the device channel etc, while the
8 * userland tap client needs to sort out the disk parameters etc.
9 *
10 * (c) 2005 Andrew Warfield and Julian Chesterfield
11 *
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License version 2
15 * as published by the Free Software Foundation; or, when distributed
16 * separately from the Linux kernel or incorporated into other
17 * software packages, subject to the following license:
18 *
19 * Permission is hereby granted, free of charge, to any person obtaining a copy
20 * of this source file (the "Software"), to deal in the Software without
21 * restriction, including without limitation the rights to use, copy, modify,
22 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
23 * and to permit persons to whom the Software is furnished to do so, subject to
24 * the following conditions:
25 *
26 * The above copyright notice and this permission notice shall be included in
27 * all copies or substantial portions of the Software.
28 *
29 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
34 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
35 * IN THE SOFTWARE.
36 */
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <err.h>
42 #include <stdarg.h>
43 #include <errno.h>
44 #include <xs.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #include <poll.h>
49 #include <time.h>
50 #include <sys/time.h>
51 #include <unistd.h>
52 #include "blktaplib.h"
53 #include "list.h"
54 #include "xs_api.h"
56 #if 0
57 #define DPRINTF(_f, _a...) printf ( _f , ## _a )
58 #else
59 #define DPRINTF(_f, _a...) ((void)0)
60 #endif
62 struct backend_info
63 {
64 /* our communications channel */
65 blkif_t *blkif;
67 long int frontend_id;
68 long int pdev;
69 long int readonly;
71 char *backpath;
72 char *frontpath;
74 struct list_head list;
75 };
77 static LIST_HEAD(belist);
79 static int strsep_len(const char *str, char c, unsigned int len)
80 {
81 unsigned int i;
83 for (i = 0; str[i]; i++)
84 if (str[i] == c) {
85 if (len == 0)
86 return i;
87 len--;
88 }
89 return (len == 0) ? i : -ERANGE;
90 }
92 static int get_be_id(const char *str)
93 {
94 int len,end;
95 const char *ptr;
96 char *tptr, num[10];
98 len = strsep_len(str, '/', 6);
99 end = strlen(str);
100 if( (len < 0) || (end < 0) ) return -1;
102 ptr = str + len + 1;
103 strncpy(num, ptr, end - len);
104 tptr = num + (end - (len + 1));
105 *tptr = '\0';
107 return atoi(num);
108 }
110 static int get_be_domid(const char *str)
111 {
112 int len1, len2;
113 const char *ptr;
114 char *tptr, num[10];
116 len2 = strsep_len(str, '/', 3);
117 if ( len2 < 0 ) return -1;
118 len1 = strsep_len(str, '/', 2);
120 ptr = str + len1 + 1;
121 strncpy(num, ptr, len2 - len1 - 1);
122 tptr = num + (len2 - len1 - 1);
123 *tptr = '\0';
125 return atoi(num);
126 }
128 static struct backend_info *be_lookup_be(const char *bepath)
129 {
130 struct backend_info *be;
132 list_for_each_entry(be, &belist, list)
133 if (strcmp(bepath, be->backpath) == 0)
134 return be;
135 return (struct backend_info *)NULL;
136 }
138 static int be_exists_be(const char *bepath)
139 {
140 return (be_lookup_be(bepath) != NULL);
141 }
143 static struct backend_info *be_lookup_fe(const char *fepath)
144 {
145 struct backend_info *be;
147 list_for_each_entry(be, &belist, list)
148 if (strcmp(fepath, be->frontpath) == 0)
149 return be;
150 return (struct backend_info *)NULL;
151 }
153 static int backend_remove(struct xs_handle *h, struct backend_info *be)
154 {
155 /* Unhook from be list. */
156 list_del(&be->list);
158 /* Free everything else. */
159 if (be->blkif) {
160 DPRINTF("Freeing blkif dev [%d]\n",be->blkif->devnum);
161 free_blkif(be->blkif);
162 }
163 if (be->frontpath)
164 free(be->frontpath);
165 if (be->backpath)
166 free(be->backpath);
167 free(be);
168 return 0;
169 }
171 static const char *get_image_path(const char *path)
172 {
173 const char *tmp;
175 /* Strip off the image type */
176 if (!strncmp(path, "tapdisk:", strlen("tapdisk:"))) {
177 path += strlen("tapdisk:");
178 } else if (!strncmp(path, "ioemu:", strlen("ioemu:"))) {
179 path += strlen("ioemu:");
180 }
182 tmp = strchr(path, ':');
183 if (tmp != NULL)
184 path = tmp + 1;
186 return path;
187 }
189 static int check_sharing(struct xs_handle *h, struct backend_info *be)
190 {
191 char *dom_uuid;
192 char *cur_dom_uuid;
193 char *path;
194 char *mode;
195 char *params;
196 char **domains;
197 char **devices;
198 int i, j;
199 unsigned int num_dom, num_dev;
200 blkif_info_t *info = be->blkif->info;
201 int ret = 0;
202 const char *image_path[2];
203 int be_domid = get_be_domid(be->backpath);
205 image_path[0] = get_image_path(info->params);
207 /* If the mode contains '!' or doesn't contain 'w' don't check anything */
208 xs_gather(h, be->backpath, "mode", NULL, &mode, NULL);
209 if (strchr(mode, '!'))
210 goto out;
211 if (strchr(mode, 'w') == NULL)
212 goto out;
214 /* Get the UUID of the domain we want to attach to */
215 if (asprintf(&path, "/local/domain/%ld", be->frontend_id) == -1)
216 goto fail;
217 xs_gather(h, path, "vm", NULL, &dom_uuid, NULL);
218 free(path);
220 /* Iterate through the devices of all VMs */
221 if (asprintf(&path, "/local/domain/%d/backend/tap", be_domid) == -1)
222 goto fail;
223 domains = xs_directory(h, XBT_NULL, path, &num_dom);
224 free(path);
225 if (domains == NULL)
226 num_dom = 0;
228 for (i = 0; !ret && (i < num_dom); i++) {
230 /* If it's the same VM, no action needed */
231 if (asprintf(&path, "/local/domain/%s", domains[i]) == -1) {
232 ret = -1;
233 break;
234 }
235 cur_dom_uuid = NULL;
236 xs_gather(h, path, "vm", NULL, &cur_dom_uuid, NULL);
237 free(path);
238 if (!cur_dom_uuid)
239 continue;
241 if (!strcmp(cur_dom_uuid, dom_uuid)) {
242 free(cur_dom_uuid);
243 continue;
244 }
246 /* Check the devices */
247 if (asprintf(&path, "/local/domain/%d/backend/tap/%s", be_domid, domains[i]) == -1) {
248 ret = -1;
249 free(cur_dom_uuid);
250 break;
251 }
252 devices = xs_directory(h, XBT_NULL, path, &num_dev);
253 if (devices == NULL)
254 num_dev = 0;
255 free(path);
257 for (j = 0; !ret && (j < num_dev); j++) {
258 if (asprintf(&path, "/local/domain/%d/backend/tap/%s/%s", be_domid, domains[i], devices[j]) == -1) {
259 ret = -1;
260 break;
261 }
262 params = NULL;
263 xs_gather(h, path, "params", NULL, &params, NULL);
264 free(path);
265 if (!params)
266 continue;
268 image_path[1] = get_image_path(params);
269 if (!strcmp(image_path[0], image_path[1])) {
270 ret = -1;
271 }
273 free(params);
274 }
276 free(cur_dom_uuid);
277 free(devices);
278 }
279 free(domains);
280 free(dom_uuid);
281 goto out;
283 fail:
284 ret = -1;
285 out:
286 free(mode);
287 return ret;
288 }
290 static int check_image(struct xs_handle *h, struct backend_info *be,
291 const char** errmsg)
292 {
293 const char *path;
294 int mode;
295 blkif_t *blkif = be->blkif;
296 blkif_info_t *info = blkif->info;
298 path = get_image_path(info->params);
300 /* Check if the image exists and access is permitted */
301 mode = R_OK;
302 if (!be->readonly)
303 mode |= W_OK;
304 if (access(path, mode)) {
305 if (errno == ENOENT)
306 *errmsg = "File not found.";
307 else
308 *errmsg = "Insufficient file permissions.";
309 return -1;
310 }
312 /* Check that the image is not attached to a different VM */
313 if (check_sharing(h, be)) {
314 *errmsg = "File already in use by other domain";
315 return -1;
316 }
318 return 0;
319 }
321 static void ueblktap_setup(struct xs_handle *h, char *bepath)
322 {
323 struct backend_info *be;
324 char *path = NULL, *p,*dev;
325 int len, er, deverr;
326 long int pdev = 0, handle;
327 blkif_info_t *blk;
328 const char* errmsg = NULL;
330 be = be_lookup_be(bepath);
331 if (be == NULL)
332 {
333 DPRINTF("ERROR: backend changed called for nonexistent "
334 "backend! (%s)\n", bepath);
335 goto fail;
336 }
338 deverr = xs_gather(h, bepath, "physical-device", "%li", &pdev, NULL);
339 if (!deverr) {
340 DPRINTF("pdev set to %ld\n",pdev);
341 if (be->pdev && be->pdev != pdev) {
342 DPRINTF("changing physical-device not supported");
343 goto fail;
344 }
345 be->pdev = pdev;
346 }
348 /* Check to see if device is to be opened read-only. */
349 deverr = xs_gather(h, bepath, "mode", NULL, &path, NULL);
350 if (deverr) {
351 DPRINTF("ERROR: could not find read/write mode\n");
352 goto fail;
353 } else if (path[0] == 'r')
354 be->readonly = 1;
356 if (be->blkif == NULL) {
357 /* Front end dir is a number, which is used as the handle. */
358 p = strrchr(be->frontpath, '/') + 1;
359 handle = strtoul(p, NULL, 0);
361 be->blkif = alloc_blkif(be->frontend_id);
362 if (be->blkif == NULL)
363 goto fail;
365 be->blkif->be_id = get_be_id(bepath);
367 /* Insert device specific info, */
368 blk = malloc(sizeof(blkif_info_t));
369 if (!blk) {
370 DPRINTF("Out of memory - blkif_info_t\n");
371 goto fail;
372 }
373 er = xs_gather(h, bepath, "params", NULL, &blk->params, NULL);
374 if (er)
375 goto fail;
376 be->blkif->info = blk;
378 if (deverr) {
379 /*Dev number was not available, try to set manually*/
380 pdev = convert_dev_name_to_num(blk->params);
381 be->pdev = pdev;
382 }
384 if (check_image(h, be, &errmsg))
385 goto fail;
387 er = blkif_init(be->blkif, handle, be->pdev, be->readonly);
388 if (er != 0) {
389 DPRINTF("Unable to open device %s\n",blk->params);
390 goto fail;
391 }
393 DPRINTF("[BECHG]: ADDED A NEW BLKIF (%s)\n", bepath);
394 }
396 /* Supply the information about the device to xenstore */
397 er = xs_printf(h, be->backpath, "sectors", "%llu",
398 be->blkif->ops->get_size(be->blkif));
400 if (er == 0) {
401 DPRINTF("ERROR: Failed writing sectors");
402 goto fail;
403 }
405 er = xs_printf(h, be->backpath, "sector-size", "%lu",
406 be->blkif->ops->get_secsize(be->blkif));
408 if (er == 0) {
409 DPRINTF("ERROR: Failed writing sector-size");
410 goto fail;
411 }
413 er = xs_printf(h, be->backpath, "info", "%u",
414 be->blkif->ops->get_info(be->blkif));
416 if (er == 0) {
417 DPRINTF("ERROR: Failed writing info");
418 goto fail;
419 }
421 be->blkif->state = CONNECTED;
422 xs_printf(h, be->backpath, "hotplug-status", "connected");
424 DPRINTF("[SETUP] Complete\n\n");
425 goto close;
427 fail:
428 if (be) {
429 if (errmsg == NULL)
430 errmsg = "Setting up the backend failed. See the log "
431 "files in /var/log/xen/ for details.";
432 xs_printf(h, be->backpath, "hotplug-error", errmsg);
433 xs_printf(h, be->backpath, "hotplug-status", "error");
435 backend_remove(h, be);
436 }
437 close:
438 if (path)
439 free(path);
440 return;
441 }
443 /**
444 * Xenstore watch callback entry point. This code replaces the hotplug scripts,
445 * and as soon as the xenstore backend driver entries are created, this script
446 * gets called.
447 */
448 static void ueblktap_probe(struct xs_handle *h, struct xenbus_watch *w,
449 const char *bepath_im)
450 {
451 struct backend_info *be = NULL;
452 char *frontend = NULL, *bepath = NULL, *p;
453 int er, len;
454 blkif_t *blkif;
457 bepath = strdup(bepath_im);
459 if (!bepath) {
460 DPRINTF("No path\n");
461 return;
462 }
464 /*
465 *asserts that xenstore structure is always 7 levels deep
466 *e.g. /local/domain/0/backend/vbd/1/2049
467 */
468 len = strsep_len(bepath, '/', 7);
469 if (len < 0)
470 goto free_be;
471 if (bepath[len] != '\0')
472 goto free_be;
474 be = malloc(sizeof(*be));
475 if (!be) {
476 DPRINTF("ERROR: allocating backend structure\n");
477 goto free_be;
478 }
479 memset(be, 0, sizeof(*be));
480 frontend = NULL;
482 er = xs_gather(h, bepath,
483 "frontend-id", "%li", &be->frontend_id,
484 "frontend", NULL, &frontend,
485 NULL);
487 if (er) {
488 /*
489 *Unable to find frontend entries,
490 *bus-id is no longer valid
491 */
492 DPRINTF("ERROR: Frontend-id check failed, removing backend: "
493 "[%s]\n",bepath);
495 /**
496 * BE info should already exist,
497 * free new mem and find old entry
498 */
499 free(be);
500 be = be_lookup_be(bepath);
501 if ( (be != NULL) && (be->blkif != NULL) )
502 backend_remove(h, be);
503 else goto free_be;
504 if (bepath)
505 free(bepath);
506 return;
507 }
509 /* Are we already tracking this device? */
510 if (be_exists_be(bepath))
511 goto free_be;
513 be->backpath = bepath;
514 be->frontpath = frontend;
516 list_add(&be->list, &belist);
518 DPRINTF("[PROBE]\tADDED NEW DEVICE (%s)\n", bepath);
519 DPRINTF("\tFRONTEND (%s),(%ld)\n", frontend,be->frontend_id);
521 ueblktap_setup(h, bepath);
522 return;
524 free_be:
525 if (frontend)
526 free(frontend);
527 if (bepath)
528 free(bepath);
529 if (be)
530 free(be);
531 }
533 /**
534 *We set a general watch on the backend vbd directory
535 *ueblktap_probe is called for every update
536 *Our job is to monitor for new entries. As they
537 *are created, we initalise the state and attach a disk.
538 */
540 static int add_blockdevice_probe_watch(struct xs_handle *h, const char *domid)
541 {
542 char *path;
543 struct xenbus_watch *vbd_watch;
545 if (asprintf(&path, "/local/domain/%s/backend/tap", domid) == -1)
546 return -ENOMEM;
548 vbd_watch = (struct xenbus_watch *)malloc(sizeof(struct xenbus_watch));
549 if (!vbd_watch) {
550 DPRINTF("ERROR: unable to malloc vbd_watch [%s]\n", path);
551 return -EINVAL;
552 }
553 vbd_watch->node = path;
554 vbd_watch->callback = ueblktap_probe;
555 if (register_xenbus_watch(h, vbd_watch) != 0) {
556 DPRINTF("ERROR: adding vbd probe watch %s\n", path);
557 return -EINVAL;
558 }
559 return 0;
560 }
562 /* Asynch callback to check for /local/domain/<DOMID>/name */
563 static void check_dom(struct xs_handle *h, struct xenbus_watch *w,
564 const char *bepath_im)
565 {
566 char *domid;
568 domid = get_dom_domid(h);
569 if (domid == NULL)
570 return;
572 add_blockdevice_probe_watch(h, domid);
573 free(domid);
574 unregister_xenbus_watch(h, w);
575 }
577 /* We must wait for xend to register /local/domain/<DOMID> */
578 static int watch_for_domid(struct xs_handle *h)
579 {
580 struct xenbus_watch *domid_watch;
581 char *path = NULL;
583 if (asprintf(&path, "/local/domain") == -1)
584 return -ENOMEM;
586 domid_watch = malloc(sizeof(struct xenbus_watch));
587 if (domid_watch == NULL) {
588 DPRINTF("ERROR: unable to malloc domid_watch [%s]\n", path);
589 return -EINVAL;
590 }
592 domid_watch->node = path;
593 domid_watch->callback = check_dom;
595 if (register_xenbus_watch(h, domid_watch) != 0) {
596 DPRINTF("ERROR: adding vbd probe watch %s\n", path);
597 return -EINVAL;
598 }
600 DPRINTF("Set async watch for /local/domain\n");
602 return 0;
603 }
605 int setup_probe_watch(struct xs_handle *h)
606 {
607 char *domid;
608 int ret;
610 domid = get_dom_domid(h);
611 if (domid == NULL)
612 return watch_for_domid(h);
614 ret = add_blockdevice_probe_watch(h, domid);
615 free(domid);
616 return ret;
617 }