ia64/xen-unstable

view tools/blktap/xenbus.c @ 6946:e703abaf6e3d

Add behaviour to the remove methods to remove the transaction's path itself. This allows us to write Remove(path) to remove the specified path rather than having to slice the path ourselves.
author emellor@ewan
date Sun Sep 18 14:42:13 2005 +0100 (2005-09-18)
parents 3233e7ecfa9f
children f7a7f8f2e6e4 872cf6ee0594
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 clinet needs to sort out the disk parameters etc.
9 *
10 * A. Warfield 2005 Based primarily on the blkback and xenbus driver code.
11 * Comments there apply here...
12 */
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <err.h>
18 #include <stdarg.h>
19 #include <errno.h>
20 #include <xs.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <poll.h>
25 #include "blktaplib.h"
26 #include "list.h"
28 #if 0
29 #define DPRINTF(_f, _a...) printf ( _f , ## _a )
30 #else
31 #define DPRINTF(_f, _a...) ((void)0)
32 #endif
34 /* --- Xenstore / Xenbus helpers ---------------------------------------- */
35 /*
36 * These should all be pulled out into the xenstore API. I'm faulting commands
37 * in from the xenbus interface as i need them.
38 */
41 /* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
42 int xs_gather(struct xs_handle *xs, const char *dir, ...)
43 {
44 va_list ap;
45 const char *name;
46 char *path;
47 int ret = 0;
49 va_start(ap, dir);
50 while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
51 const char *fmt = va_arg(ap, char *);
52 void *result = va_arg(ap, void *);
53 char *p;
55 if (asprintf(&path, "%s/%s", dir, name) == -1)
56 {
57 warn("allocation error in xs_gather!\n");
58 ret = ENOMEM;
59 break;
60 }
61 p = xs_read(xs, path, NULL);
62 free(path);
63 if (p == NULL) {
64 ret = ENOENT;
65 break;
66 }
67 if (fmt) {
68 if (sscanf(p, fmt, result) == 0)
69 ret = EINVAL;
70 free(p);
71 } else
72 *(char **)result = p;
73 }
74 va_end(ap);
75 return ret;
76 }
78 /* Single printf and write: returns -errno or 0. */
79 int xs_printf(struct xs_handle *h, const char *dir, const char *node,
80 const char *fmt, ...)
81 {
82 char *buf, *path;
83 va_list ap;
84 int ret;
86 va_start(ap, fmt);
87 ret = vasprintf(&buf, fmt, ap);
88 va_end(ap);
90 asprintf(&path, "%s/%s", dir, node);
92 if ((path == NULL) || (buf == NULL))
93 return 0;
95 ret = xs_write(h, path, buf, strlen(buf)+1, O_CREAT);
97 free(buf);
98 free(path);
100 return ret;
101 }
104 int xs_exists(struct xs_handle *h, const char *path)
105 {
106 char **d;
107 int num;
109 d = xs_directory(h, path, &num);
110 if (d == NULL)
111 return 0;
112 free(d);
113 return 1;
114 }
118 /* This assumes that the domain name we are looking for is unique! */
119 char *get_dom_uuid(struct xs_handle *h, const char *name)
120 {
121 char **e, *val, *uuid = NULL;
122 int num, i, len;
123 char *path;
125 e = xs_directory(h, "/domain", &num);
127 i=0;
128 while (i < num) {
129 asprintf(&path, "/domain/%s/name", e[i]);
130 val = xs_read(h, path, &len);
131 free(path);
132 if (val == NULL)
133 continue;
134 if (strcmp(val, name) == 0) {
135 /* match! */
136 asprintf(&path, "/domain/%s/uuid", e[i]);
137 uuid = xs_read(h, path, &len);
138 free(val);
139 free(path);
140 break;
141 }
142 free(val);
143 i++;
144 }
146 free(e);
147 return uuid;
148 }
150 static int strsep_len(const char *str, char c, unsigned int len)
151 {
152 unsigned int i;
154 for (i = 0; str[i]; i++)
155 if (str[i] == c) {
156 if (len == 0)
157 return i;
158 len--;
159 }
160 return (len == 0) ? i : -ERANGE;
161 }
164 /* xenbus watches: */
165 /* Register callback to watch this node. */
166 struct xenbus_watch
167 {
168 struct list_head list;
169 char *node;
170 void (*callback)(struct xs_handle *h,
171 struct xenbus_watch *,
172 const char *node);
173 };
175 static LIST_HEAD(watches);
177 /* A little paranoia: we don't just trust token. */
178 static struct xenbus_watch *find_watch(const char *token)
179 {
180 struct xenbus_watch *i, *cmp;
182 cmp = (void *)strtoul(token, NULL, 16);
184 list_for_each_entry(i, &watches, list)
185 if (i == cmp)
186 return i;
187 return NULL;
188 }
190 /* Register callback to watch this node. like xs_watch, return 0 on failure */
191 int register_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch)
192 {
193 /* Pointer in ascii is the token. */
194 char token[sizeof(watch) * 2 + 1];
195 int er;
197 sprintf(token, "%lX", (long)watch);
198 if (find_watch(token))
199 {
200 warn("watch collision!");
201 return -EINVAL;
202 }
204 er = xs_watch(h, watch->node, token);
205 if (er != 0) {
206 list_add(&watch->list, &watches);
207 }
209 return er;
210 }
212 int unregister_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch)
213 {
214 char token[sizeof(watch) * 2 + 1];
215 int er;
217 sprintf(token, "%lX", (long)watch);
218 if (!find_watch(token))
219 {
220 warn("no such watch!");
221 return -EINVAL;
222 }
225 er = xs_unwatch(h, watch->node, token);
226 list_del(&watch->list);
228 if (er == 0)
229 warn("XENBUS Failed to release watch %s: %i",
230 watch->node, er);
231 return 0;
232 }
234 /* Re-register callbacks to all watches. */
235 void reregister_xenbus_watches(struct xs_handle *h)
236 {
237 struct xenbus_watch *watch;
238 char token[sizeof(watch) * 2 + 1];
240 list_for_each_entry(watch, &watches, list) {
241 sprintf(token, "%lX", (long)watch);
242 xs_watch(h, watch->node, token);
243 }
244 }
246 /* based on watch_thread() */
247 int xs_fire_next_watch(struct xs_handle *h)
248 {
249 char **res;
250 char *token;
251 char *node = NULL;
252 struct xenbus_watch *w;
253 int er;
255 res = xs_read_watch(h);
256 if (res == NULL)
257 return -EAGAIN; /* in O_NONBLOCK, read_watch returns 0... */
259 node = res[0];
260 token = res[1];
262 er = xs_acknowledge_watch(h, token);
263 if (er == 0)
264 warn("Couldn't acknowledge watch (%s)", token);
266 w = find_watch(token);
267 if (!w)
268 {
269 warn("unregistered watch fired");
270 goto done;
271 }
272 w->callback(h, w, node);
274 done:
275 free(res);
276 return 1;
277 }
282 /* ---------------------------------------------------------------------- */
284 struct backend_info
285 {
286 /* our communications channel */
287 blkif_t *blkif;
289 long int frontend_id;
290 long int pdev;
291 long int readonly;
293 /* watch back end for changes */
294 struct xenbus_watch backend_watch;
295 char *backpath;
297 /* watch front end for changes */
298 struct xenbus_watch watch;
299 char *frontpath;
301 struct list_head list;
302 };
304 static LIST_HEAD(belist);
306 static struct backend_info *be_lookup_be(const char *bepath)
307 {
308 struct backend_info *be;
310 list_for_each_entry(be, &belist, list)
311 if (strcmp(bepath, be->backpath) == 0)
312 return be;
313 return (struct backend_info *)NULL;
314 }
316 static int be_exists_be(const char *bepath)
317 {
318 return ( be_lookup_be(bepath) != NULL );
319 }
321 static struct backend_info *be_lookup_fe(const char *fepath)
322 {
323 struct backend_info *be;
325 list_for_each_entry(be, &belist, list)
326 if (strcmp(fepath, be->frontpath) == 0)
327 return be;
328 return (struct backend_info *)NULL;
329 }
331 static int backend_remove(struct xs_handle *h, struct backend_info *be)
332 {
333 /* Turn off watches. */
334 if (be->watch.node)
335 unregister_xenbus_watch(h, &be->watch);
336 if (be->backend_watch.node)
337 unregister_xenbus_watch(h, &be->backend_watch);
339 /* Unhook from be list. */
340 list_del(&be->list);
342 /* Free everything else. */
343 if (be->blkif)
344 free_blkif(be->blkif);
345 if (be->frontpath)
346 free(be->frontpath);
347 if (be->backpath)
348 free(be->backpath);
349 free(be);
350 return 0;
351 }
353 static void frontend_changed(struct xs_handle *h, struct xenbus_watch *w,
354 const char *fepath_im)
355 {
356 struct backend_info *be;
357 char *fepath = NULL;
358 int er;
360 be = be_lookup_fe(w->node);
361 if (be == NULL)
362 {
363 warn("frontend changed called for nonexistent backend! (%s)", fepath);
364 goto fail;
365 }
367 /* If other end is gone, delete ourself. */
368 if (w->node && !xs_exists(h, be->frontpath)) {
369 DPRINTF("DELETING BE: %s\n", be->backpath);
370 backend_remove(h, be);
371 return;
372 }
374 if (be->blkif == NULL || (be->blkif->state == CONNECTED))
375 return;
377 /* Supply the information about the device the frontend needs */
378 er = xs_transaction_start(h, be->backpath);
379 if (er == 0) {
380 warn("starting transaction");
381 goto fail;
382 }
384 er = xs_printf(h, be->backpath, "sectors", "%lu",
385 be->blkif->ops->get_size(be->blkif));
386 if (er == 0) {
387 warn("writing sectors");
388 goto fail;
389 }
391 er = xs_printf(h, be->backpath, "info", "%u",
392 be->blkif->ops->get_info(be->blkif));
393 if (er == 0) {
394 warn("writing info");
395 goto fail;
396 }
398 er = xs_printf(h, be->backpath, "sector-size", "%lu",
399 be->blkif->ops->get_secsize(be->blkif));
400 if (er == 0) {
401 warn("writing sector-size");
402 goto fail;
403 }
405 be->blkif->state = CONNECTED;
407 xs_transaction_end(h, 0);
409 return;
411 fail:
412 if (fepath)
413 free(fepath);
414 }
417 static void backend_changed(struct xs_handle *h, struct xenbus_watch *w,
418 const char *bepath_im)
419 {
420 struct backend_info *be;
421 char *path = NULL, *p;
422 int len, er;
423 long int pdev = 0, handle;
425 be = be_lookup_be(w->node);
426 if (be == NULL)
427 {
428 warn("backend changed called for nonexistent backend! (%s)", w->node);
429 goto fail;
430 }
432 er = xs_gather(h, be->backpath, "physical-device", "%li", &pdev, NULL);
433 if (er != 0)
434 goto fail;
436 if (be->pdev && be->pdev != pdev) {
437 warn("changing physical-device not supported");
438 goto fail;
439 }
440 be->pdev = pdev;
442 asprintf(&path, "%s/%s", w->node, "read-only");
443 if (xs_exists(h, path))
444 be->readonly = 1;
446 if (be->blkif == NULL) {
447 /* Front end dir is a number, which is used as the handle. */
448 p = strrchr(be->frontpath, '/') + 1;
449 handle = strtoul(p, NULL, 0);
451 be->blkif = alloc_blkif(be->frontend_id);
452 if (be->blkif == NULL)
453 goto fail;
455 er = blkif_init(be->blkif, handle, be->pdev, be->readonly);
456 if (er)
457 goto fail;
459 DPRINTF("[BECHG]: ADDED A NEW BLKIF (%s)\n", w->node);
461 /* Pass in NULL node to skip exist test. */
462 frontend_changed(h, &be->watch, NULL);
463 }
465 fail:
466 if (path)
467 free(path);
469 }
471 static void blkback_probe(struct xs_handle *h, struct xenbus_watch *w,
472 const char *bepath_im)
473 {
474 struct backend_info *be = NULL;
475 char *frontend = NULL, *bepath = NULL;
476 int er, len;
478 bepath = strdup(bepath_im);
479 if (!bepath)
480 return;
481 len = strsep_len(bepath, '/', 6);
482 if (len < 0)
483 goto free_be;
485 bepath[len] = '\0'; /*truncate the passed-in string with predjudice. */
487 be = malloc(sizeof(*be));
488 if (!be) {
489 warn("allocating backend structure");
490 goto free_be;
491 }
492 memset(be, 0, sizeof(*be));
494 frontend = NULL;
495 er = xs_gather(h, bepath,
496 "frontend-id", "%li", &be->frontend_id,
497 "frontend", NULL, &frontend,
498 NULL);
499 if (er)
500 goto free_be;
502 if (strlen(frontend) == 0 || !xs_exists(h, frontend)) {
503 /* If we can't get a frontend path and a frontend-id,
504 * then our bus-id is no longer valid and we need to
505 * destroy the backend device.
506 */
507 DPRINTF("No frontend (%s)\n", frontend);
508 goto free_be;
509 }
511 /* Are we already tracking this device? */
512 if (be_exists_be(bepath))
513 goto free_be;
515 be->backpath = bepath;
516 be->backend_watch.node = be->backpath;
517 be->backend_watch.callback = backend_changed;
518 er = register_xenbus_watch(h, &be->backend_watch);
519 if (er == 0) {
520 be->backend_watch.node = NULL;
521 warn("error adding backend watch on %s", bepath);
522 goto free_be;
523 }
525 be->frontpath = frontend;
526 be->watch.node = be->frontpath;
527 be->watch.callback = frontend_changed;
528 er = register_xenbus_watch(h, &be->watch);
529 if (er == 0) {
530 be->watch.node = NULL;
531 warn("adding frontend watch on %s", be->frontpath);
532 goto free_be;
533 }
535 list_add(&be->list, &belist);
537 DPRINTF("[PROBE]: ADDED NEW DEVICE (%s)\n", bepath_im);
539 backend_changed(h, &be->backend_watch, bepath);
540 return;
542 free_be:
543 if ((be) && (be->backend_watch.node))
544 unregister_xenbus_watch(h, &be->backend_watch);
545 if (frontend)
546 free(frontend);
547 if (bepath)
548 free(bepath);
549 free(be);
550 return;
551 }
554 int add_blockdevice_probe_watch(struct xs_handle *h, const char *domname)
555 {
556 char *uuid, *path;
557 struct xenbus_watch *vbd_watch;
558 int er;
560 uuid = get_dom_uuid(h, domname);
562 DPRINTF("%s: %s\n", domname, (uuid != NULL) ? uuid : "[ not found! ]");
564 asprintf(&path, "/domain/%s/backend/vbd", uuid);
565 if (path == NULL)
566 return -ENOMEM;
568 vbd_watch = (struct xenbus_watch *)malloc(sizeof(struct xenbus_watch));
569 vbd_watch->node = path;
570 vbd_watch->callback = blkback_probe;
571 er = register_xenbus_watch(h, vbd_watch);
572 if (er == 0) {
573 warn("Error adding vbd probe watch %s", path);
574 return -EINVAL;
575 }
577 return 0;
578 }