svn commit: r334142 - head/sys/dev/xen/xenstore
Roger Pau Monné
royger at FreeBSD.org
Thu May 24 10:17:50 UTC 2018
Author: royger
Date: Thu May 24 10:17:49 2018
New Revision: 334142
URL: https://svnweb.freebsd.org/changeset/base/334142
Log:
dev/xenstore: add support for watches
Allow user-space applications to register watches using the xenstore
device. This is needed in order to run toolstack operations on
domains different than the one where xenstore is running (in which
case the device is not used, since the connection to xenstore is done
using a plain socket).
Tested by: Nathan Friess <nathan.friess at gmail.com>
Sponsored by: Citrix Systems R&D
Modified:
head/sys/dev/xen/xenstore/xenstore_dev.c
Modified: head/sys/dev/xen/xenstore/xenstore_dev.c
==============================================================================
--- head/sys/dev/xen/xenstore/xenstore_dev.c Thu May 24 10:17:03 2018 (r334141)
+++ head/sys/dev/xen/xenstore/xenstore_dev.c Thu May 24 10:17:49 2018 (r334142)
@@ -44,6 +44,8 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/conf.h>
#include <sys/module.h>
+#include <sys/selinfo.h>
+#include <sys/poll.h>
#include <xen/xen-os.h>
@@ -56,10 +58,20 @@ struct xs_dev_transaction {
struct xs_transaction handle;
};
+struct xs_dev_watch {
+ LIST_ENTRY(xs_dev_watch) list;
+ struct xs_watch watch;
+ char *token;
+ struct xs_dev_data *user;
+};
+
struct xs_dev_data {
/* In-progress transaction. */
- LIST_HEAD(xdd_list_head, xs_dev_transaction) transactions;
+ LIST_HEAD(, xs_dev_transaction) transactions;
+ /* Active watches. */
+ LIST_HEAD(, xs_dev_watch) watches;
+
/* Partial request. */
unsigned int len;
union {
@@ -71,8 +83,137 @@ struct xs_dev_data {
#define MASK_READ_IDX(idx) ((idx)&(PAGE_SIZE-1))
char read_buffer[PAGE_SIZE];
unsigned int read_cons, read_prod;
+
+ /* Serializes writes to the read buffer. */
+ struct mtx lock;
+
+ /* Polling structure (for reads only ATM). */
+ struct selinfo ev_rsel;
};
+static void
+xs_queue_reply(struct xs_dev_data *u, const char *data, unsigned int len)
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++, u->read_prod++)
+ u->read_buffer[MASK_READ_IDX(u->read_prod)] = data[i];
+
+ KASSERT((u->read_prod - u->read_cons) <= sizeof(u->read_buffer),
+ ("xenstore reply too big"));
+
+ wakeup(u);
+ selwakeup(&u->ev_rsel);
+}
+
+static const char *
+xs_dev_error_to_string(int error)
+{
+ unsigned int i;
+
+ for (i = 0; i < nitems(xsd_errors); i++)
+ if (xsd_errors[i].errnum == error)
+ return (xsd_errors[i].errstring);
+
+ return (NULL);
+}
+
+static void
+xs_dev_return_error(struct xs_dev_data *u, int error, int req_id, int tx_id)
+{
+ struct xsd_sockmsg msg;
+ const char *payload;
+
+ msg.type = XS_ERROR;
+ msg.req_id = req_id;
+ msg.tx_id = tx_id;
+ payload = NULL;
+
+
+ payload = xs_dev_error_to_string(error);
+ if (payload == NULL)
+ payload = xs_dev_error_to_string(EINVAL);
+ KASSERT(payload != NULL, ("Unable to find string for EINVAL errno"));
+
+ msg.len = strlen(payload) + 1;
+
+ mtx_lock(&u->lock);
+ xs_queue_reply(u, (char *)&msg, sizeof(msg));
+ xs_queue_reply(u, payload, msg.len);
+ mtx_unlock(&u->lock);
+}
+
+static int
+xs_dev_watch_message_parse_string(const char **p, const char *end,
+ const char **string_r)
+{
+ const char *nul;
+
+ nul = memchr(*p, 0, end - *p);
+ if (!nul)
+ return (EINVAL);
+
+ *string_r = *p;
+ *p = nul+1;
+
+ return (0);
+}
+
+static int
+xs_dev_watch_message_parse(const struct xsd_sockmsg *msg, const char **path_r,
+ const char **token_r)
+{
+ const char *p, *end;
+ int error;
+
+ p = (const char *)msg + sizeof(*msg);
+ end = p + msg->len;
+ KASSERT(p <= end, ("payload overflow"));
+
+ error = xs_dev_watch_message_parse_string(&p, end, path_r);
+ if (error)
+ return (error);
+ error = xs_dev_watch_message_parse_string(&p, end, token_r);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+static struct xs_dev_watch *
+xs_dev_find_watch(struct xs_dev_data *u, const char *token)
+{
+ struct xs_dev_watch *watch;
+
+ LIST_FOREACH(watch, &u->watches, list)
+ if (strcmp(watch->token, token) == 0)
+ return (watch);
+
+ return (NULL);
+}
+
+static void
+xs_dev_watch_cb(struct xs_watch *watch, const char **vec, unsigned int len)
+{
+ struct xs_dev_watch *dwatch;
+ struct xsd_sockmsg msg;
+ char *payload;
+
+ dwatch = (struct xs_dev_watch *)watch->callback_data;
+ msg.type = XS_WATCH_EVENT;
+ msg.req_id = msg.tx_id = 0;
+ msg.len = strlen(vec[XS_WATCH_PATH]) + strlen(dwatch->token) + 2;
+
+ payload = malloc(msg.len, M_XENSTORE, M_WAITOK);
+ strcpy(payload, vec[XS_WATCH_PATH]);
+ strcpy(&payload[strlen(vec[XS_WATCH_PATH]) + 1], dwatch->token);
+ mtx_lock(&dwatch->user->lock);
+ xs_queue_reply(dwatch->user, (char *)&msg, sizeof(msg));
+ xs_queue_reply(dwatch->user, payload, msg.len);
+ mtx_unlock(&dwatch->user->lock);
+ free(payload, M_XENSTORE);
+}
+
static int
xs_dev_read(struct cdev *dev, struct uio *uio, int ioflag)
{
@@ -101,27 +242,16 @@ xs_dev_read(struct cdev *dev, struct uio *uio, int iof
return (0);
}
-static void
-xs_queue_reply(struct xs_dev_data *u, char *data, unsigned int len)
-{
- int i;
-
- for (i = 0; i < len; i++, u->read_prod++)
- u->read_buffer[MASK_READ_IDX(u->read_prod)] = data[i];
-
- KASSERT((u->read_prod - u->read_cons) <= sizeof(u->read_buffer),
- ("xenstore reply too big"));
-
- wakeup(u);
-}
-
static int
xs_dev_write(struct cdev *dev, struct uio *uio, int ioflag)
{
int error;
+ const char *wpath, *wtoken;
struct xs_dev_data *u;
struct xs_dev_transaction *trans;
+ struct xs_dev_watch *watch;
void *reply;
+ static const char *ok = "OK";
int len = uio->uio_resid;
error = devfs_get_cdevpriv((void **)&u);
@@ -168,35 +298,130 @@ xs_dev_write(struct cdev *dev, struct uio *uio, int io
LIST_REMOVE(trans, list);
free(trans, M_XENSTORE);
}
+ mtx_lock(&u->lock);
xs_queue_reply(u, (char *)&u->u.msg, sizeof(u->u.msg));
xs_queue_reply(u, (char *)reply, u->u.msg.len);
+ mtx_unlock(&u->lock);
free(reply, M_XENSTORE);
}
break;
+ case XS_WATCH:
+ u->u.msg.tx_id = 0;
+ error = xs_dev_watch_message_parse(&u->u.msg, &wpath, &wtoken);
+ if (error)
+ break;
+ if (xs_dev_find_watch(u, wtoken) != NULL) {
+ error = EINVAL;
+ break;
+ }
+ watch = malloc(sizeof(*watch), M_XENSTORE, M_WAITOK);
+ watch->watch.node = strdup(wpath, M_XENSTORE);
+ watch->watch.callback = xs_dev_watch_cb;
+ watch->watch.callback_data = (uintptr_t)watch;
+ watch->token = strdup(wtoken, M_XENSTORE);
+ watch->user = u;
+
+ error = xs_register_watch(&watch->watch);
+ if (error != 0) {
+ free(watch->token, M_XENSTORE);
+ free(watch->watch.node, M_XENSTORE);
+ free(watch, M_XENSTORE);
+ break;
+ }
+
+ LIST_INSERT_HEAD(&u->watches, watch, list);
+ u->u.msg.len = sizeof(ok);
+ mtx_lock(&u->lock);
+ xs_queue_reply(u, (char *)&u->u.msg, sizeof(u->u.msg));
+ xs_queue_reply(u, ok, sizeof(ok));
+ mtx_unlock(&u->lock);
+ break;
+ case XS_UNWATCH:
+ u->u.msg.tx_id = 0;
+ error = xs_dev_watch_message_parse(&u->u.msg, &wpath, &wtoken);
+ if (error)
+ break;
+ watch = xs_dev_find_watch(u, wtoken);
+ if (watch == NULL) {
+ error = EINVAL;
+ break;
+ }
+
+ LIST_REMOVE(watch, list);
+ xs_unregister_watch(&watch->watch);
+ free(watch->watch.node, M_XENSTORE);
+ free(watch->token, M_XENSTORE);
+ free(watch, M_XENSTORE);
+ u->u.msg.len = sizeof(ok);
+ mtx_lock(&u->lock);
+ xs_queue_reply(u, (char *)&u->u.msg, sizeof(u->u.msg));
+ xs_queue_reply(u, ok, sizeof(ok));
+ mtx_unlock(&u->lock);
+ break;
default:
error = EINVAL;
break;
}
- if (error == 0)
- u->len = 0;
+ if (error != 0)
+ xs_dev_return_error(u, error, u->u.msg.req_id, u->u.msg.tx_id);
- return (error);
+ /* Reset the write buffer. */
+ u->len = 0;
+
+ return (0);
}
+static int
+xs_dev_poll(struct cdev *dev, int events, struct thread *td)
+{
+ struct xs_dev_data *u;
+ int error, mask;
+
+ error = devfs_get_cdevpriv((void **)&u);
+ if (error != 0)
+ return (POLLERR);
+
+ /* we can always write */
+ mask = events & (POLLOUT | POLLWRNORM);
+
+ if (events & (POLLIN | POLLRDNORM)) {
+ if (u->read_cons != u->read_prod) {
+ mask |= events & (POLLIN | POLLRDNORM);
+ } else {
+ /* Record that someone is waiting */
+ selrecord(td, &u->ev_rsel);
+ }
+ }
+
+ return (mask);
+}
+
static void
xs_dev_dtor(void *arg)
{
struct xs_dev_data *u = arg;
- struct xs_dev_transaction *trans, *tmp;
+ struct xs_dev_transaction *trans, *tmpt;
+ struct xs_dev_watch *watch, *tmpw;
- LIST_FOREACH_SAFE(trans, &u->transactions, list, tmp) {
+ seldrain(&u->ev_rsel);
+
+ LIST_FOREACH_SAFE(trans, &u->transactions, list, tmpt) {
xs_transaction_end(trans->handle, 1);
LIST_REMOVE(trans, list);
free(trans, M_XENSTORE);
}
+ LIST_FOREACH_SAFE(watch, &u->watches, list, tmpw) {
+ LIST_REMOVE(watch, list);
+ xs_unregister_watch(&watch->watch);
+ free(watch->watch.node, M_XENSTORE);
+ free(watch->token, M_XENSTORE);
+ free(watch, M_XENSTORE);
+ }
+ mtx_destroy(&u->lock);
+
free(u, M_XENSTORE);
}
@@ -207,7 +432,9 @@ xs_dev_open(struct cdev *dev, int oflags, int devtype,
int error;
u = malloc(sizeof(*u), M_XENSTORE, M_WAITOK|M_ZERO);
+ mtx_init(&u->lock, "xsdev_lock", NULL, MTX_DEF);
LIST_INIT(&u->transactions);
+ LIST_INIT(&u->watches);
error = devfs_set_cdevpriv(u, xs_dev_dtor);
if (error != 0)
free(u, M_XENSTORE);
@@ -220,6 +447,7 @@ static struct cdevsw xs_dev_cdevsw = {
.d_read = xs_dev_read,
.d_write = xs_dev_write,
.d_open = xs_dev_open,
+ .d_poll = xs_dev_poll,
.d_name = "xs_dev",
};
More information about the svn-src-head
mailing list