From owner-svn-src-all@freebsd.org Thu May 24 10:17:50 2018 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id F02AAEAAE0E; Thu, 24 May 2018 10:17:49 +0000 (UTC) (envelope-from royger@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id A0C0881D7B; Thu, 24 May 2018 10:17:49 +0000 (UTC) (envelope-from royger@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 8353F10E73; Thu, 24 May 2018 10:17:49 +0000 (UTC) (envelope-from royger@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id w4OAHn12076693; Thu, 24 May 2018 10:17:49 GMT (envelope-from royger@FreeBSD.org) Received: (from royger@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id w4OAHn4W076692; Thu, 24 May 2018 10:17:49 GMT (envelope-from royger@FreeBSD.org) Message-Id: <201805241017.w4OAHn4W076692@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: royger set sender to royger@FreeBSD.org using -f From: =?UTF-8?Q?Roger_Pau_Monn=c3=a9?= Date: Thu, 24 May 2018 10:17:49 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r334142 - head/sys/dev/xen/xenstore X-SVN-Group: head X-SVN-Commit-Author: royger X-SVN-Commit-Paths: head/sys/dev/xen/xenstore X-SVN-Commit-Revision: 334142 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.26 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 24 May 2018 10:17:50 -0000 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 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 #include #include +#include +#include #include @@ -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", };