debuggers.hg

view tools/libxl/libxl_device.c @ 21965:20540b8fe4b6

xl: detect pci-insert-failed dm status on pci-passthrough

NOTE: This functionality depends on a corresponding qemu-dm patch to work as
expected. Should be safe to use with an un-patched qemu-dm as before.

libxl_wait_for_device_model can only wait for one status value, re-work the
API so that a callback function can chose between several different possible
status values for qemu-dm and fix up all callers appropriately.

In the case of PCI device insert we succeed if qemu-dm reports
"pci-device-inserted" and error out instead of hanging forever if it fails
since qemu-dm now reports a status of "pci-insert-failed".

Signed-off-by: Gianni Tedesco <gianni.tedesco@citrix.com>
Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
author Gianni Tedesco <gianni.tedesco@citrix.com>
date Wed Aug 04 14:43:46 2010 +0100 (2010-08-04)
parents 1a92c8881770
children 51147d5b17c3
line source
1 /*
2 * Copyright (C) 2009 Citrix Ltd.
3 * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
4 * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation; version 2.1 only. with the special
9 * exception on linking described in file LICENSE.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 */
17 #include "libxl_osdeps.h"
19 #include <string.h>
20 #include <stdio.h>
21 #include <sys/time.h> /* for struct timeval */
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <fcntl.h>
28 #include "libxl.h"
29 #include "libxl_internal.h"
31 static const char *string_of_kinds[] = {
32 [DEVICE_VIF] = "vif",
33 [DEVICE_VIF2] = "vif2",
34 [DEVICE_VBD] = "vbd",
35 [DEVICE_TAP] = "tap",
36 [DEVICE_PCI] = "pci",
37 [DEVICE_VFB] = "vfb",
38 [DEVICE_VKBD] = "vkbd",
39 [DEVICE_CONSOLE] = "console",
40 };
42 int libxl_device_generic_add(libxl_ctx *ctx, libxl_device *device,
43 char **bents, char **fents)
44 {
45 char *dom_path_backend, *dom_path, *frontend_path, *backend_path;
46 xs_transaction_t t;
47 struct xs_permissions frontend_perms[2];
48 struct xs_permissions backend_perms[2];
50 if (!is_valid_device_kind(device->backend_kind) || !is_valid_device_kind(device->kind))
51 return ERROR_INVAL;
53 dom_path_backend = libxl_xs_get_dompath(ctx, device->backend_domid);
54 dom_path = libxl_xs_get_dompath(ctx, device->domid);
56 frontend_path = libxl_sprintf(ctx, "%s/device/%s/%d",
57 dom_path, string_of_kinds[device->kind], device->devid);
58 backend_path = libxl_sprintf(ctx, "%s/backend/%s/%u/%d",
59 dom_path_backend, string_of_kinds[device->backend_kind], device->domid, device->devid);
61 frontend_perms[0].id = device->domid;
62 frontend_perms[0].perms = XS_PERM_NONE;
63 frontend_perms[1].id = device->backend_domid;
64 frontend_perms[1].perms = XS_PERM_READ;
66 backend_perms[0].id = device->backend_domid;
67 backend_perms[0].perms = XS_PERM_NONE;
68 backend_perms[1].id = device->domid;
69 backend_perms[1].perms = XS_PERM_READ;
71 retry_transaction:
72 t = xs_transaction_start(ctx->xsh);
73 /* FIXME: read frontend_path and check state before removing stuff */
75 xs_rm(ctx->xsh, t, frontend_path);
76 xs_rm(ctx->xsh, t, backend_path);
78 xs_mkdir(ctx->xsh, t, frontend_path);
79 xs_set_permissions(ctx->xsh, t, frontend_path, frontend_perms, ARRAY_SIZE(frontend_perms));
81 xs_mkdir(ctx->xsh, t, backend_path);
82 xs_set_permissions(ctx->xsh, t, backend_path, backend_perms, ARRAY_SIZE(backend_perms));
84 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/backend", frontend_path), backend_path, strlen(backend_path));
85 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/frontend", backend_path), frontend_path, strlen(frontend_path));
87 /* and write frontend kvs and backend kvs */
88 libxl_xs_writev(ctx, t, backend_path, bents);
89 libxl_xs_writev(ctx, t, frontend_path, fents);
91 if (!xs_transaction_end(ctx->xsh, t, 0)) {
92 if (errno == EAGAIN)
93 goto retry_transaction;
94 else
95 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "xs transaction failed");
96 }
97 return 0;
98 }
100 char *device_disk_string_of_phystype(libxl_disk_phystype phystype)
101 {
102 switch (phystype) {
103 case PHYSTYPE_QCOW: return "qcow";
104 case PHYSTYPE_QCOW2: return "qcow2";
105 case PHYSTYPE_VHD: return "vhd";
106 case PHYSTYPE_AIO: return "aio";
107 case PHYSTYPE_FILE: return "file";
108 case PHYSTYPE_PHY: return "phy";
109 default: return NULL;
110 }
111 }
113 char *device_disk_backend_type_of_phystype(libxl_disk_phystype phystype)
114 {
115 switch (phystype) {
116 case PHYSTYPE_QCOW: return "tap";
117 case PHYSTYPE_VHD: return "tap";
118 case PHYSTYPE_AIO: return "tap";
119 /* let's pretend file is tap:aio */
120 case PHYSTYPE_FILE: return "tap";
121 case PHYSTYPE_PHY: return "phy";
122 default: return NULL;
123 }
124 }
126 int device_physdisk_major_minor(const char *physpath, int *major, int *minor)
127 {
128 struct stat buf;
129 if (stat(physpath, &buf) < 0)
130 return -1;
131 *major = major(buf.st_rdev);
132 *minor = minor(buf.st_rdev);
133 return 0;
134 }
136 static int device_virtdisk_matches(const char *virtpath, const char *devtype,
137 int *index_r, int max_index,
138 int *partition_r, int max_partition) {
139 const char *p;
140 char *ep;
141 int tl, c;
142 long pl;
144 tl = strlen(devtype);
145 if (memcmp(virtpath, devtype, tl))
146 return 0;
148 /* We decode the drive letter as if it were in base 52
149 * with digits a-zA-Z, more or less */
150 *index_r = -1;
151 p = virtpath + tl;
152 for (;;) {
153 c = *p++;
154 if (c >= 'a' && c <= 'z') {
155 c -= 'a';
156 } else {
157 --p;
158 break;
159 }
160 (*index_r)++;
161 (*index_r) *= 26;
162 (*index_r) += c;
164 if (*index_r > max_index)
165 return 0;
166 }
168 if (!*p) {
169 *partition_r = 0;
170 return 1;
171 }
173 if (*p=='0')
174 return 0; /* leading zeroes not permitted in partition number */
176 pl = strtoul(p, &ep, 10);
177 if (pl > max_partition || *ep)
178 return 0;
180 *partition_r = pl;
181 return 1;
182 }
184 int device_disk_dev_number(char *virtpath)
185 {
186 int disk, partition;
187 char *ep;
188 unsigned long ul;
189 int chrused;
191 chrused = -1;
192 if ((sscanf(virtpath, "d%ip%i%n", &disk, &partition, &chrused) >= 2
193 && chrused == strlen(virtpath) && disk < (1<<20) && partition < 256)
194 ||
195 device_virtdisk_matches(virtpath, "xvd",
196 &disk, (1<<20)-1,
197 &partition, 255)) {
198 if (disk <= 15 && partition <= 15)
199 return (202 << 8) | (disk << 4) | partition;
200 else
201 return (1 << 28) | (disk << 8) | partition;
202 }
204 errno = 0;
205 ul = strtoul(virtpath, &ep, 0);
206 if (!errno && !*ep && ul <= INT_MAX)
207 return ul;
209 if (device_virtdisk_matches(virtpath, "hd",
210 &disk, 3,
211 &partition, 63)) {
212 return ((disk<2 ? 3 : 22) << 8) | ((disk & 1) << 6) | partition;
213 }
214 if (device_virtdisk_matches(virtpath, "sd",
215 &disk, 15,
216 &partition, 15)) {
217 return (8 << 8) | (disk << 4) | partition;
218 }
219 return -1;
220 }
222 int libxl_device_destroy(libxl_ctx *ctx, char *be_path, int force)
223 {
224 xs_transaction_t t;
225 char *state_path = libxl_sprintf(ctx, "%s/state", be_path);
226 char *state = libxl_xs_read(ctx, XBT_NULL, state_path);
227 if (!state)
228 return 0;
229 if (atoi(state) != 4) {
230 xs_rm(ctx->xsh, XBT_NULL, be_path);
231 return 0;
232 }
234 retry_transaction:
235 t = xs_transaction_start(ctx->xsh);
236 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/online", be_path), "0", strlen("0"));
237 xs_write(ctx->xsh, t, state_path, "5", strlen("5"));
238 if (!xs_transaction_end(ctx->xsh, t, 0)) {
239 if (errno == EAGAIN)
240 goto retry_transaction;
241 else
242 return -1;
243 }
244 if (!force) {
245 xs_watch(ctx->xsh, state_path, be_path);
246 return 1;
247 } else
248 return 0;
249 }
251 int wait_for_dev_destroy(libxl_ctx *ctx, struct timeval *tv)
252 {
253 int nfds, rc;
254 unsigned int n;
255 fd_set rfds;
256 char **l1 = NULL;
258 rc = 1;
259 nfds = xs_fileno(ctx->xsh) + 1;
260 FD_ZERO(&rfds);
261 FD_SET(xs_fileno(ctx->xsh), &rfds);
262 if (select(nfds, &rfds, NULL, NULL, tv) > 0) {
263 l1 = xs_read_watch(ctx->xsh, &n);
264 if (l1 != NULL) {
265 char *state = libxl_xs_read(ctx, XBT_NULL, l1[XS_WATCH_PATH]);
266 if (!state || atoi(state) == 6) {
267 xs_unwatch(ctx->xsh, l1[0], l1[1]);
268 xs_rm(ctx->xsh, XBT_NULL, l1[XS_WATCH_TOKEN]);
269 XL_LOG(ctx, XL_LOG_DEBUG, "Destroyed device backend at %s", l1[XS_WATCH_TOKEN]);
270 rc = 0;
271 }
272 libxl_free(ctx, state);
273 free(l1);
274 }
275 }
276 return rc;
277 }
279 int libxl_devices_destroy(libxl_ctx *ctx, uint32_t domid, int force)
280 {
281 char *path, *be_path, *fe_path;
282 unsigned int num1, num2;
283 char **l1 = NULL, **l2 = NULL;
284 int i, j, n = 0, n_watches = 0;
285 flexarray_t *toremove;
286 libxl_ctx clone;
288 if (libxl_ctx_init(&clone, LIBXL_VERSION, ctx->lg)) {
289 return -1;
290 }
292 toremove = flexarray_make(16, 1);
293 path = libxl_sprintf(&clone, "/local/domain/%d/device", domid);
294 l1 = libxl_xs_directory(&clone, XBT_NULL, path, &num1);
295 if (!l1) {
296 XL_LOG(&clone, XL_LOG_ERROR, "%s is empty", path);
297 libxl_ctx_free(&clone);
298 return 0;
299 }
300 for (i = 0; i < num1; i++) {
301 if (!strcmp("vfs", l1[i]))
302 continue;
303 path = libxl_sprintf(&clone, "/local/domain/%d/device/%s", domid, l1[i]);
304 l2 = libxl_xs_directory(&clone, XBT_NULL, path, &num2);
305 if (!l2)
306 continue;
307 for (j = 0; j < num2; j++) {
308 fe_path = libxl_sprintf(&clone, "/local/domain/%d/device/%s/%s", domid, l1[i], l2[j]);
309 be_path = libxl_xs_read(&clone, XBT_NULL, libxl_sprintf(&clone, "%s/backend", fe_path));
310 if (be_path != NULL) {
311 if (libxl_device_destroy(&clone, be_path, force) > 0)
312 n_watches++;
313 flexarray_set(toremove, n++, libxl_dirname(&clone, be_path));
314 } else {
315 xs_rm(clone.xsh, XBT_NULL, path);
316 }
317 }
318 }
319 if (!force) {
320 /* Linux-ism. Most implementations leave the timeout
321 * untouched after select. Linux, however, will chip
322 * away the elapsed time from it, which is what we
323 * need to enforce a single time span waiting for
324 * device destruction. */
325 struct timeval tv;
326 tv.tv_sec = LIBXL_DESTROY_TIMEOUT;
327 tv.tv_usec = 0;
328 while (n_watches > 0) {
329 if (wait_for_dev_destroy(&clone, &tv)) {
330 break;
331 } else {
332 n_watches--;
333 }
334 }
335 }
336 for (i = 0; i < n; i++) {
337 flexarray_get(toremove, i, (void**) &path);
338 xs_rm(clone.xsh, XBT_NULL, path);
339 }
340 flexarray_free(toremove);
341 libxl_ctx_free(&clone);
342 return 0;
343 }
345 int libxl_device_del(libxl_ctx *ctx, libxl_device *dev, int wait)
346 {
347 char *dom_path_backend, *backend_path;
348 int rc;
349 libxl_ctx clone;
351 if (libxl_ctx_init(&clone, LIBXL_VERSION, ctx->lg)) {
352 return -1;
353 }
355 /* Create strings */
356 dom_path_backend = libxl_xs_get_dompath(&clone, dev->backend_domid);
357 backend_path = libxl_sprintf(&clone, "%s/backend/%s/%u/%d",
358 dom_path_backend,
359 string_of_kinds[dev->backend_kind],
360 dev->domid, dev->devid);
361 libxl_free(&clone, dom_path_backend);
363 rc = libxl_device_destroy(&clone, backend_path, !wait);
364 if (rc == -1) {
365 libxl_ctx_free(&clone);
366 return ERROR_FAIL;
367 }
369 if (wait) {
370 struct timeval tv;
371 tv.tv_sec = LIBXL_DESTROY_TIMEOUT;
372 tv.tv_usec = 0;
373 (void)wait_for_dev_destroy(&clone, &tv);
374 }
376 libxl_ctx_free(&clone);
377 return 0;
378 }
380 int libxl_wait_for_device_model(libxl_ctx *ctx,
381 uint32_t domid, char *state,
382 int (*check_callback)(libxl_ctx *ctx,
383 uint32_t domid,
384 const char *state,
385 void *userdata),
386 void *check_callback_userdata)
387 {
388 char *path;
389 char *p;
390 unsigned int len;
391 int rc = 0;
392 struct xs_handle *xsh;
393 int nfds;
394 fd_set rfds;
395 struct timeval tv;
396 unsigned int num;
397 char **l = NULL;
399 xsh = xs_daemon_open();
400 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/state", domid);
401 xs_watch(xsh, path, path);
402 tv.tv_sec = LIBXL_DEVICE_MODEL_START_TIMEOUT;
403 tv.tv_usec = 0;
404 nfds = xs_fileno(xsh) + 1;
405 while (rc > 0 || (!rc && tv.tv_sec > 0)) {
406 p = xs_read(xsh, XBT_NULL, path, &len);
407 if ( NULL == p )
408 goto again;
410 if ( NULL != state && strcmp(p, state) )
411 goto again;
413 if ( NULL != check_callback ) {
414 rc = (*check_callback)(ctx, domid, p, check_callback_userdata);
415 if ( rc > 0 )
416 goto again;
417 }
419 free(p);
420 xs_unwatch(xsh, path, path);
421 xs_daemon_close(xsh);
422 return rc;
423 again:
424 free(p);
425 FD_ZERO(&rfds);
426 FD_SET(xs_fileno(xsh), &rfds);
427 rc = select(nfds, &rfds, NULL, NULL, &tv);
428 if (rc > 0) {
429 l = xs_read_watch(xsh, &num);
430 if (l != NULL)
431 free(l);
432 else
433 goto again;
434 }
435 }
436 xs_unwatch(xsh, path, path);
437 xs_daemon_close(xsh);
438 XL_LOG(ctx, XL_LOG_ERROR, "Device Model not ready");
439 return -1;
440 }
442 int libxl_wait_for_backend(libxl_ctx *ctx, char *be_path, char *state)
443 {
444 int watchdog = 100;
445 unsigned int len;
446 char *p;
447 char *path = libxl_sprintf(ctx, "%s/state", be_path);
449 while (watchdog > 0) {
450 p = xs_read(ctx->xsh, XBT_NULL, path, &len);
451 if (p == NULL) {
452 if (errno == ENOENT) {
453 XL_LOG(ctx, XL_LOG_ERROR, "Backend %s does not exist",
454 be_path);
455 } else {
456 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Failed to access backend %s",
457 be_path);
458 }
459 return -1;
460 } else {
461 if (!strcmp(p, state)) {
462 return 0;
463 } else {
464 usleep(100000);
465 watchdog--;
466 }
467 }
468 }
469 XL_LOG(ctx, XL_LOG_ERROR, "Backend %s not ready", be_path);
470 return -1;
471 }