Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 24 May 2018 10:17:49 +0000 (UTC)
From:      =?UTF-8?Q?Roger_Pau_Monn=c3=a9?= <royger@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r334142 - head/sys/dev/xen/xenstore
Message-ID:  <201805241017.w4OAHn4W076692@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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@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",
 };
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201805241017.w4OAHn4W076692>