summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormikeb <mikeb@openbsd.org>2016-12-19 21:07:10 +0000
committermikeb <mikeb@openbsd.org>2016-12-19 21:07:10 +0000
commit2e3b9c726e0d70a492d48efe8f0b3d9f8bec48bb (patch)
treea09213ba47ddd46d6d98c9090be4e6804474dc30
parentConvert the gcc 3 scheduling information to the gcc 4 model. (diff)
downloadwireguard-openbsd-2e3b9c726e0d70a492d48efe8f0b3d9f8bec48bb.tar.xz
wireguard-openbsd-2e3b9c726e0d70a492d48efe8f0b3d9f8bec48bb.zip
Add experimental support for device hot-plugging
We're installing watches on all nodes under "device/" and re-scan the subtree every time the watch is triggered looking for changes in the output. Tested with xnf(4) and xbf(4), helpful hints from Roger Pau Monne, thanks!
-rw-r--r--sys/dev/pv/xen.c174
-rw-r--r--sys/dev/pv/xenvar.h27
2 files changed, 171 insertions, 30 deletions
diff --git a/sys/dev/pv/xen.c b/sys/dev/pv/xen.c
index f6ae8d3df13..fcf7ab44ae7 100644
--- a/sys/dev/pv/xen.c
+++ b/sys/dev/pv/xen.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: xen.c,v 1.68 2016/12/09 17:29:48 mikeb Exp $ */
+/* $OpenBSD: xen.c,v 1.69 2016/12/19 21:07:10 mikeb Exp $ */
/*
* Copyright (c) 2015 Mike Belopuhov
@@ -35,6 +35,7 @@
#include <sys/signalvar.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
+#include <sys/stdint.h>
#include <sys/device.h>
#include <sys/task.h>
#include <sys/syslog.h>
@@ -75,8 +76,11 @@ int xen_match(struct device *, void *, void *);
void xen_attach(struct device *, struct device *, void *);
void xen_deferred(struct device *);
void xen_control(void *);
+void xen_hotplug(void *);
void xen_resume(struct device *);
int xen_activate(struct device *, int);
+int xen_attach_device(struct xen_softc *, struct xen_devlist *,
+ const char *, const char *);
int xen_probe_devices(struct xen_softc *);
int xen_bus_dmamap_create(bus_dma_tag_t, bus_size_t, int, bus_size_t,
@@ -1253,13 +1257,51 @@ xen_attach_print(void *aux, const char *name)
}
int
-xen_probe_devices(struct xen_softc *sc)
+xen_attach_device(struct xen_softc *sc, struct xen_devlist *xdl,
+ const char *name, const char *unit)
{
struct xen_attach_args xa;
+ struct xen_device *xdv;
+ unsigned long long res;
+
+ xa.xa_dmat = &xen_bus_dma_tag;
+
+ strlcpy(xa.xa_name, name, sizeof(xa.xa_name));
+ snprintf(xa.xa_node, sizeof(xa.xa_node), "device/%s/%s", name, unit);
+
+ if (xs_getprop(sc, xa.xa_node, "backend", xa.xa_backend,
+ sizeof(xa.xa_backend))) {
+ DPRINTF("%s: failed to identify \"backend\" for "
+ "\"%s\"\n", sc->sc_dev.dv_xname, xa.xa_node);
+ return (EIO);
+ }
+
+ if (xs_getnum(sc, xa.xa_node, "backend-id", &res) || res > UINT16_MAX) {
+ DPRINTF("%s: invalid \"backend-id\" for \"%s\"\n",
+ sc->sc_dev.dv_xname, xa.xa_node);
+ return (EIO);
+ }
+ xa.xa_domid = (uint16_t)res;
+
+ xdv = malloc(sizeof(struct xen_device), M_DEVBUF, M_ZERO | M_NOWAIT);
+ if (xdv == NULL)
+ return (ENOMEM);
+
+ strlcpy(xdv->dv_unit, unit, sizeof(xdv->dv_unit));
+ LIST_INSERT_HEAD(&xdl->dl_devs, xdv, dv_entry);
+
+ xdv->dv_dev = config_found((struct device *)sc, &xa, xen_attach_print);
+
+ return (0);
+}
+
+int
+xen_probe_devices(struct xen_softc *sc)
+{
+ struct xen_devlist *xdl;
struct xs_transaction xst;
struct iovec *iovp1 = NULL, *iovp2 = NULL;
int i, j, error, iov1_cnt = 0, iov2_cnt = 0;
- unsigned long long res;
char path[256];
memset(&xst, 0, sizeof(xst));
@@ -1267,6 +1309,10 @@ xen_probe_devices(struct xen_softc *sc)
xst.xst_cookie = sc->sc_xs;
xst.xst_flags |= XST_POLL;
+ rw_init(&sc->sc_devlck, "xenprobe");
+
+ rw_enter_write(&sc->sc_devlck);
+
if ((error = xs_cmd(&xst, XS_LIST, "device", &iovp1, &iov1_cnt)) != 0)
return (error);
@@ -1276,36 +1322,112 @@ xen_probe_devices(struct xen_softc *sc)
snprintf(path, sizeof(path), "device/%s",
(char *)iovp1[i].iov_base);
if ((error = xs_cmd(&xst, XS_LIST, path, &iovp2,
- &iov2_cnt)) != 0) {
- xs_resfree(&xst, iovp1, iov1_cnt);
- return (error);
+ &iov2_cnt)) != 0)
+ goto out;
+ if ((xdl = malloc(sizeof(struct xen_devlist), M_DEVBUF,
+ M_ZERO | M_NOWAIT)) == NULL) {
+ error = ENOMEM;
+ goto out;
}
+ xdl->dl_xen = sc;
+ strlcpy(xdl->dl_node, (const char *)iovp1[i].iov_base,
+ XEN_MAX_NODE_LEN);
for (j = 0; j < iov2_cnt; j++) {
- xa.xa_dmat = &xen_bus_dma_tag;
- strlcpy(xa.xa_name, (char *)iovp1[i].iov_base,
- sizeof(xa.xa_name));
- snprintf(xa.xa_node, sizeof(xa.xa_node), "device/%s/%s",
- (char *)iovp1[i].iov_base,
- (char *)iovp2[j].iov_base);
- if (xs_getprop(sc, xa.xa_node, "backend", xa.xa_backend,
- sizeof(xa.xa_backend))) {
- printf("%s: failed to identify \"backend\" for "
- "\"%s\"\n", sc->sc_dev.dv_xname, xa.xa_node);
- return (ENODEV);
- }
- if (xs_getnum(sc, xa.xa_node, "backend-id", &res) ||
- res > 0xffffULL) {
- printf("%s: invalid \"backend-id\" for \"%s\"\n",
- sc->sc_dev.dv_xname, xa.xa_node);
- return (ENODEV);
+ error = xen_attach_device(sc, xdl,
+ (const char *)iovp1[i].iov_base,
+ (const char *)iovp2[j].iov_base);
+ if (error) {
+ printf("%s: failed to attach \"%s/%s\"\n",
+ sc->sc_dev.dv_xname, path,
+ (const char *)iovp2[j].iov_base);
+ goto out;
}
- xa.xa_domid = (uint16_t)res;
- config_found((struct device *)sc, &xa, xen_attach_print);
}
+ /* Setup a watch for every device subtree */
+ if (xs_watch(sc, "device", (char *)iovp1[i].iov_base,
+ &xdl->dl_task, xen_hotplug, xdl))
+ printf("%s: failed to setup hotplug watch for \"%s\"\n",
+ sc->sc_dev.dv_xname, (char *)iovp1[i].iov_base);
+ SLIST_INSERT_HEAD(&sc->sc_devlists, xdl, dl_entry);
xs_resfree(&xst, iovp2, iov2_cnt);
+ iovp2 = NULL;
+ iov2_cnt = 0;
}
- return (0);
+ out:
+ rw_exit_write(&sc->sc_devlck);
+
+ if (iovp2)
+ xs_resfree(&xst, iovp2, iov2_cnt);
+ xs_resfree(&xst, iovp1, iov1_cnt);
+ return (error);
+}
+
+void
+xen_hotplug(void *arg)
+{
+ struct xen_devlist *xdl = arg;
+ struct xen_softc *sc = xdl->dl_xen;
+ struct xen_device *xdv, *xvdn;
+ struct xs_transaction xst;
+ struct iovec *iovp = NULL;
+ int error, i, keep, iov_cnt = 0;
+ char path[256];
+ int8_t *seen;
+
+ memset(&xst, 0, sizeof(xst));
+ xst.xst_id = 0;
+ xst.xst_cookie = sc->sc_xs;
+
+ rw_enter_write(&sc->sc_devlck);
+
+ snprintf(path, sizeof(path), "device/%s", xdl->dl_node);
+ if ((error = xs_cmd(&xst, XS_LIST, path, &iovp, &iov_cnt)) != 0)
+ return;
+
+ seen = malloc(iov_cnt, M_TEMP, M_ZERO | M_WAITOK);
+
+ /* Detect all removed and kept devices */
+ LIST_FOREACH_SAFE(xdv, &xdl->dl_devs, dv_entry, xvdn) {
+ for (i = 0, keep = 0; i < iov_cnt; i++) {
+ if (seen[i])
+ continue;
+ if (!strcmp(xdv->dv_unit, (char *)iovp[i].iov_base)) {
+ seen[i]++;
+ keep++;
+ break;
+ }
+ }
+ if (!keep) {
+ DPRINTF("%s: removing \"%s/%s\"\n", sc->sc_dev.dv_xname,
+ xdl->dl_node, xdv->dv_unit);
+ LIST_REMOVE(xdv, dv_entry);
+ config_detach(xdv->dv_dev, 0);
+ free(xdv, M_DEVBUF, sizeof(struct xen_device));
+ }
+ }
+
+ /* Attach all new devices */
+ for (i = 0; i < iov_cnt; i++) {
+ if (seen[i])
+ continue;
+ DPRINTF("%s: attaching \"%s/%s\"\n", sc->sc_dev.dv_xname,
+ xdl->dl_node, (const char *)iovp[i].iov_base);
+ error = xen_attach_device(sc, xdl, xdl->dl_node,
+ (const char *)iovp[i].iov_base);
+ if (error) {
+ printf("%s: failed to attach \"%s/%s\"\n",
+ sc->sc_dev.dv_xname, path,
+ (const char *)iovp[i].iov_base);
+ continue;
+ }
+ }
+
+ rw_exit_write(&sc->sc_devlck);
+
+ free(seen, M_TEMP, iov_cnt);
+
+ xs_resfree(&xst, iovp, iov_cnt);
}
#include <machine/pio.h>
diff --git a/sys/dev/pv/xenvar.h b/sys/dev/pv/xenvar.h
index 5c97d142b7f..f0eae5636a2 100644
--- a/sys/dev/pv/xenvar.h
+++ b/sys/dev/pv/xenvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: xenvar.h,v 1.43 2016/12/09 17:24:55 mikeb Exp $ */
+/* $OpenBSD: xenvar.h,v 1.44 2016/12/19 21:07:10 mikeb Exp $ */
/*
* Copyright (c) 2015 Mike Belopuhov
@@ -45,6 +45,9 @@ test_bit(u_int b, volatile void *p)
return !!(((volatile u_int *)p)[b >> 5] & (1 << (b & 0x1f)));
}
+#define XEN_MAX_NODE_LEN 64
+#define XEN_MAX_BACKEND_LEN 128
+
struct xen_intsrc {
SLIST_ENTRY(xen_intsrc) xi_entry;
struct evcount xi_evcnt;
@@ -69,6 +72,22 @@ struct xen_gntmap {
paddr_t gm_paddr;
};
+struct xen_device {
+ struct device *dv_dev;
+ char dv_unit[16];
+ LIST_ENTRY(xen_device) dv_entry;
+};
+LIST_HEAD(xen_devices, xen_device);
+
+struct xen_devlist {
+ struct xen_softc *dl_xen;
+ char dl_node[XEN_MAX_NODE_LEN];
+ struct task dl_task;
+ struct xen_devices dl_devs;
+ SLIST_ENTRY(xen_devlist) dl_entry;
+};
+SLIST_HEAD(xen_devlists, xen_devlist);
+
struct xen_softc {
struct device sc_dev;
uint32_t sc_base;
@@ -97,13 +116,13 @@ struct xen_softc {
struct xs_softc *sc_xs; /* xenstore softc */
struct task sc_ctltsk; /* control task */
+
+ struct xen_devlists sc_devlists; /* device lists heads */
+ struct rwlock sc_devlck;
};
extern struct xen_softc *xen_sc;
-#define XEN_MAX_NODE_LEN 64
-#define XEN_MAX_BACKEND_LEN 128
-
struct xen_attach_args {
char xa_name[16];
char xa_node[XEN_MAX_NODE_LEN];