Michael LeMay
2006-Aug-02 12:18 UTC
[Xen-devel] [PATCH][RFC] permit domU userspace to watch xenstore
Hello, This patch allows userspace tools on a domU to setup a watch on the xenstore. It does this by intercepting XS_WATCH requests written to /proc/xen/xenbus and then re-submitting the request to the in-kernel xenstore interface, in linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_xs.c. When a callback occurs, an in-kernel function is invoked, which then reconstructs a response in the format expected by userspace, and sends this response through /proc/xen/xenbus. It was necessary to add some supporting infrastructure to linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c, such as an additional mutex to protect the response queue and a list of active watches associated with each connection. Signed-off-by: Michael LeMay <mdlemay@epoch.ncsc.mil> --- linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c | 114 +++++++++++++++++++ 1 file changed, 114 insertions(+) diff -r eb8083d63198 -r 0c8a22ad7e46 linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c --- a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c Tue Aug 01 10:52:02 2006 -0400 +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c Wed Aug 02 08:20:06 2006 -0400 @@ -58,6 +58,9 @@ struct xenbus_dev_data { /* In-progress transaction. */ struct list_head transactions; + /* Active watches. */ + struct list_head watches; + /* Partial request. */ unsigned int len; union { @@ -70,6 +73,8 @@ struct xenbus_dev_data { char read_buffer[PAGE_SIZE]; unsigned int read_cons, read_prod; wait_queue_head_t read_waitq; + + struct mutex reply_mutex; }; static struct proc_dir_entry *xenbus_dev_intf; @@ -100,13 +105,59 @@ static void queue_reply(struct xenbus_de { int i; + mutex_lock(&u->reply_mutex); + for (i = 0; i < len; i++, u->read_prod++) u->read_buffer[MASK_READ_IDX(u->read_prod)] = data[i]; BUG_ON((u->read_prod - u->read_cons) > sizeof(u->read_buffer)); + mutex_unlock(&u->reply_mutex); + wake_up(&u->read_waitq); } + +struct watch_adapter +{ + struct list_head list; + struct xenbus_watch watch; + struct xenbus_dev_data *dev_data; + char *token; +}; + +static void free_watch_adapter (struct watch_adapter *watch) +{ + kfree(watch->watch.node); + kfree(watch->token); + kfree(watch); +} + +static void watch_fired(struct xenbus_watch *watch, + const char **vec, + unsigned int len) +{ + struct watch_adapter *adap + container_of(watch, struct watch_adapter, watch); + struct xsd_sockmsg hdr; + const char *path, *token; + int path_len, tok_len, body_len; + + path = vec[XS_WATCH_PATH]; + token = adap->token; + + path_len = strlen(path) + 1; + tok_len = strlen(token) + 1; + body_len = path_len + tok_len; + + hdr.type = XS_WATCH_EVENT; + hdr.len = body_len; + + queue_reply(adap->dev_data, (char *)&hdr, sizeof(hdr)); + queue_reply(adap->dev_data, (char *)path, path_len); + queue_reply(adap->dev_data, (char *)token, tok_len); +} + +static LIST_HEAD(watch_list); static ssize_t xenbus_dev_write(struct file *filp, const char __user *ubuf, @@ -116,6 +167,9 @@ static ssize_t xenbus_dev_write(struct f struct xenbus_dev_transaction *trans = NULL; uint32_t msg_type; void *reply; + char *path, *token; + struct watch_adapter *watch, *tmp_watch; + int err; if ((len + u->len) > sizeof(u->u.buffer)) return -EINVAL; @@ -169,6 +223,56 @@ static ssize_t xenbus_dev_write(struct f kfree(reply); break; + case XS_WATCH: + case XS_UNWATCH: + path = u->u.buffer + sizeof(u->u.msg); + token = memchr(path, 0, u->u.msg.len); + if (token == NULL) + return -EILSEQ; + token++; + + if (msg_type == XS_WATCH) { + static const char * XS_WATCH_RESP = "OK"; + struct xsd_sockmsg hdr; + + watch = kmalloc(sizeof(*watch), GFP_KERNEL); + watch->watch.node = kmalloc(strlen(path)+1, + GFP_KERNEL); + strcpy((char *)watch->watch.node, path); + watch->watch.callback = watch_fired; + watch->token = kmalloc(strlen(token)+1, GFP_KERNEL); + strcpy(watch->token, token); + watch->dev_data = u; + + err = register_xenbus_watch(&watch->watch); + if (err) { + free_watch_adapter(watch); + return err; + } + + list_add(&watch->list, &u->watches); + + hdr.type = XS_WATCH; + hdr.len = strlen(XS_WATCH_RESP) + 1; + queue_reply(u, (char *)&hdr, sizeof(hdr)); + queue_reply(u, (char *)XS_WATCH_RESP, hdr.len); + } else { + list_for_each_entry_safe(watch, tmp_watch, + &u->watches, list) { + if (!strcmp(watch->token, token) && + !strcmp(watch->watch.node, path)) + break; + { + unregister_xenbus_watch(&watch->watch); + list_del(&watch->list); + free_watch_adapter(watch); + break; + } + } + } + + break; + default: return -EINVAL; } @@ -191,7 +295,10 @@ static int xenbus_dev_open(struct inode return -ENOMEM; INIT_LIST_HEAD(&u->transactions); + INIT_LIST_HEAD(&u->watches); init_waitqueue_head(&u->read_waitq); + + mutex_init(&u->reply_mutex); filp->private_data = u; @@ -202,11 +309,18 @@ static int xenbus_dev_release(struct ino { struct xenbus_dev_data *u = filp->private_data; struct xenbus_dev_transaction *trans, *tmp; + struct watch_adapter *watch, *tmp_watch; list_for_each_entry_safe(trans, tmp, &u->transactions, list) { xenbus_transaction_end(trans->handle, 1); list_del(&trans->list); kfree(trans); + } + + list_for_each_entry_safe(watch, tmp_watch, &u->watches, list) { + unregister_xenbus_watch(&watch->watch); + list_del(&watch->list); + free_watch_adapter(watch); } kfree(u); _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Cihula, Joseph
2006-Aug-04 20:48 UTC
RE: [Xen-devel] [PATCH][RFC] permit domU userspace to watch xenstore
Michael, There was some discussion on this in January (http://lists.xensource.com/archives/cgi-bin/extract-mesg.cgi?a=xen-deve l&m=2006-01&i=5901fecb838e65c42d197d246ec28e47%40cl.cam.ac.uk). At that time I looked at both this approach and pushing the watches to xenstore (which currently only knows about per-domain watches and not intra-domain, except for dom0). My thinking was that doing the multiplexing in the xenstore kernel module would add complexity and fragility (you need to worry about apps DoS''ing the kernel by adding too many watches, etc.). So I proposed using the connection id as an intra-domain id that xenstore would recognize and manage like it managed domain watches. This would have the side-effect of allowing the removal of the current kernel module multiplexing, since they could also use this field. I realize that working code is worth more than a proposal, but perhaps you could see if the advantages of this approach might be worth a little re-coding. Joe On Wednesday, August 02, 2006 5:18 AM, Michael LeMay <> wrote:> Hello, > > This patch allows userspace tools on a domU to setup a watch on > the xenstore. It does this by intercepting XS_WATCH requests written > to /proc/xen/xenbus and then re-submitting the request to the > in-kernel > xenstore interface, in > linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_xs.c. When a callback > occurs, an in-kernel function is invoked, which then reconstructs a > response in the format expected by userspace, and sends this response > through /proc/xen/xenbus. > > It was necessary to add some supporting infrastructure to > linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c, such as an > additional mutex to protect the response queue and a list of active > watches associated with each connection. > > > Signed-off-by: Michael LeMay <mdlemay@epoch.ncsc.mil> > > --- > > linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c | 114 > +++++++++++++++++++ 1 file changed, 114 insertions(+) > > diff -r eb8083d63198 -r 0c8a22ad7e46 > linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c --- > a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c Tue Aug01> 10:52:02 2006 -0400 +++ > b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c Wed Aug02> 08:20:06 2006 -0400 @@ -58,6 +58,9 @@ struct xenbus_dev_data {/*> In-progress transaction. */ struct list_head transactions; > > + /* Active watches. */ > + struct list_head watches; > + > /* Partial request. */ > unsigned int len; > union { > @@ -70,6 +73,8 @@ struct xenbus_dev_data { > char read_buffer[PAGE_SIZE]; > unsigned int read_cons, read_prod; > wait_queue_head_t read_waitq; > + > + struct mutex reply_mutex; > }; > > static struct proc_dir_entry *xenbus_dev_intf; > @@ -100,13 +105,59 @@ static void queue_reply(struct xenbus_de > { > int i; > > + mutex_lock(&u->reply_mutex); > + > for (i = 0; i < len; i++, u->read_prod++) > u->read_buffer[MASK_READ_IDX(u->read_prod)] = data[i]; > > BUG_ON((u->read_prod - u->read_cons) > sizeof(u->read_buffer)); > > + mutex_unlock(&u->reply_mutex); > + > wake_up(&u->read_waitq); > } > + > +struct watch_adapter > +{ > + struct list_head list; > + struct xenbus_watch watch; > + struct xenbus_dev_data *dev_data; > + char *token; > +}; > + > +static void free_watch_adapter (struct watch_adapter *watch) > +{ > + kfree(watch->watch.node); > + kfree(watch->token); > + kfree(watch); > +} > + > +static void watch_fired(struct xenbus_watch *watch, > + const char **vec, > + unsigned int len) > +{ > + struct watch_adapter *adap > + container_of(watch, struct watch_adapter, watch); > + struct xsd_sockmsg hdr; > + const char *path, *token; > + int path_len, tok_len, body_len; > + > + path = vec[XS_WATCH_PATH]; > + token = adap->token; > + > + path_len = strlen(path) + 1; > + tok_len = strlen(token) + 1; > + body_len = path_len + tok_len; > + > + hdr.type = XS_WATCH_EVENT; > + hdr.len = body_len; > + > + queue_reply(adap->dev_data, (char *)&hdr, sizeof(hdr)); > + queue_reply(adap->dev_data, (char *)path, path_len); > + queue_reply(adap->dev_data, (char *)token, tok_len); > +} > + > +static LIST_HEAD(watch_list); > > static ssize_t xenbus_dev_write(struct file *filp, > const char __user *ubuf, > @@ -116,6 +167,9 @@ static ssize_t xenbus_dev_write(struct f > struct xenbus_dev_transaction *trans = NULL; > uint32_t msg_type; > void *reply; > + char *path, *token; > + struct watch_adapter *watch, *tmp_watch; > + int err; > > if ((len + u->len) > sizeof(u->u.buffer)) > return -EINVAL; > @@ -169,6 +223,56 @@ static ssize_t xenbus_dev_write(struct f > kfree(reply); > break; > > + case XS_WATCH: > + case XS_UNWATCH: > + path = u->u.buffer + sizeof(u->u.msg); > + token = memchr(path, 0, u->u.msg.len); > + if (token == NULL) > + return -EILSEQ; > + token++; > + > + if (msg_type == XS_WATCH) { > + static const char * XS_WATCH_RESP = "OK"; > + struct xsd_sockmsg hdr; > + > + watch = kmalloc(sizeof(*watch), GFP_KERNEL); > + watch->watch.node = kmalloc(strlen(path)+1, > + GFP_KERNEL); > + strcpy((char *)watch->watch.node, path); > + watch->watch.callback = watch_fired; > + watch->token = kmalloc(strlen(token)+1,GFP_KERNEL);> + strcpy(watch->token, token); > + watch->dev_data = u; > + > + err = register_xenbus_watch(&watch->watch); > + if (err) { > + free_watch_adapter(watch); > + return err; > + } > + > + list_add(&watch->list, &u->watches); > + > + hdr.type = XS_WATCH; > + hdr.len = strlen(XS_WATCH_RESP) + 1; > + queue_reply(u, (char *)&hdr, sizeof(hdr)); > + queue_reply(u, (char *)XS_WATCH_RESP, hdr.len); > + } else { > + list_for_each_entry_safe(watch, tmp_watch, > + &u->watches, list) { > + if (!strcmp(watch->token, token) && > + !strcmp(watch->watch.node, path)) > + break; > + { > +unregister_xenbus_watch(&watch->watch);> + list_del(&watch->list); > + free_watch_adapter(watch); > + break; > + } > + } > + } > + > + break; > + > default: > return -EINVAL; > } > @@ -191,7 +295,10 @@ static int xenbus_dev_open(struct inode > return -ENOMEM; > > INIT_LIST_HEAD(&u->transactions); > + INIT_LIST_HEAD(&u->watches); > init_waitqueue_head(&u->read_waitq); > + > + mutex_init(&u->reply_mutex); > > filp->private_data = u; > > @@ -202,11 +309,18 @@ static int xenbus_dev_release(struct ino > { > struct xenbus_dev_data *u = filp->private_data; > struct xenbus_dev_transaction *trans, *tmp; > + struct watch_adapter *watch, *tmp_watch; > > list_for_each_entry_safe(trans, tmp, &u->transactions, list) { > xenbus_transaction_end(trans->handle, 1); > list_del(&trans->list); > kfree(trans); > + } > + > + list_for_each_entry_safe(watch, tmp_watch, &u->watches, list) { > + unregister_xenbus_watch(&watch->watch); > + list_del(&watch->list); > + free_watch_adapter(watch); > } > > kfree(u); > > > > _______________________________________________ > Xen-devel mailing list > Xen-devel@lists.xensource.com > http://lists.xensource.com/xen-devel_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Michael LeMay
2006-Aug-08 12:25 UTC
RE: [Xen-devel] [PATCH][RFC] permit domU userspace to watch xenstore
On Fri, 2006-08-04 at 13:48 -0700, Cihula, Joseph wrote:> Michael,...> I realize that working code is worth more than a proposal, but perhaps > you could see if the advantages of this approach might be worth a little > re-coding. >Thanks for the suggestions, I''ll see what I can do to fit them in.> Joe > > On Wednesday, August 02, 2006 5:18 AM, Michael LeMay <> wrote: > > > Hello, > > > > This patch allows userspace tools on a domU to setup a watch on > > the xenstore. It does this by intercepting XS_WATCH requests written > > to /proc/xen/xenbus and then re-submitting the request to the > > in-kernel > > xenstore interface, in > > linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_xs.c. When a callback > > occurs, an in-kernel function is invoked, which then reconstructs a > > response in the format expected by userspace, and sends this response > > through /proc/xen/xenbus. > > > > It was necessary to add some supporting infrastructure to > > linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c, such as an > > additional mutex to protect the response queue and a list of active > > watches associated with each connection. > > > > > > Signed-off-by: Michael LeMay <mdlemay@epoch.ncsc.mil> > > > > --- > > > > linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c | 114 > > +++++++++++++++++++ 1 file changed, 114 insertions(+) > > > > diff -r eb8083d63198 -r 0c8a22ad7e46 > > linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c --- > > a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c Tue Aug > 01 > > 10:52:02 2006 -0400 +++ > > b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_dev.c Wed Aug > 02 > > 08:20:06 2006 -0400 @@ -58,6 +58,9 @@ struct xenbus_dev_data { > /* > > In-progress transaction. */ struct list_head transactions; > > > > + /* Active watches. */ > > + struct list_head watches; > > + > > /* Partial request. */ > > unsigned int len; > > union { > > @@ -70,6 +73,8 @@ struct xenbus_dev_data { > > char read_buffer[PAGE_SIZE]; > > unsigned int read_cons, read_prod; > > wait_queue_head_t read_waitq; > > + > > + struct mutex reply_mutex; > > }; > > > > static struct proc_dir_entry *xenbus_dev_intf; > > @@ -100,13 +105,59 @@ static void queue_reply(struct xenbus_de > > { > > int i; > > > > + mutex_lock(&u->reply_mutex); > > + > > for (i = 0; i < len; i++, u->read_prod++) > > u->read_buffer[MASK_READ_IDX(u->read_prod)] = data[i]; > > > > BUG_ON((u->read_prod - u->read_cons) > sizeof(u->read_buffer)); > > > > + mutex_unlock(&u->reply_mutex); > > + > > wake_up(&u->read_waitq); > > } > > + > > +struct watch_adapter > > +{ > > + struct list_head list; > > + struct xenbus_watch watch; > > + struct xenbus_dev_data *dev_data; > > + char *token; > > +}; > > + > > +static void free_watch_adapter (struct watch_adapter *watch) > > +{ > > + kfree(watch->watch.node); > > + kfree(watch->token); > > + kfree(watch); > > +} > > + > > +static void watch_fired(struct xenbus_watch *watch, > > + const char **vec, > > + unsigned int len) > > +{ > > + struct watch_adapter *adap > > + container_of(watch, struct watch_adapter, watch); > > + struct xsd_sockmsg hdr; > > + const char *path, *token; > > + int path_len, tok_len, body_len; > > + > > + path = vec[XS_WATCH_PATH]; > > + token = adap->token; > > + > > + path_len = strlen(path) + 1; > > + tok_len = strlen(token) + 1; > > + body_len = path_len + tok_len; > > + > > + hdr.type = XS_WATCH_EVENT; > > + hdr.len = body_len; > > + > > + queue_reply(adap->dev_data, (char *)&hdr, sizeof(hdr)); > > + queue_reply(adap->dev_data, (char *)path, path_len); > > + queue_reply(adap->dev_data, (char *)token, tok_len); > > +} > > + > > +static LIST_HEAD(watch_list); > > > > static ssize_t xenbus_dev_write(struct file *filp, > > const char __user *ubuf, > > @@ -116,6 +167,9 @@ static ssize_t xenbus_dev_write(struct f > > struct xenbus_dev_transaction *trans = NULL; > > uint32_t msg_type; > > void *reply; > > + char *path, *token; > > + struct watch_adapter *watch, *tmp_watch; > > + int err; > > > > if ((len + u->len) > sizeof(u->u.buffer)) > > return -EINVAL; > > @@ -169,6 +223,56 @@ static ssize_t xenbus_dev_write(struct f > > kfree(reply); > > break; > > > > + case XS_WATCH: > > + case XS_UNWATCH: > > + path = u->u.buffer + sizeof(u->u.msg); > > + token = memchr(path, 0, u->u.msg.len); > > + if (token == NULL) > > + return -EILSEQ; > > + token++; > > + > > + if (msg_type == XS_WATCH) { > > + static const char * XS_WATCH_RESP = "OK"; > > + struct xsd_sockmsg hdr; > > + > > + watch = kmalloc(sizeof(*watch), GFP_KERNEL); > > + watch->watch.node = kmalloc(strlen(path)+1, > > + GFP_KERNEL); > > + strcpy((char *)watch->watch.node, path); > > + watch->watch.callback = watch_fired; > > + watch->token = kmalloc(strlen(token)+1, > GFP_KERNEL); > > + strcpy(watch->token, token); > > + watch->dev_data = u; > > + > > + err = register_xenbus_watch(&watch->watch); > > + if (err) { > > + free_watch_adapter(watch); > > + return err; > > + } > > + > > + list_add(&watch->list, &u->watches); > > + > > + hdr.type = XS_WATCH; > > + hdr.len = strlen(XS_WATCH_RESP) + 1; > > + queue_reply(u, (char *)&hdr, sizeof(hdr)); > > + queue_reply(u, (char *)XS_WATCH_RESP, hdr.len); > > + } else { > > + list_for_each_entry_safe(watch, tmp_watch, > > + &u->watches, list) { > > + if (!strcmp(watch->token, token) && > > + !strcmp(watch->watch.node, path)) > > + break; > > + { > > + > unregister_xenbus_watch(&watch->watch); > > + list_del(&watch->list); > > + free_watch_adapter(watch); > > + break; > > + } > > + } > > + } > > + > > + break; > > + > > default: > > return -EINVAL; > > } > > @@ -191,7 +295,10 @@ static int xenbus_dev_open(struct inode > > return -ENOMEM; > > > > INIT_LIST_HEAD(&u->transactions); > > + INIT_LIST_HEAD(&u->watches); > > init_waitqueue_head(&u->read_waitq); > > + > > + mutex_init(&u->reply_mutex); > > > > filp->private_data = u; > > > > @@ -202,11 +309,18 @@ static int xenbus_dev_release(struct ino > > { > > struct xenbus_dev_data *u = filp->private_data; > > struct xenbus_dev_transaction *trans, *tmp; > > + struct watch_adapter *watch, *tmp_watch; > > > > list_for_each_entry_safe(trans, tmp, &u->transactions, list) { > > xenbus_transaction_end(trans->handle, 1); > > list_del(&trans->list); > > kfree(trans); > > + } > > + > > + list_for_each_entry_safe(watch, tmp_watch, &u->watches, list) { > > + unregister_xenbus_watch(&watch->watch); > > + list_del(&watch->list); > > + free_watch_adapter(watch); > > } > > > > kfree(u); > > > > > > > > _______________________________________________ > > Xen-devel mailing list > > Xen-devel@lists.xensource.com > > http://lists.xensource.com/xen-devel_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel