ia64/xen-unstable

view tools/blktap/lib/xenbus.c @ 14105:e9bd3267ff23

[TAPDISK] honor read-only attributes when creating tap-based VBDs
Signed-off-by: Jake Wires <jwires@xensource.com>
author Jake Wires <jwires@xensource.com>
date Fri Feb 23 17:26:07 2007 -0800 (2007-02-23)
parents 4666710bfc55
children b515e66234e8
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 <printf.h>
41 #include <string.h>
42 #include <err.h>
43 #include <stdarg.h>
44 #include <errno.h>
45 #include <xs.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <fcntl.h>
49 #include <poll.h>
50 #include <time.h>
51 #include <sys/time.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 struct backend_info *be_lookup_be(const char *bepath)
111 {
112 struct backend_info *be;
114 list_for_each_entry(be, &belist, list)
115 if (strcmp(bepath, be->backpath) == 0)
116 return be;
117 return (struct backend_info *)NULL;
118 }
120 static int be_exists_be(const char *bepath)
121 {
122 return (be_lookup_be(bepath) != NULL);
123 }
125 static struct backend_info *be_lookup_fe(const char *fepath)
126 {
127 struct backend_info *be;
129 list_for_each_entry(be, &belist, list)
130 if (strcmp(fepath, be->frontpath) == 0)
131 return be;
132 return (struct backend_info *)NULL;
133 }
135 static int backend_remove(struct xs_handle *h, struct backend_info *be)
136 {
137 /* Unhook from be list. */
138 list_del(&be->list);
140 /* Free everything else. */
141 if (be->blkif) {
142 DPRINTF("Freeing blkif dev [%d]\n",be->blkif->devnum);
143 free_blkif(be->blkif);
144 }
145 if (be->frontpath)
146 free(be->frontpath);
147 if (be->backpath)
148 free(be->backpath);
149 free(be);
150 return 0;
151 }
153 static void ueblktap_setup(struct xs_handle *h, char *bepath)
154 {
155 struct backend_info *be;
156 char *path = NULL, *p,*dev;
157 int len, er, deverr;
158 long int pdev = 0, handle;
159 blkif_info_t *blk;
161 be = be_lookup_be(bepath);
162 if (be == NULL)
163 {
164 DPRINTF("ERROR: backend changed called for nonexistent "
165 "backend! (%s)\n", bepath);
166 goto fail;
167 }
169 deverr = xs_gather(h, bepath, "physical-device", "%li", &pdev, NULL);
170 if (!deverr) {
171 DPRINTF("pdev set to %ld\n",pdev);
172 if (be->pdev && be->pdev != pdev) {
173 DPRINTF("changing physical-device not supported");
174 goto fail;
175 }
176 be->pdev = pdev;
177 }
179 /* Check to see if device is to be opened read-only. */
180 deverr = xs_gather(h, bepath, "mode", NULL, &path, NULL);
181 if (deverr) {
182 DPRINTF("ERROR: could not find read/write mode\n");
183 goto fail;
184 } else if (path[0] == 'r')
185 be->readonly = 1;
187 if (be->blkif == NULL) {
188 /* Front end dir is a number, which is used as the handle. */
189 p = strrchr(be->frontpath, '/') + 1;
190 handle = strtoul(p, NULL, 0);
192 be->blkif = alloc_blkif(be->frontend_id);
193 if (be->blkif == NULL)
194 goto fail;
196 be->blkif->be_id = get_be_id(bepath);
198 /* Insert device specific info, */
199 blk = malloc(sizeof(blkif_info_t));
200 if (!blk) {
201 DPRINTF("Out of memory - blkif_info_t\n");
202 goto fail;
203 }
204 er = xs_gather(h, bepath, "params", NULL, &blk->params, NULL);
205 if (er)
206 goto fail;
207 be->blkif->info = blk;
209 if (deverr) {
210 /*Dev number was not available, try to set manually*/
211 pdev = convert_dev_name_to_num(blk->params);
212 be->pdev = pdev;
213 }
215 er = blkif_init(be->blkif, handle, be->pdev, be->readonly);
216 if (er != 0) {
217 DPRINTF("Unable to open device %s\n",blk->params);
218 goto fail;
219 }
221 DPRINTF("[BECHG]: ADDED A NEW BLKIF (%s)\n", bepath);
222 }
224 /* Supply the information about the device to xenstore */
225 er = xs_printf(h, be->backpath, "sectors", "%llu",
226 be->blkif->ops->get_size(be->blkif));
228 if (er == 0) {
229 DPRINTF("ERROR: Failed writing sectors");
230 goto fail;
231 }
233 er = xs_printf(h, be->backpath, "sector-size", "%lu",
234 be->blkif->ops->get_secsize(be->blkif));
236 if (er == 0) {
237 DPRINTF("ERROR: Failed writing sector-size");
238 goto fail;
239 }
241 er = xs_printf(h, be->backpath, "info", "%u",
242 be->blkif->ops->get_info(be->blkif));
244 if (er == 0) {
245 DPRINTF("ERROR: Failed writing info");
246 goto fail;
247 }
249 be->blkif->state = CONNECTED;
250 DPRINTF("[SETUP] Complete\n\n");
251 goto close;
253 fail:
254 if ( (be != NULL) && (be->blkif != NULL) )
255 backend_remove(h, be);
256 close:
257 if (path)
258 free(path);
259 return;
260 }
262 /**
263 * Xenstore watch callback entry point. This code replaces the hotplug scripts,
264 * and as soon as the xenstore backend driver entries are created, this script
265 * gets called.
266 */
267 static void ueblktap_probe(struct xs_handle *h, struct xenbus_watch *w,
268 const char *bepath_im)
269 {
270 struct backend_info *be = NULL;
271 char *frontend = NULL, *bepath = NULL, *p;
272 int er, len;
273 blkif_t *blkif;
276 bepath = strdup(bepath_im);
278 if (!bepath) {
279 DPRINTF("No path\n");
280 return;
281 }
283 /*
284 *asserts that xenstore structure is always 7 levels deep
285 *e.g. /local/domain/0/backend/vbd/1/2049
286 */
287 len = strsep_len(bepath, '/', 7);
288 if (len < 0)
289 goto free_be;
290 bepath[len] = '\0';
292 be = malloc(sizeof(*be));
293 if (!be) {
294 DPRINTF("ERROR: allocating backend structure\n");
295 goto free_be;
296 }
297 memset(be, 0, sizeof(*be));
298 frontend = NULL;
300 er = xs_gather(h, bepath,
301 "frontend-id", "%li", &be->frontend_id,
302 "frontend", NULL, &frontend,
303 NULL);
305 if (er) {
306 /*
307 *Unable to find frontend entries,
308 *bus-id is no longer valid
309 */
310 DPRINTF("ERROR: Frontend-id check failed, removing backend: "
311 "[%s]\n",bepath);
313 /**
314 * BE info should already exist,
315 * free new mem and find old entry
316 */
317 free(be);
318 be = be_lookup_be(bepath);
319 if ( (be != NULL) && (be->blkif != NULL) )
320 backend_remove(h, be);
321 else goto free_be;
322 if (bepath)
323 free(bepath);
324 return;
325 }
327 /* Are we already tracking this device? */
328 if (be_exists_be(bepath))
329 goto free_be;
331 be->backpath = bepath;
332 be->frontpath = frontend;
334 list_add(&be->list, &belist);
336 DPRINTF("[PROBE]\tADDED NEW DEVICE (%s)\n", bepath);
337 DPRINTF("\tFRONTEND (%s),(%ld)\n", frontend,be->frontend_id);
339 ueblktap_setup(h, bepath);
340 return;
342 free_be:
343 if (frontend)
344 free(frontend);
345 if (bepath)
346 free(bepath);
347 if (be)
348 free(be);
349 }
351 /**
352 *We set a general watch on the backend vbd directory
353 *ueblktap_probe is called for every update
354 *Our job is to monitor for new entries. As they
355 *are created, we initalise the state and attach a disk.
356 */
358 int add_blockdevice_probe_watch(struct xs_handle *h, const char *domid)
359 {
360 char *path;
361 struct xenbus_watch *vbd_watch;
363 asprintf(&path, "/local/domain/%s/backend/tap", domid);
364 if (path == NULL)
365 return -ENOMEM;
367 vbd_watch = (struct xenbus_watch *)malloc(sizeof(struct xenbus_watch));
368 if (!vbd_watch) {
369 DPRINTF("ERROR: unable to malloc vbd_watch [%s]\n", path);
370 return -EINVAL;
371 }
372 vbd_watch->node = path;
373 vbd_watch->callback = ueblktap_probe;
374 if (register_xenbus_watch(h, vbd_watch) != 0) {
375 DPRINTF("ERROR: adding vbd probe watch %s\n", path);
376 return -EINVAL;
377 }
378 return 0;
379 }
381 /* Asynch callback to check for /local/domain/<DOMID>/name */
382 void check_dom(struct xs_handle *h, struct xenbus_watch *w,
383 const char *bepath_im)
384 {
385 char *domid;
387 domid = get_dom_domid(h);
388 if (domid == NULL)
389 return;
391 add_blockdevice_probe_watch(h, domid);
392 free(domid);
393 unregister_xenbus_watch(h, w);
394 }
396 /* We must wait for xend to register /local/domain/<DOMID> */
397 int watch_for_domid(struct xs_handle *h)
398 {
399 struct xenbus_watch *domid_watch;
400 char *path = NULL;
402 asprintf(&path, "/local/domain");
403 if (path == NULL)
404 return -ENOMEM;
406 domid_watch = malloc(sizeof(struct xenbus_watch));
407 if (domid_watch == NULL) {
408 DPRINTF("ERROR: unable to malloc domid_watch [%s]\n", path);
409 return -EINVAL;
410 }
412 domid_watch->node = path;
413 domid_watch->callback = check_dom;
415 if (register_xenbus_watch(h, domid_watch) != 0) {
416 DPRINTF("ERROR: adding vbd probe watch %s\n", path);
417 return -EINVAL;
418 }
420 DPRINTF("Set async watch for /local/domain\n");
422 return 0;
423 }
425 int setup_probe_watch(struct xs_handle *h)
426 {
427 char *domid;
428 int ret;
430 domid = get_dom_domid(h);
431 if (domid == NULL)
432 return watch_for_domid(h);
434 ret = add_blockdevice_probe_watch(h, domid);
435 free(domid);
436 return ret;
437 }