qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

xen: add xenstore watcher infrastructure

A Xen PV frontend communicates its state to the PV backend by writing to
the 'state' key in the frontend area in xenstore. It is therefore
necessary for a XenDevice implementation to be notified whenever the
value of this key changes.

This patch adds code to do this as follows:

- an 'fd handler' is registered on the libxenstore handle which will be
triggered whenever a 'watch' event occurs
- primitives are added to xen-bus-helper to add or remove watch events
- a list of Notifier objects is added to XenBus to provide a mechanism
to call the appropriate 'watch handler' when its associated event
occurs

The xen-block implementation is extended with a 'frontend_changed' method,
which calls as-yet stub 'connect' and 'disconnect' functions when the
relevant frontend state transitions occur. A subsequent patch will supply
a full implementation for these functions.

Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
Reviewed-by: Anthony Perard <anthony.perard@citrix.com>
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>

authored by

Paul Durrant and committed by
Anthony PERARD
82a29e30 094a2239

+342 -2
+2
hw/block/trace-events
··· 130 130 131 131 # hw/block/xen-block.c 132 132 xen_block_realize(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u" 133 + xen_block_connect(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u" 134 + xen_block_disconnect(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u" 133 135 xen_block_unrealize(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u" 134 136 xen_disk_realize(void) "" 135 137 xen_disk_unrealize(void) ""
+70
hw/block/xen-block.c
··· 21 21 return g_strdup_printf("%lu", vdev->number); 22 22 } 23 23 24 + static void xen_block_disconnect(XenDevice *xendev, Error **errp) 25 + { 26 + XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev); 27 + const char *type = object_get_typename(OBJECT(blockdev)); 28 + XenBlockVdev *vdev = &blockdev->props.vdev; 29 + 30 + trace_xen_block_disconnect(type, vdev->disk, vdev->partition); 31 + } 32 + 33 + static void xen_block_connect(XenDevice *xendev, Error **errp) 34 + { 35 + XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev); 36 + const char *type = object_get_typename(OBJECT(blockdev)); 37 + XenBlockVdev *vdev = &blockdev->props.vdev; 38 + 39 + trace_xen_block_connect(type, vdev->disk, vdev->partition); 40 + } 41 + 24 42 static void xen_block_unrealize(XenDevice *xendev, Error **errp) 25 43 { 26 44 XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev); ··· 35 53 36 54 trace_xen_block_unrealize(type, vdev->disk, vdev->partition); 37 55 56 + /* Disconnect from the frontend in case this has not already happened */ 57 + xen_block_disconnect(xendev, NULL); 58 + 38 59 if (blockdev_class->unrealize) { 39 60 blockdev_class->unrealize(blockdev, errp); 40 61 } ··· 61 82 if (local_err) { 62 83 error_propagate(errp, local_err); 63 84 } 85 + } 86 + } 87 + 88 + static void xen_block_frontend_changed(XenDevice *xendev, 89 + enum xenbus_state frontend_state, 90 + Error **errp) 91 + { 92 + enum xenbus_state backend_state = xen_device_backend_get_state(xendev); 93 + Error *local_err = NULL; 94 + 95 + switch (frontend_state) { 96 + case XenbusStateInitialised: 97 + case XenbusStateConnected: 98 + if (backend_state == XenbusStateConnected) { 99 + break; 100 + } 101 + 102 + xen_block_disconnect(xendev, &local_err); 103 + if (local_err) { 104 + error_propagate(errp, local_err); 105 + break; 106 + } 107 + 108 + xen_block_connect(xendev, &local_err); 109 + if (local_err) { 110 + error_propagate(errp, local_err); 111 + break; 112 + } 113 + 114 + xen_device_backend_set_state(xendev, XenbusStateConnected); 115 + break; 116 + 117 + case XenbusStateClosing: 118 + xen_device_backend_set_state(xendev, XenbusStateClosing); 119 + break; 120 + 121 + case XenbusStateClosed: 122 + xen_block_disconnect(xendev, &local_err); 123 + if (local_err) { 124 + error_propagate(errp, local_err); 125 + break; 126 + } 127 + 128 + xen_device_backend_set_state(xendev, XenbusStateClosed); 129 + break; 130 + 131 + default: 132 + break; 64 133 } 65 134 } 66 135 ··· 272 341 273 342 xendev_class->get_name = xen_block_get_name; 274 343 xendev_class->realize = xen_block_realize; 344 + xendev_class->frontend_changed = xen_block_frontend_changed; 275 345 xendev_class->unrealize = xen_block_unrealize; 276 346 277 347 dev_class->props = xen_block_props;
+6
hw/xen/trace-events
··· 16 16 # include/hw/xen/xen-bus.c 17 17 xen_bus_realize(void) "" 18 18 xen_bus_unrealize(void) "" 19 + xen_bus_add_watch(const char *node, const char *key, char *token) "node: %s key: %s token: %s" 20 + xen_bus_remove_watch(const char *node, const char *key, char *token) "node: %s key: %s token: %s" 21 + xen_bus_watch(const char *token) "token: %s" 19 22 xen_device_realize(const char *type, char *name) "type: %s name: %s" 20 23 xen_device_unrealize(const char *type, char *name) "type: %s name: %s" 21 24 xen_device_backend_state(const char *type, char *name, const char *state) "type: %s name: %s -> %s" 22 25 xen_device_frontend_state(const char *type, char *name, const char *state) "type: %s name: %s -> %s" 26 + xen_device_frontend_changed(const char *type, char *name) "type: %s name: %s" 23 27 24 28 # include/hw/xen/xen-bus-helper.c 25 29 xs_node_create(const char *node) "%s" 26 30 xs_node_destroy(const char *node) "%s" 27 31 xs_node_vprintf(char *path, char *value) "%s %s" 28 32 xs_node_vscanf(char *path, char *value) "%s %s" 33 + xs_node_watch(char *path) "%s" 34 + xs_node_unwatch(char *path) "%s"
+34
hw/xen/xen-bus-helper.c
··· 148 148 149 149 return rc; 150 150 } 151 + 152 + void xs_node_watch(struct xs_handle *xsh, const char *node, const char *key, 153 + char *token, Error **errp) 154 + { 155 + char *path; 156 + 157 + path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) : 158 + g_strdup(key); 159 + 160 + trace_xs_node_watch(path); 161 + 162 + if (!xs_watch(xsh, path, token)) { 163 + error_setg_errno(errp, errno, "failed to watch node '%s'", path); 164 + } 165 + 166 + g_free(path); 167 + } 168 + 169 + void xs_node_unwatch(struct xs_handle *xsh, const char *node, 170 + const char *key, const char *token, Error **errp) 171 + { 172 + char *path; 173 + 174 + path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) : 175 + g_strdup(key); 176 + 177 + trace_xs_node_unwatch(path); 178 + 179 + if (!xs_unwatch(xsh, path, token)) { 180 + error_setg_errno(errp, errno, "failed to unwatch node '%s'", path); 181 + } 182 + 183 + g_free(path); 184 + }
+209 -2
hw/xen/xen-bus.c
··· 6 6 */ 7 7 8 8 #include "qemu/osdep.h" 9 + #include "qemu/main-loop.h" 10 + #include "qemu/uuid.h" 9 11 #include "hw/hw.h" 10 12 #include "hw/sysbus.h" 11 13 #include "hw/xen/xen.h" ··· 59 61 return xen_device_get_backend_path(XEN_DEVICE(dev)); 60 62 } 61 63 64 + struct XenWatch { 65 + char *node, *key; 66 + char *token; 67 + XenWatchHandler handler; 68 + void *opaque; 69 + Notifier notifier; 70 + }; 71 + 72 + static void watch_notify(Notifier *n, void *data) 73 + { 74 + XenWatch *watch = container_of(n, XenWatch, notifier); 75 + const char *token = data; 76 + 77 + if (!strcmp(watch->token, token)) { 78 + watch->handler(watch->opaque); 79 + } 80 + } 81 + 82 + static XenWatch *new_watch(const char *node, const char *key, 83 + XenWatchHandler handler, void *opaque) 84 + { 85 + XenWatch *watch = g_new0(XenWatch, 1); 86 + QemuUUID uuid; 87 + 88 + qemu_uuid_generate(&uuid); 89 + 90 + watch->token = qemu_uuid_unparse_strdup(&uuid); 91 + watch->node = g_strdup(node); 92 + watch->key = g_strdup(key); 93 + watch->handler = handler; 94 + watch->opaque = opaque; 95 + watch->notifier.notify = watch_notify; 96 + 97 + return watch; 98 + } 99 + 100 + static void free_watch(XenWatch *watch) 101 + { 102 + g_free(watch->token); 103 + g_free(watch->key); 104 + g_free(watch->node); 105 + 106 + g_free(watch); 107 + } 108 + 109 + static XenWatch *xen_bus_add_watch(XenBus *xenbus, const char *node, 110 + const char *key, XenWatchHandler handler, 111 + void *opaque, Error **errp) 112 + { 113 + XenWatch *watch = new_watch(node, key, handler, opaque); 114 + Error *local_err = NULL; 115 + 116 + trace_xen_bus_add_watch(watch->node, watch->key, watch->token); 117 + 118 + notifier_list_add(&xenbus->watch_notifiers, &watch->notifier); 119 + 120 + xs_node_watch(xenbus->xsh, node, key, watch->token, &local_err); 121 + if (local_err) { 122 + error_propagate(errp, local_err); 123 + 124 + notifier_remove(&watch->notifier); 125 + free_watch(watch); 126 + 127 + return NULL; 128 + } 129 + 130 + return watch; 131 + } 132 + 133 + static void xen_bus_remove_watch(XenBus *xenbus, XenWatch *watch, 134 + Error **errp) 135 + { 136 + trace_xen_bus_remove_watch(watch->node, watch->key, watch->token); 137 + 138 + xs_node_unwatch(xenbus->xsh, watch->node, watch->key, watch->token, 139 + errp); 140 + 141 + notifier_remove(&watch->notifier); 142 + free_watch(watch); 143 + } 144 + 62 145 static void xen_bus_unrealize(BusState *bus, Error **errp) 63 146 { 64 147 XenBus *xenbus = XEN_BUS(bus); ··· 69 152 return; 70 153 } 71 154 155 + qemu_set_fd_handler(xs_fileno(xenbus->xsh), NULL, NULL, NULL); 156 + 72 157 xs_close(xenbus->xsh); 73 158 } 74 159 160 + static void xen_bus_watch(void *opaque) 161 + { 162 + XenBus *xenbus = opaque; 163 + char **v; 164 + const char *token; 165 + 166 + g_assert(xenbus->xsh); 167 + 168 + v = xs_check_watch(xenbus->xsh); 169 + if (!v) { 170 + return; 171 + } 172 + 173 + token = v[XS_WATCH_TOKEN]; 174 + 175 + trace_xen_bus_watch(token); 176 + 177 + notifier_list_notify(&xenbus->watch_notifiers, (void *)token); 178 + 179 + free(v); 180 + } 181 + 75 182 static void xen_bus_realize(BusState *bus, Error **errp) 76 183 { 77 184 XenBus *xenbus = XEN_BUS(bus); ··· 92 199 xenbus->backend_id = 0; /* Assume lack of node means dom0 */ 93 200 } 94 201 202 + notifier_list_init(&xenbus->watch_notifiers); 203 + qemu_set_fd_handler(xs_fileno(xenbus->xsh), xen_bus_watch, NULL, 204 + xenbus); 95 205 return; 96 206 97 207 fail: ··· 139 249 } 140 250 } 141 251 142 - static void xen_device_backend_set_state(XenDevice *xendev, 143 - enum xenbus_state state) 252 + static int xen_device_backend_scanf(XenDevice *xendev, const char *key, 253 + const char *fmt, ...) 254 + { 255 + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 256 + va_list ap; 257 + int rc; 258 + 259 + g_assert(xenbus->xsh); 260 + 261 + va_start(ap, fmt); 262 + rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->backend_path, key, 263 + NULL, fmt, ap); 264 + va_end(ap); 265 + 266 + return rc; 267 + } 268 + 269 + void xen_device_backend_set_state(XenDevice *xendev, 270 + enum xenbus_state state) 144 271 { 145 272 const char *type = object_get_typename(OBJECT(xendev)); 146 273 ··· 155 282 xen_device_backend_printf(xendev, "state", "%u", state); 156 283 } 157 284 285 + enum xenbus_state xen_device_backend_get_state(XenDevice *xendev) 286 + { 287 + return xendev->backend_state; 288 + } 289 + 158 290 static void xen_device_backend_create(XenDevice *xendev, Error **errp) 159 291 { 160 292 XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); ··· 218 350 } 219 351 } 220 352 353 + static int xen_device_frontend_scanf(XenDevice *xendev, const char *key, 354 + const char *fmt, ...) 355 + { 356 + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 357 + va_list ap; 358 + int rc; 359 + 360 + g_assert(xenbus->xsh); 361 + 362 + va_start(ap, fmt); 363 + rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key, 364 + NULL, fmt, ap); 365 + va_end(ap); 366 + 367 + return rc; 368 + } 369 + 221 370 static void xen_device_frontend_set_state(XenDevice *xendev, 222 371 enum xenbus_state state) 223 372 { ··· 234 383 xen_device_frontend_printf(xendev, "state", "%u", state); 235 384 } 236 385 386 + static void xen_device_frontend_changed(void *opaque) 387 + { 388 + XenDevice *xendev = opaque; 389 + XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); 390 + const char *type = object_get_typename(OBJECT(xendev)); 391 + enum xenbus_state state; 392 + 393 + trace_xen_device_frontend_changed(type, xendev->name); 394 + 395 + if (xen_device_frontend_scanf(xendev, "state", "%u", &state) != 1) { 396 + state = XenbusStateUnknown; 397 + } 398 + 399 + xen_device_frontend_set_state(xendev, state); 400 + 401 + if (xendev_class->frontend_changed) { 402 + Error *local_err = NULL; 403 + 404 + xendev_class->frontend_changed(xendev, state, &local_err); 405 + 406 + if (local_err) { 407 + error_reportf_err(local_err, "frontend change error: "); 408 + } 409 + } 410 + 411 + /* 412 + * If a backend is still 'online' then its state should be cycled 413 + * back round to InitWait in order for a new frontend instance to 414 + * connect. This may happen when, for example, a frontend driver is 415 + * re-installed or updated. 416 + */ 417 + if (xendev->backend_state == XenbusStateClosed) { 418 + unsigned int online; 419 + 420 + if (xen_device_backend_scanf(xendev, "online", "%u", &online) != 1) { 421 + online = 0; 422 + } 423 + 424 + if (online) { 425 + xen_device_backend_set_state(xendev, XenbusStateInitWait); 426 + } 427 + } 428 + } 429 + 237 430 static void xen_device_frontend_create(XenDevice *xendev, Error **errp) 238 431 { 239 432 XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); ··· 254 447 if (local_err) { 255 448 error_propagate_prepend(errp, local_err, 256 449 "failed to create frontend: "); 450 + return; 451 + } 452 + 453 + xendev->frontend_state_watch = 454 + xen_bus_add_watch(xenbus, xendev->frontend_path, "state", 455 + xen_device_frontend_changed, xendev, &local_err); 456 + if (local_err) { 457 + error_propagate_prepend(errp, local_err, 458 + "failed to watch frontend state: "); 257 459 } 258 460 } 259 461 ··· 261 463 { 262 464 XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 263 465 Error *local_err = NULL; 466 + 467 + if (xendev->frontend_state_watch) { 468 + xen_bus_remove_watch(xenbus, xendev->frontend_state_watch, NULL); 469 + xendev->frontend_state_watch = NULL; 470 + } 264 471 265 472 if (!xendev->frontend_path) { 266 473 return;
+6
include/hw/xen/xen-bus-helper.h
··· 36 36 const char *node, const char *key, Error **errp, 37 37 const char *fmt, ...); 38 38 39 + /* Watch node/key unless node is empty, in which case watch key */ 40 + void xs_node_watch(struct xs_handle *xsh, const char *node, const char *key, 41 + char *token, Error **errp); 42 + void xs_node_unwatch(struct xs_handle *xsh, const char *node, const char *key, 43 + const char *token, Error **errp); 44 + 39 45 #endif /* HW_XEN_BUS_HELPER_H */
+15
include/hw/xen/xen-bus.h
··· 10 10 11 11 #include "hw/xen/xen_common.h" 12 12 #include "hw/sysbus.h" 13 + #include "qemu/notify.h" 14 + 15 + typedef void (*XenWatchHandler)(void *opaque); 16 + 17 + typedef struct XenWatch XenWatch; 13 18 14 19 typedef struct XenDevice { 15 20 DeviceState qdev; ··· 18 23 char *backend_path, *frontend_path; 19 24 enum xenbus_state backend_state, frontend_state; 20 25 Notifier exit; 26 + XenWatch *frontend_state_watch; 21 27 } XenDevice; 22 28 23 29 typedef char *(*XenDeviceGetName)(XenDevice *xendev, Error **errp); 24 30 typedef void (*XenDeviceRealize)(XenDevice *xendev, Error **errp); 31 + typedef void (*XenDeviceFrontendChanged)(XenDevice *xendev, 32 + enum xenbus_state frontend_state, 33 + Error **errp); 25 34 typedef void (*XenDeviceUnrealize)(XenDevice *xendev, Error **errp); 26 35 27 36 typedef struct XenDeviceClass { ··· 32 41 const char *device; 33 42 XenDeviceGetName get_name; 34 43 XenDeviceRealize realize; 44 + XenDeviceFrontendChanged frontend_changed; 35 45 XenDeviceUnrealize unrealize; 36 46 } XenDeviceClass; 37 47 ··· 47 57 BusState qbus; 48 58 domid_t backend_id; 49 59 struct xs_handle *xsh; 60 + NotifierList watch_notifiers; 50 61 } XenBus; 51 62 52 63 typedef struct XenBusClass { ··· 63 74 OBJECT_GET_CLASS(XenBusClass, (obj), TYPE_XEN_BUS) 64 75 65 76 void xen_bus_init(void); 77 + 78 + void xen_device_backend_set_state(XenDevice *xendev, 79 + enum xenbus_state state); 80 + enum xenbus_state xen_device_backend_get_state(XenDevice *xendev); 66 81 67 82 #endif /* HW_XEN_BUS_H */