summaryrefslogtreecommitdiffstats
path: root/sys/xen/xenbus/xenbusb.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/xen/xenbus/xenbusb.c')
-rw-r--r--sys/xen/xenbus/xenbusb.c111
1 files changed, 91 insertions, 20 deletions
diff --git a/sys/xen/xenbus/xenbusb.c b/sys/xen/xenbus/xenbusb.c
index 4bc86aa..cc519c5 100644
--- a/sys/xen/xenbus/xenbusb.c
+++ b/sys/xen/xenbus/xenbusb.c
@@ -90,10 +90,16 @@ xenbusb_free_child_ivars(struct xenbus_device_ivars *ivars)
ivars->xd_otherend_watch.node = NULL;
}
+ if (ivars->xd_local_watch.node != NULL) {
+ xs_unregister_watch(&ivars->xd_local_watch);
+ ivars->xd_local_watch.node = NULL;
+ }
+
if (ivars->xd_node != NULL) {
free(ivars->xd_node, M_XENBUS);
ivars->xd_node = NULL;
}
+ ivars->xd_node_len = 0;
if (ivars->xd_type != NULL) {
free(ivars->xd_type, M_XENBUS);
@@ -104,6 +110,7 @@ xenbusb_free_child_ivars(struct xenbus_device_ivars *ivars)
free(ivars->xd_otherend_path, M_XENBUS);
ivars->xd_otherend_path = NULL;
}
+ ivars->xd_otherend_path_len = 0;
free(ivars, M_XENBUS);
}
@@ -121,30 +128,64 @@ xenbusb_free_child_ivars(struct xenbus_device_ivars *ivars)
* watch event data. The vector should be indexed via the
* xs_watch_type enum in xs_wire.h.
* \param vec_size The number of elements in vec.
- *
- * \return The device_t of the found device if any, or NULL.
- *
- * \note device_t is a pointer type, so it can be compared against
- * NULL for validity.
*/
static void
-xenbusb_otherend_changed(struct xs_watch *watch, const char **vec,
+xenbusb_otherend_watch_cb(struct xs_watch *watch, const char **vec,
unsigned int vec_size __unused)
{
struct xenbus_device_ivars *ivars;
- device_t dev;
+ device_t child;
+ device_t bus;
+ const char *path;
enum xenbus_state newstate;
- ivars = (struct xenbus_device_ivars *) watch;
- dev = ivars->xd_dev;
+ ivars = (struct xenbus_device_ivars *)watch->callback_data;
+ child = ivars->xd_dev;
+ bus = device_get_parent(child);
- if (!ivars->xd_otherend_path
- || strncmp(ivars->xd_otherend_path, vec[XS_WATCH_PATH],
- strlen(ivars->xd_otherend_path)))
+ path = vec[XS_WATCH_PATH];
+ if (ivars->xd_otherend_path == NULL
+ || strncmp(ivars->xd_otherend_path, path, ivars->xd_otherend_path_len))
return;
newstate = xenbus_read_driver_state(ivars->xd_otherend_path);
- XENBUS_OTHEREND_CHANGED(dev, newstate);
+ XENBUSB_OTHEREND_CHANGED(bus, child, newstate);
+}
+
+/**
+ * XenBus watch callback registered against the XenStore sub-tree
+ * represnting the local half of a split device connection.
+ *
+ * This callback is invoked whenever any XenStore data in the subtree
+ * is modified, either by us or another privledged domain.
+ *
+ * \param watch The xs_watch object used to register this callback
+ * function.
+ * \param vec An array of pointers to NUL terminated strings containing
+ * watch event data. The vector should be indexed via the
+ * xs_watch_type enum in xs_wire.h.
+ * \param vec_size The number of elements in vec.
+ *
+ */
+static void
+xenbusb_local_watch_cb(struct xs_watch *watch, const char **vec,
+ unsigned int vec_size __unused)
+{
+ struct xenbus_device_ivars *ivars;
+ device_t child;
+ device_t bus;
+ const char *path;
+
+ ivars = (struct xenbus_device_ivars *)watch->callback_data;
+ child = ivars->xd_dev;
+ bus = device_get_parent(child);
+
+ path = vec[XS_WATCH_PATH];
+ if (ivars->xd_node == NULL
+ || strncmp(ivars->xd_node, path, ivars->xd_node_len))
+ return;
+
+ XENBUSB_LOCALEND_CHANGED(bus, child, &path[ivars->xd_node_len]);
}
/**
@@ -193,12 +234,14 @@ xenbusb_delete_child(device_t dev, device_t child)
/*
* We no longer care about the otherend of the
- * connection. Cancel the watch now so that we
+ * connection. Cancel the watches now so that we
* don't try to handle an event for a partially
* detached child.
*/
if (ivars->xd_otherend_watch.node != NULL)
xs_unregister_watch(&ivars->xd_otherend_watch);
+ if (ivars->xd_local_watch.node != NULL)
+ xs_unregister_watch(&ivars->xd_local_watch);
device_delete_child(dev, child);
xenbusb_free_child_ivars(ivars);
@@ -421,6 +464,7 @@ xenbusb_probe_children(device_t dev)
*/
ivars = device_get_ivars(kids[i]);
xs_register_watch(&ivars->xd_otherend_watch);
+ xs_register_watch(&ivars->xd_local_watch);
}
free(kids, M_TEMP);
}
@@ -475,7 +519,7 @@ xenbusb_devices_changed(struct xs_watch *watch, const char **vec,
char *p;
u_int component;
- xbs = (struct xenbusb_softc *)watch;
+ xbs = (struct xenbusb_softc *)watch->callback_data;
dev = xbs->xbs_dev;
if (len <= XS_WATCH_PATH) {
@@ -620,6 +664,7 @@ xenbusb_add_device(device_t dev, const char *type, const char *id)
sx_init(&ivars->xd_lock, "xdlock");
ivars->xd_flags = XDF_CONNECTING;
ivars->xd_node = strdup(devpath, M_XENBUS);
+ ivars->xd_node_len = strlen(devpath);
ivars->xd_type = strdup(type, M_XENBUS);
ivars->xd_state = XenbusStateInitialising;
@@ -630,12 +675,16 @@ xenbusb_add_device(device_t dev, const char *type, const char *id)
goto out;
}
- statepath = malloc(strlen(ivars->xd_otherend_path)
+ statepath = malloc(ivars->xd_otherend_path_len
+ strlen("/state") + 1, M_XENBUS, M_WAITOK);
sprintf(statepath, "%s/state", ivars->xd_otherend_path);
-
ivars->xd_otherend_watch.node = statepath;
- ivars->xd_otherend_watch.callback = xenbusb_otherend_changed;
+ ivars->xd_otherend_watch.callback = xenbusb_otherend_watch_cb;
+ ivars->xd_otherend_watch.callback_data = (uintptr_t)ivars;
+
+ ivars->xd_local_watch.node = ivars->xd_node;
+ ivars->xd_local_watch.callback = xenbusb_local_watch_cb;
+ ivars->xd_local_watch.callback_data = (uintptr_t)ivars;
mtx_lock(&xbs->xbs_lock);
xbs->xbs_connecting_children++;
@@ -693,6 +742,7 @@ xenbusb_attach(device_t dev, char *bus_node, u_int id_components)
xbs->xbs_device_watch.node = bus_node;
xbs->xbs_device_watch.callback = xenbusb_devices_changed;
+ xbs->xbs_device_watch.callback_data = (uintptr_t)xbs;
TASK_INIT(&xbs->xbs_probe_children, 0, xenbusb_probe_children_cb, dev);
@@ -735,7 +785,7 @@ xenbusb_resume(device_t dev)
DEVICE_RESUME(kids[i]);
- statepath = malloc(strlen(ivars->xd_otherend_path)
+ statepath = malloc(ivars->xd_otherend_path_len
+ strlen("/state") + 1, M_XENBUS, M_WAITOK);
sprintf(statepath, "%s/state", ivars->xd_otherend_path);
@@ -819,7 +869,7 @@ xenbusb_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
{
int error;
- newstate = (enum xenbus_state) value;
+ newstate = (enum xenbus_state)value;
sx_xlock(&ivars->xd_lock);
if (ivars->xd_state == newstate) {
error = 0;
@@ -876,3 +926,24 @@ xenbusb_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
return (ENOENT);
}
+
+void
+xenbusb_otherend_changed(device_t bus, device_t child, enum xenbus_state state)
+{
+ XENBUS_OTHEREND_CHANGED(child, state);
+}
+
+void
+xenbusb_localend_changed(device_t bus, device_t child, const char *path)
+{
+
+ if (strcmp(path, "/state") != 0) {
+ struct xenbus_device_ivars *ivars;
+
+ ivars = device_get_ivars(child);
+ sx_xlock(&ivars->xd_lock);
+ ivars->xd_state = xenbus_read_driver_state(ivars->xd_node);
+ sx_xunlock(&ivars->xd_lock);
+ }
+ XENBUS_LOCALEND_CHANGED(child, path);
+}
OpenPOWER on IntegriCloud