|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH 4/5] xen: Add xenbus device driver
On Sun, Nov 27, 2011 at 11:07:07PM +0100, Bastian Blank wrote:
> Access to xenbus is currently handled via xenfs. This adds a device
> driver for xenbus and makes xenfs use this code.
>
> Signed-off-by: Bastian Blank <waldi@xxxxxxxxxx>
> ---
> drivers/xen/xenbus/Makefile | 1 +
> drivers/xen/xenbus/xenbus_comms.h | 4 +
> drivers/xen/xenbus/xenbus_dev_frontend.c | 624
> ++++++++++++++++++++++++++++++
> drivers/xen/xenfs/Makefile | 2 +-
> drivers/xen/xenfs/super.c | 3 +-
> drivers/xen/xenfs/xenbus.c | 593 ----------------------------
> drivers/xen/xenfs/xenfs.h | 1 -
Can you use 'git mv' please?
This looks ike you are just moving the file around.
> 7 files changed, 632 insertions(+), 596 deletions(-)
> create mode 100644 drivers/xen/xenbus/xenbus_dev_frontend.c
> delete mode 100644 drivers/xen/xenfs/xenbus.c
>
> diff --git a/drivers/xen/xenbus/Makefile b/drivers/xen/xenbus/Makefile
> index 8dca685..a2ea363 100644
> --- a/drivers/xen/xenbus/Makefile
> +++ b/drivers/xen/xenbus/Makefile
> @@ -1,4 +1,5 @@
> obj-y += xenbus.o
> +obj-y += xenbus_dev_frontend.o
>
> xenbus-objs =
> xenbus-objs += xenbus_client.o
> diff --git a/drivers/xen/xenbus/xenbus_comms.h
> b/drivers/xen/xenbus/xenbus_comms.h
> index c21db75..6e42800 100644
> --- a/drivers/xen/xenbus/xenbus_comms.h
> +++ b/drivers/xen/xenbus/xenbus_comms.h
> @@ -31,6 +31,8 @@
> #ifndef _XENBUS_COMMS_H
> #define _XENBUS_COMMS_H
>
> +#include <linux/fs.h>
> +
> int xs_init(void);
> int xb_init_comms(void);
>
> @@ -43,4 +45,6 @@ int xs_input_avail(void);
> extern struct xenstore_domain_interface *xen_store_interface;
> extern int xen_store_evtchn;
>
> +extern const struct file_operations xen_xenbus_fops;
> +
> #endif /* _XENBUS_COMMS_H */
> diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c
> b/drivers/xen/xenbus/xenbus_dev_frontend.c
> new file mode 100644
> index 0000000..fb30cff
> --- /dev/null
> +++ b/drivers/xen/xenbus/xenbus_dev_frontend.c
> @@ -0,0 +1,624 @@
> +/*
> + * Driver giving user-space access to the kernel's xenbus connection
> + * to xenstore.
> + *
> + * Copyright (c) 2005, Christian Limpach
> + * Copyright (c) 2005, Rusty Russell, IBM Corporation
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation; or, when distributed
> + * separately from the Linux kernel or incorporated into other
> + * software packages, subject to the following license:
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> copy
> + * of this source file (the "Software"), to deal in the Software without
> + * restriction, including without limitation the rights to use, copy, modify,
> + * merge, publish, distribute, sublicense, and/or sell copies of the
> Software,
> + * and to permit persons to whom the Software is furnished to do so, subject
> to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> THE
> + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> DEALINGS
> + * IN THE SOFTWARE.
> + *
> + * Changes:
> + * 2008-10-07 Alex Zeffertt Replaced /proc/xen/xenbus with xenfs
> filesystem
> + * and /proc/xen compatibility mount point.
> + * Turned xenfs into a loadable module.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/uio.h>
> +#include <linux/notifier.h>
> +#include <linux/wait.h>
> +#include <linux/fs.h>
> +#include <linux/poll.h>
> +#include <linux/mutex.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/mount.h>
> +#include <linux/pagemap.h>
> +#include <linux/uaccess.h>
> +#include <linux/init.h>
> +#include <linux/namei.h>
> +#include <linux/string.h>
> +#include <linux/slab.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +
> +#include "xenbus_comms.h"
> +
> +#include <xen/xenbus.h>
> +#include <asm/xen/hypervisor.h>
> +
> +MODULE_LICENSE("GPL");
> +
> +/*
> + * An element of a list of outstanding transactions, for which we're
> + * still waiting a reply.
> + */
> +struct xenbus_transaction_holder {
> + struct list_head list;
> + struct xenbus_transaction handle;
> +};
> +
> +/*
> + * A buffer of data on the queue.
> + */
> +struct read_buffer {
> + struct list_head list;
> + unsigned int cons;
> + unsigned int len;
> + char msg[];
> +};
> +
> +struct xenbus_file_priv {
> + /*
> + * msgbuffer_mutex is held while partial requests are built up
> + * and complete requests are acted on. It therefore protects
> + * the "transactions" and "watches" lists, and the partial
> + * request length and buffer.
> + *
> + * reply_mutex protects the reply being built up to return to
> + * usermode. It nests inside msgbuffer_mutex but may be held
> + * alone during a watch callback.
> + */
> + struct mutex msgbuffer_mutex;
> +
> + /* In-progress transactions */
> + struct list_head transactions;
> +
> + /* Active watches. */
> + struct list_head watches;
> +
> + /* Partial request. */
> + unsigned int len;
> + union {
> + struct xsd_sockmsg msg;
> + char buffer[PAGE_SIZE];
> + } u;
> +
> + /* Response queue. */
> + struct mutex reply_mutex;
> + struct list_head read_buffers;
> + wait_queue_head_t read_waitq;
> +
> +};
> +
> +/* Read out any raw xenbus messages queued up. */
> +static ssize_t xenbus_file_read(struct file *filp,
> + char __user *ubuf,
> + size_t len, loff_t *ppos)
> +{
> + struct xenbus_file_priv *u = filp->private_data;
> + struct read_buffer *rb;
> + unsigned i;
> + int ret;
> +
> + mutex_lock(&u->reply_mutex);
> +again:
> + while (list_empty(&u->read_buffers)) {
> + mutex_unlock(&u->reply_mutex);
> + if (filp->f_flags & O_NONBLOCK)
> + return -EAGAIN;
> +
> + ret = wait_event_interruptible(u->read_waitq,
> + !list_empty(&u->read_buffers));
> + if (ret)
> + return ret;
> + mutex_lock(&u->reply_mutex);
> + }
> +
> + rb = list_entry(u->read_buffers.next, struct read_buffer, list);
> + i = 0;
> + while (i < len) {
> + unsigned sz = min((unsigned)len - i, rb->len - rb->cons);
> +
> + ret = copy_to_user(ubuf + i, &rb->msg[rb->cons], sz);
> +
> + i += sz - ret;
> + rb->cons += sz - ret;
> +
> + if (ret != 0) {
> + if (i == 0)
> + i = -EFAULT;
> + goto out;
> + }
> +
> + /* Clear out buffer if it has been consumed */
> + if (rb->cons == rb->len) {
> + list_del(&rb->list);
> + kfree(rb);
> + if (list_empty(&u->read_buffers))
> + break;
> + rb = list_entry(u->read_buffers.next,
> + struct read_buffer, list);
> + }
> + }
> + if (i == 0)
> + goto again;
> +
> +out:
> + mutex_unlock(&u->reply_mutex);
> + return i;
> +}
> +
> +/*
> + * Add a buffer to the queue. Caller must hold the appropriate lock
> + * if the queue is not local. (Commonly the caller will build up
> + * multiple queued buffers on a temporary local list, and then add it
> + * to the appropriate list under lock once all the buffers have een
> + * successfully allocated.)
> + */
> +static int queue_reply(struct list_head *queue, const void *data, size_t len)
> +{
> + struct read_buffer *rb;
> +
> + if (len == 0)
> + return 0;
> +
> + rb = kmalloc(sizeof(*rb) + len, GFP_KERNEL);
> + if (rb == NULL)
> + return -ENOMEM;
> +
> + rb->cons = 0;
> + rb->len = len;
> +
> + memcpy(rb->msg, data, len);
> +
> + list_add_tail(&rb->list, queue);
> + return 0;
> +}
> +
> +/*
> + * Free all the read_buffer s on a list.
> + * Caller must have sole reference to list.
> + */
> +static void queue_cleanup(struct list_head *list)
> +{
> + struct read_buffer *rb;
> +
> + while (!list_empty(list)) {
> + rb = list_entry(list->next, struct read_buffer, list);
> + list_del(list->next);
> + kfree(rb);
> + }
> +}
> +
> +struct watch_adapter {
> + struct list_head list;
> + struct xenbus_watch watch;
> + struct xenbus_file_priv *dev_data;
> + char *token;
> +};
> +
> +static void free_watch_adapter(struct watch_adapter *watch)
> +{
> + kfree(watch->watch.node);
> + kfree(watch->token);
> + kfree(watch);
> +}
> +
> +static struct watch_adapter *alloc_watch_adapter(const char *path,
> + const char *token)
> +{
> + struct watch_adapter *watch;
> +
> + watch = kzalloc(sizeof(*watch), GFP_KERNEL);
> + if (watch == NULL)
> + goto out_fail;
> +
> + watch->watch.node = kstrdup(path, GFP_KERNEL);
> + if (watch->watch.node == NULL)
> + goto out_free;
> +
> + watch->token = kstrdup(token, GFP_KERNEL);
> + if (watch->token == NULL)
> + goto out_free;
> +
> + return watch;
> +
> +out_free:
> + free_watch_adapter(watch);
> +
> +out_fail:
> + return NULL;
> +}
> +
> +static void watch_fired(struct xenbus_watch *watch,
> + const char **vec,
> + unsigned int len)
> +{
> + struct watch_adapter *adap;
> + struct xsd_sockmsg hdr;
> + const char *path, *token;
> + int path_len, tok_len, body_len, data_len = 0;
> + int ret;
> + LIST_HEAD(staging_q);
> +
> + adap = container_of(watch, struct watch_adapter, watch);
> +
> + path = vec[XS_WATCH_PATH];
> + token = adap->token;
> +
> + path_len = strlen(path) + 1;
> + tok_len = strlen(token) + 1;
> + if (len > 2)
> + data_len = vec[len] - vec[2] + 1;
> + body_len = path_len + tok_len + data_len;
> +
> + hdr.type = XS_WATCH_EVENT;
> + hdr.len = body_len;
> +
> + mutex_lock(&adap->dev_data->reply_mutex);
> +
> + ret = queue_reply(&staging_q, &hdr, sizeof(hdr));
> + if (!ret)
> + ret = queue_reply(&staging_q, path, path_len);
> + if (!ret)
> + ret = queue_reply(&staging_q, token, tok_len);
> + if (!ret && len > 2)
> + ret = queue_reply(&staging_q, vec[2], data_len);
> +
> + if (!ret) {
> + /* success: pass reply list onto watcher */
> + list_splice_tail(&staging_q, &adap->dev_data->read_buffers);
> + wake_up(&adap->dev_data->read_waitq);
> + } else
> + queue_cleanup(&staging_q);
> +
> + mutex_unlock(&adap->dev_data->reply_mutex);
> +}
> +
> +static int xenbus_write_transaction(unsigned msg_type,
> + struct xenbus_file_priv *u)
> +{
> + int rc;
> + void *reply;
> + struct xenbus_transaction_holder *trans = NULL;
> + LIST_HEAD(staging_q);
> +
> + if (msg_type == XS_TRANSACTION_START) {
> + trans = kmalloc(sizeof(*trans), GFP_KERNEL);
> + if (!trans) {
> + rc = -ENOMEM;
> + goto out;
> + }
> + }
> +
> + reply = xenbus_dev_request_and_reply(&u->u.msg);
> + if (IS_ERR(reply)) {
> + kfree(trans);
> + rc = PTR_ERR(reply);
> + goto out;
> + }
> +
> + if (msg_type == XS_TRANSACTION_START) {
> + trans->handle.id = simple_strtoul(reply, NULL, 0);
> +
> + list_add(&trans->list, &u->transactions);
> + } else if (msg_type == XS_TRANSACTION_END) {
> + list_for_each_entry(trans, &u->transactions, list)
> + if (trans->handle.id == u->u.msg.tx_id)
> + break;
> + BUG_ON(&trans->list == &u->transactions);
> + list_del(&trans->list);
> +
> + kfree(trans);
> + }
> +
> + mutex_lock(&u->reply_mutex);
> + rc = queue_reply(&staging_q, &u->u.msg, sizeof(u->u.msg));
> + if (!rc)
> + rc = queue_reply(&staging_q, reply, u->u.msg.len);
> + if (!rc) {
> + list_splice_tail(&staging_q, &u->read_buffers);
> + wake_up(&u->read_waitq);
> + } else {
> + queue_cleanup(&staging_q);
> + }
> + mutex_unlock(&u->reply_mutex);
> +
> + kfree(reply);
> +
> +out:
> + return rc;
> +}
> +
> +static int xenbus_write_watch(unsigned msg_type, struct xenbus_file_priv *u)
> +{
> + struct watch_adapter *watch, *tmp_watch;
> + char *path, *token;
> + int err, rc;
> + LIST_HEAD(staging_q);
> +
> + path = u->u.buffer + sizeof(u->u.msg);
> + token = memchr(path, 0, u->u.msg.len);
> + if (token == NULL) {
> + rc = -EILSEQ;
> + goto out;
> + }
> + token++;
> +
> + if (msg_type == XS_WATCH) {
> + watch = alloc_watch_adapter(path, token);
> + if (watch == NULL) {
> + rc = -ENOMEM;
> + goto out;
> + }
> +
> + watch->watch.callback = watch_fired;
> + watch->dev_data = u;
> +
> + err = register_xenbus_watch(&watch->watch);
> + if (err) {
> + free_watch_adapter(watch);
> + rc = err;
> + goto out;
> + }
> + list_add(&watch->list, &u->watches);
> + } else {
> + list_for_each_entry_safe(watch, tmp_watch, &u->watches, list) {
> + if (!strcmp(watch->token, token) &&
> + !strcmp(watch->watch.node, path)) {
> + unregister_xenbus_watch(&watch->watch);
> + list_del(&watch->list);
> + free_watch_adapter(watch);
> + break;
> + }
> + }
> + }
> +
> + /* Success. Synthesize a reply to say all is OK. */
> + {
> + struct {
> + struct xsd_sockmsg hdr;
> + char body[3];
> + } __packed reply = {
> + {
> + .type = msg_type,
> + .len = sizeof(reply.body)
> + },
> + "OK"
> + };
> +
> + mutex_lock(&u->reply_mutex);
> + rc = queue_reply(&u->read_buffers, &reply, sizeof(reply));
> + wake_up(&u->read_waitq);
> + mutex_unlock(&u->reply_mutex);
> + }
> +
> +out:
> + return rc;
> +}
> +
> +static ssize_t xenbus_file_write(struct file *filp,
> + const char __user *ubuf,
> + size_t len, loff_t *ppos)
> +{
> + struct xenbus_file_priv *u = filp->private_data;
> + uint32_t msg_type;
> + int rc = len;
> + int ret;
> + LIST_HEAD(staging_q);
> +
> + /*
> + * We're expecting usermode to be writing properly formed
> + * xenbus messages. If they write an incomplete message we
> + * buffer it up. Once it is complete, we act on it.
> + */
> +
> + /*
> + * Make sure concurrent writers can't stomp all over each
> + * other's messages and make a mess of our partial message
> + * buffer. We don't make any attemppt to stop multiple
> + * writers from making a mess of each other's incomplete
> + * messages; we're just trying to guarantee our own internal
> + * consistency and make sure that single writes are handled
> + * atomically.
> + */
> + mutex_lock(&u->msgbuffer_mutex);
> +
> + /* Get this out of the way early to avoid confusion */
> + if (len == 0)
> + goto out;
> +
> + /* Can't write a xenbus message larger we can buffer */
> + if ((len + u->len) > sizeof(u->u.buffer)) {
> + /* On error, dump existing buffer */
> + u->len = 0;
> + rc = -EINVAL;
> + goto out;
> + }
> +
> + ret = copy_from_user(u->u.buffer + u->len, ubuf, len);
> +
> + if (ret != 0) {
> + rc = -EFAULT;
> + goto out;
> + }
> +
> + /* Deal with a partial copy. */
> + len -= ret;
> + rc = len;
> +
> + u->len += len;
> +
> + /* Return if we haven't got a full message yet */
> + if (u->len < sizeof(u->u.msg))
> + goto out; /* not even the header yet */
> +
> + /* If we're expecting a message that's larger than we can
> + possibly send, dump what we have and return an error. */
> + if ((sizeof(u->u.msg) + u->u.msg.len) > sizeof(u->u.buffer)) {
> + rc = -E2BIG;
> + u->len = 0;
> + goto out;
> + }
> +
> + if (u->len < (sizeof(u->u.msg) + u->u.msg.len))
> + goto out; /* incomplete data portion */
> +
> + /*
> + * OK, now we have a complete message. Do something with it.
> + */
> +
> + msg_type = u->u.msg.type;
> +
> + switch (msg_type) {
> + case XS_WATCH:
> + case XS_UNWATCH:
> + /* (Un)Ask for some path to be watched for changes */
> + ret = xenbus_write_watch(msg_type, u);
> + break;
> +
> + default:
> + /* Send out a transaction */
> + ret = xenbus_write_transaction(msg_type, u);
> + break;
> + }
> + if (ret != 0)
> + rc = ret;
> +
> + /* Buffered message consumed */
> + u->len = 0;
> +
> + out:
> + mutex_unlock(&u->msgbuffer_mutex);
> + return rc;
> +}
> +
> +static int xenbus_file_open(struct inode *inode, struct file *filp)
> +{
> + struct xenbus_file_priv *u;
> +
> + if (xen_store_evtchn == 0)
> + return -ENOENT;
> +
> + nonseekable_open(inode, filp);
> +
> + u = kzalloc(sizeof(*u), GFP_KERNEL);
> + if (u == NULL)
> + return -ENOMEM;
> +
> + INIT_LIST_HEAD(&u->transactions);
> + INIT_LIST_HEAD(&u->watches);
> + INIT_LIST_HEAD(&u->read_buffers);
> + init_waitqueue_head(&u->read_waitq);
> +
> + mutex_init(&u->reply_mutex);
> + mutex_init(&u->msgbuffer_mutex);
> +
> + filp->private_data = u;
> +
> + return 0;
> +}
> +
> +static int xenbus_file_release(struct inode *inode, struct file *filp)
> +{
> + struct xenbus_file_priv *u = filp->private_data;
> + struct xenbus_transaction_holder *trans, *tmp;
> + struct watch_adapter *watch, *tmp_watch;
> + struct read_buffer *rb, *tmp_rb;
> +
> + /*
> + * No need for locking here because there are no other users,
> + * by definition.
> + */
> +
> + 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);
> + }
> +
> + list_for_each_entry_safe(rb, tmp_rb, &u->read_buffers, list) {
> + list_del(&rb->list);
> + kfree(rb);
> + }
> + kfree(u);
> +
> + return 0;
> +}
> +
> +static unsigned int xenbus_file_poll(struct file *file, poll_table *wait)
> +{
> + struct xenbus_file_priv *u = file->private_data;
> +
> + poll_wait(file, &u->read_waitq, wait);
> + if (!list_empty(&u->read_buffers))
> + return POLLIN | POLLRDNORM;
> + return 0;
> +}
> +
> +const struct file_operations xen_xenbus_fops = {
> + .read = xenbus_file_read,
> + .write = xenbus_file_write,
> + .open = xenbus_file_open,
> + .release = xenbus_file_release,
> + .poll = xenbus_file_poll,
> + .llseek = no_llseek,
> +};
> +EXPORT_SYMBOL_GPL(xen_xenbus_fops);
> +
> +static struct miscdevice xenbus_dev = {
> + .minor = MISC_DYNAMIC_MINOR,
> + .name = "xen/xenbus",
> + .fops = &xen_xenbus_fops,
> +};
> +
> +static int __init xenbus_init(void)
> +{
> + int err;
> +
> + if (!xen_domain())
> + return -ENODEV;
> +
> + err = misc_register(&xenbus_dev);
> + if (err)
> + printk(KERN_ERR "Could not register xenbus device\n");
> + return err;
> +}
> +
> +static void __exit xenbus_exit(void)
> +{
> + misc_deregister(&xenbus_dev);
> +}
> +
> +module_init(xenbus_init);
> +module_exit(xenbus_exit);
> diff --git a/drivers/xen/xenfs/Makefile b/drivers/xen/xenfs/Makefile
> index 5d45ff1..b019865 100644
> --- a/drivers/xen/xenfs/Makefile
> +++ b/drivers/xen/xenfs/Makefile
> @@ -1,4 +1,4 @@
> obj-$(CONFIG_XENFS) += xenfs.o
>
> -xenfs-y = super.o xenbus.o
> +xenfs-y = super.o
> xenfs-$(CONFIG_XEN_DOM0) += xenstored.o
> diff --git a/drivers/xen/xenfs/super.c b/drivers/xen/xenfs/super.c
> index a55fbf9..a84b53c 100644
> --- a/drivers/xen/xenfs/super.c
> +++ b/drivers/xen/xenfs/super.c
> @@ -17,6 +17,7 @@
>
> #include "xenfs.h"
> #include "../privcmd.h"
> +#include "../xenbus/xenbus_comms.h"
>
> #include <asm/xen/hypervisor.h>
>
> @@ -83,7 +84,7 @@ static int xenfs_fill_super(struct super_block *sb, void
> *data, int silent)
> {
> static struct tree_descr xenfs_files[] = {
> [1] = {},
> - { "xenbus", &xenbus_file_ops, S_IRUSR|S_IWUSR },
> + { "xenbus", &xen_xenbus_fops, S_IRUSR|S_IWUSR },
> { "capabilities", &capabilities_file_ops, S_IRUGO },
> { "privcmd", &xen_privcmd_fops, S_IRUSR|S_IWUSR },
> {""},
> diff --git a/drivers/xen/xenfs/xenbus.c b/drivers/xen/xenfs/xenbus.c
> deleted file mode 100644
> index bbd000f..0000000
> --- a/drivers/xen/xenfs/xenbus.c
> +++ /dev/null
> @@ -1,593 +0,0 @@
> -/*
> - * Driver giving user-space access to the kernel's xenbus connection
> - * to xenstore.
> - *
> - * Copyright (c) 2005, Christian Limpach
> - * Copyright (c) 2005, Rusty Russell, IBM Corporation
> - *
> - * This program is free software; you can redistribute it and/or
> - * modify it under the terms of the GNU General Public License version 2
> - * as published by the Free Software Foundation; or, when distributed
> - * separately from the Linux kernel or incorporated into other
> - * software packages, subject to the following license:
> - *
> - * Permission is hereby granted, free of charge, to any person obtaining a
> copy
> - * of this source file (the "Software"), to deal in the Software without
> - * restriction, including without limitation the rights to use, copy, modify,
> - * merge, publish, distribute, sublicense, and/or sell copies of the
> Software,
> - * and to permit persons to whom the Software is furnished to do so, subject
> to
> - * the following conditions:
> - *
> - * The above copyright notice and this permission notice shall be included in
> - * all copies or substantial portions of the Software.
> - *
> - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> THE
> - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> DEALINGS
> - * IN THE SOFTWARE.
> - *
> - * Changes:
> - * 2008-10-07 Alex Zeffertt Replaced /proc/xen/xenbus with xenfs
> filesystem
> - * and /proc/xen compatibility mount point.
> - * Turned xenfs into a loadable module.
> - */
> -
> -#include <linux/kernel.h>
> -#include <linux/errno.h>
> -#include <linux/uio.h>
> -#include <linux/notifier.h>
> -#include <linux/wait.h>
> -#include <linux/fs.h>
> -#include <linux/poll.h>
> -#include <linux/mutex.h>
> -#include <linux/sched.h>
> -#include <linux/spinlock.h>
> -#include <linux/mount.h>
> -#include <linux/pagemap.h>
> -#include <linux/uaccess.h>
> -#include <linux/init.h>
> -#include <linux/namei.h>
> -#include <linux/string.h>
> -#include <linux/slab.h>
> -
> -#include "xenfs.h"
> -#include "../xenbus/xenbus_comms.h"
> -
> -#include <xen/xenbus.h>
> -#include <asm/xen/hypervisor.h>
> -
> -/*
> - * An element of a list of outstanding transactions, for which we're
> - * still waiting a reply.
> - */
> -struct xenbus_transaction_holder {
> - struct list_head list;
> - struct xenbus_transaction handle;
> -};
> -
> -/*
> - * A buffer of data on the queue.
> - */
> -struct read_buffer {
> - struct list_head list;
> - unsigned int cons;
> - unsigned int len;
> - char msg[];
> -};
> -
> -struct xenbus_file_priv {
> - /*
> - * msgbuffer_mutex is held while partial requests are built up
> - * and complete requests are acted on. It therefore protects
> - * the "transactions" and "watches" lists, and the partial
> - * request length and buffer.
> - *
> - * reply_mutex protects the reply being built up to return to
> - * usermode. It nests inside msgbuffer_mutex but may be held
> - * alone during a watch callback.
> - */
> - struct mutex msgbuffer_mutex;
> -
> - /* In-progress transactions */
> - struct list_head transactions;
> -
> - /* Active watches. */
> - struct list_head watches;
> -
> - /* Partial request. */
> - unsigned int len;
> - union {
> - struct xsd_sockmsg msg;
> - char buffer[PAGE_SIZE];
> - } u;
> -
> - /* Response queue. */
> - struct mutex reply_mutex;
> - struct list_head read_buffers;
> - wait_queue_head_t read_waitq;
> -
> -};
> -
> -/* Read out any raw xenbus messages queued up. */
> -static ssize_t xenbus_file_read(struct file *filp,
> - char __user *ubuf,
> - size_t len, loff_t *ppos)
> -{
> - struct xenbus_file_priv *u = filp->private_data;
> - struct read_buffer *rb;
> - unsigned i;
> - int ret;
> -
> - mutex_lock(&u->reply_mutex);
> -again:
> - while (list_empty(&u->read_buffers)) {
> - mutex_unlock(&u->reply_mutex);
> - if (filp->f_flags & O_NONBLOCK)
> - return -EAGAIN;
> -
> - ret = wait_event_interruptible(u->read_waitq,
> - !list_empty(&u->read_buffers));
> - if (ret)
> - return ret;
> - mutex_lock(&u->reply_mutex);
> - }
> -
> - rb = list_entry(u->read_buffers.next, struct read_buffer, list);
> - i = 0;
> - while (i < len) {
> - unsigned sz = min((unsigned)len - i, rb->len - rb->cons);
> -
> - ret = copy_to_user(ubuf + i, &rb->msg[rb->cons], sz);
> -
> - i += sz - ret;
> - rb->cons += sz - ret;
> -
> - if (ret != 0) {
> - if (i == 0)
> - i = -EFAULT;
> - goto out;
> - }
> -
> - /* Clear out buffer if it has been consumed */
> - if (rb->cons == rb->len) {
> - list_del(&rb->list);
> - kfree(rb);
> - if (list_empty(&u->read_buffers))
> - break;
> - rb = list_entry(u->read_buffers.next,
> - struct read_buffer, list);
> - }
> - }
> - if (i == 0)
> - goto again;
> -
> -out:
> - mutex_unlock(&u->reply_mutex);
> - return i;
> -}
> -
> -/*
> - * Add a buffer to the queue. Caller must hold the appropriate lock
> - * if the queue is not local. (Commonly the caller will build up
> - * multiple queued buffers on a temporary local list, and then add it
> - * to the appropriate list under lock once all the buffers have een
> - * successfully allocated.)
> - */
> -static int queue_reply(struct list_head *queue, const void *data, size_t len)
> -{
> - struct read_buffer *rb;
> -
> - if (len == 0)
> - return 0;
> -
> - rb = kmalloc(sizeof(*rb) + len, GFP_KERNEL);
> - if (rb == NULL)
> - return -ENOMEM;
> -
> - rb->cons = 0;
> - rb->len = len;
> -
> - memcpy(rb->msg, data, len);
> -
> - list_add_tail(&rb->list, queue);
> - return 0;
> -}
> -
> -/*
> - * Free all the read_buffer s on a list.
> - * Caller must have sole reference to list.
> - */
> -static void queue_cleanup(struct list_head *list)
> -{
> - struct read_buffer *rb;
> -
> - while (!list_empty(list)) {
> - rb = list_entry(list->next, struct read_buffer, list);
> - list_del(list->next);
> - kfree(rb);
> - }
> -}
> -
> -struct watch_adapter {
> - struct list_head list;
> - struct xenbus_watch watch;
> - struct xenbus_file_priv *dev_data;
> - char *token;
> -};
> -
> -static void free_watch_adapter(struct watch_adapter *watch)
> -{
> - kfree(watch->watch.node);
> - kfree(watch->token);
> - kfree(watch);
> -}
> -
> -static struct watch_adapter *alloc_watch_adapter(const char *path,
> - const char *token)
> -{
> - struct watch_adapter *watch;
> -
> - watch = kzalloc(sizeof(*watch), GFP_KERNEL);
> - if (watch == NULL)
> - goto out_fail;
> -
> - watch->watch.node = kstrdup(path, GFP_KERNEL);
> - if (watch->watch.node == NULL)
> - goto out_free;
> -
> - watch->token = kstrdup(token, GFP_KERNEL);
> - if (watch->token == NULL)
> - goto out_free;
> -
> - return watch;
> -
> -out_free:
> - free_watch_adapter(watch);
> -
> -out_fail:
> - return NULL;
> -}
> -
> -static void watch_fired(struct xenbus_watch *watch,
> - const char **vec,
> - unsigned int len)
> -{
> - struct watch_adapter *adap;
> - struct xsd_sockmsg hdr;
> - const char *path, *token;
> - int path_len, tok_len, body_len, data_len = 0;
> - int ret;
> - LIST_HEAD(staging_q);
> -
> - adap = container_of(watch, struct watch_adapter, watch);
> -
> - path = vec[XS_WATCH_PATH];
> - token = adap->token;
> -
> - path_len = strlen(path) + 1;
> - tok_len = strlen(token) + 1;
> - if (len > 2)
> - data_len = vec[len] - vec[2] + 1;
> - body_len = path_len + tok_len + data_len;
> -
> - hdr.type = XS_WATCH_EVENT;
> - hdr.len = body_len;
> -
> - mutex_lock(&adap->dev_data->reply_mutex);
> -
> - ret = queue_reply(&staging_q, &hdr, sizeof(hdr));
> - if (!ret)
> - ret = queue_reply(&staging_q, path, path_len);
> - if (!ret)
> - ret = queue_reply(&staging_q, token, tok_len);
> - if (!ret && len > 2)
> - ret = queue_reply(&staging_q, vec[2], data_len);
> -
> - if (!ret) {
> - /* success: pass reply list onto watcher */
> - list_splice_tail(&staging_q, &adap->dev_data->read_buffers);
> - wake_up(&adap->dev_data->read_waitq);
> - } else
> - queue_cleanup(&staging_q);
> -
> - mutex_unlock(&adap->dev_data->reply_mutex);
> -}
> -
> -static int xenbus_write_transaction(unsigned msg_type,
> - struct xenbus_file_priv *u)
> -{
> - int rc;
> - void *reply;
> - struct xenbus_transaction_holder *trans = NULL;
> - LIST_HEAD(staging_q);
> -
> - if (msg_type == XS_TRANSACTION_START) {
> - trans = kmalloc(sizeof(*trans), GFP_KERNEL);
> - if (!trans) {
> - rc = -ENOMEM;
> - goto out;
> - }
> - }
> -
> - reply = xenbus_dev_request_and_reply(&u->u.msg);
> - if (IS_ERR(reply)) {
> - kfree(trans);
> - rc = PTR_ERR(reply);
> - goto out;
> - }
> -
> - if (msg_type == XS_TRANSACTION_START) {
> - trans->handle.id = simple_strtoul(reply, NULL, 0);
> -
> - list_add(&trans->list, &u->transactions);
> - } else if (msg_type == XS_TRANSACTION_END) {
> - list_for_each_entry(trans, &u->transactions, list)
> - if (trans->handle.id == u->u.msg.tx_id)
> - break;
> - BUG_ON(&trans->list == &u->transactions);
> - list_del(&trans->list);
> -
> - kfree(trans);
> - }
> -
> - mutex_lock(&u->reply_mutex);
> - rc = queue_reply(&staging_q, &u->u.msg, sizeof(u->u.msg));
> - if (!rc)
> - rc = queue_reply(&staging_q, reply, u->u.msg.len);
> - if (!rc) {
> - list_splice_tail(&staging_q, &u->read_buffers);
> - wake_up(&u->read_waitq);
> - } else {
> - queue_cleanup(&staging_q);
> - }
> - mutex_unlock(&u->reply_mutex);
> -
> - kfree(reply);
> -
> -out:
> - return rc;
> -}
> -
> -static int xenbus_write_watch(unsigned msg_type, struct xenbus_file_priv *u)
> -{
> - struct watch_adapter *watch, *tmp_watch;
> - char *path, *token;
> - int err, rc;
> - LIST_HEAD(staging_q);
> -
> - path = u->u.buffer + sizeof(u->u.msg);
> - token = memchr(path, 0, u->u.msg.len);
> - if (token == NULL) {
> - rc = -EILSEQ;
> - goto out;
> - }
> - token++;
> -
> - if (msg_type == XS_WATCH) {
> - watch = alloc_watch_adapter(path, token);
> - if (watch == NULL) {
> - rc = -ENOMEM;
> - goto out;
> - }
> -
> - watch->watch.callback = watch_fired;
> - watch->dev_data = u;
> -
> - err = register_xenbus_watch(&watch->watch);
> - if (err) {
> - free_watch_adapter(watch);
> - rc = err;
> - goto out;
> - }
> - list_add(&watch->list, &u->watches);
> - } else {
> - list_for_each_entry_safe(watch, tmp_watch, &u->watches, list) {
> - if (!strcmp(watch->token, token) &&
> - !strcmp(watch->watch.node, path)) {
> - unregister_xenbus_watch(&watch->watch);
> - list_del(&watch->list);
> - free_watch_adapter(watch);
> - break;
> - }
> - }
> - }
> -
> - /* Success. Synthesize a reply to say all is OK. */
> - {
> - struct {
> - struct xsd_sockmsg hdr;
> - char body[3];
> - } __packed reply = {
> - {
> - .type = msg_type,
> - .len = sizeof(reply.body)
> - },
> - "OK"
> - };
> -
> - mutex_lock(&u->reply_mutex);
> - rc = queue_reply(&u->read_buffers, &reply, sizeof(reply));
> - wake_up(&u->read_waitq);
> - mutex_unlock(&u->reply_mutex);
> - }
> -
> -out:
> - return rc;
> -}
> -
> -static ssize_t xenbus_file_write(struct file *filp,
> - const char __user *ubuf,
> - size_t len, loff_t *ppos)
> -{
> - struct xenbus_file_priv *u = filp->private_data;
> - uint32_t msg_type;
> - int rc = len;
> - int ret;
> - LIST_HEAD(staging_q);
> -
> - /*
> - * We're expecting usermode to be writing properly formed
> - * xenbus messages. If they write an incomplete message we
> - * buffer it up. Once it is complete, we act on it.
> - */
> -
> - /*
> - * Make sure concurrent writers can't stomp all over each
> - * other's messages and make a mess of our partial message
> - * buffer. We don't make any attemppt to stop multiple
> - * writers from making a mess of each other's incomplete
> - * messages; we're just trying to guarantee our own internal
> - * consistency and make sure that single writes are handled
> - * atomically.
> - */
> - mutex_lock(&u->msgbuffer_mutex);
> -
> - /* Get this out of the way early to avoid confusion */
> - if (len == 0)
> - goto out;
> -
> - /* Can't write a xenbus message larger we can buffer */
> - if ((len + u->len) > sizeof(u->u.buffer)) {
> - /* On error, dump existing buffer */
> - u->len = 0;
> - rc = -EINVAL;
> - goto out;
> - }
> -
> - ret = copy_from_user(u->u.buffer + u->len, ubuf, len);
> -
> - if (ret != 0) {
> - rc = -EFAULT;
> - goto out;
> - }
> -
> - /* Deal with a partial copy. */
> - len -= ret;
> - rc = len;
> -
> - u->len += len;
> -
> - /* Return if we haven't got a full message yet */
> - if (u->len < sizeof(u->u.msg))
> - goto out; /* not even the header yet */
> -
> - /* If we're expecting a message that's larger than we can
> - possibly send, dump what we have and return an error. */
> - if ((sizeof(u->u.msg) + u->u.msg.len) > sizeof(u->u.buffer)) {
> - rc = -E2BIG;
> - u->len = 0;
> - goto out;
> - }
> -
> - if (u->len < (sizeof(u->u.msg) + u->u.msg.len))
> - goto out; /* incomplete data portion */
> -
> - /*
> - * OK, now we have a complete message. Do something with it.
> - */
> -
> - msg_type = u->u.msg.type;
> -
> - switch (msg_type) {
> - case XS_WATCH:
> - case XS_UNWATCH:
> - /* (Un)Ask for some path to be watched for changes */
> - ret = xenbus_write_watch(msg_type, u);
> - break;
> -
> - default:
> - /* Send out a transaction */
> - ret = xenbus_write_transaction(msg_type, u);
> - break;
> - }
> - if (ret != 0)
> - rc = ret;
> -
> - /* Buffered message consumed */
> - u->len = 0;
> -
> - out:
> - mutex_unlock(&u->msgbuffer_mutex);
> - return rc;
> -}
> -
> -static int xenbus_file_open(struct inode *inode, struct file *filp)
> -{
> - struct xenbus_file_priv *u;
> -
> - if (xen_store_evtchn == 0)
> - return -ENOENT;
> -
> - nonseekable_open(inode, filp);
> -
> - u = kzalloc(sizeof(*u), GFP_KERNEL);
> - if (u == NULL)
> - return -ENOMEM;
> -
> - INIT_LIST_HEAD(&u->transactions);
> - INIT_LIST_HEAD(&u->watches);
> - INIT_LIST_HEAD(&u->read_buffers);
> - init_waitqueue_head(&u->read_waitq);
> -
> - mutex_init(&u->reply_mutex);
> - mutex_init(&u->msgbuffer_mutex);
> -
> - filp->private_data = u;
> -
> - return 0;
> -}
> -
> -static int xenbus_file_release(struct inode *inode, struct file *filp)
> -{
> - struct xenbus_file_priv *u = filp->private_data;
> - struct xenbus_transaction_holder *trans, *tmp;
> - struct watch_adapter *watch, *tmp_watch;
> - struct read_buffer *rb, *tmp_rb;
> -
> - /*
> - * No need for locking here because there are no other users,
> - * by definition.
> - */
> -
> - 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);
> - }
> -
> - list_for_each_entry_safe(rb, tmp_rb, &u->read_buffers, list) {
> - list_del(&rb->list);
> - kfree(rb);
> - }
> - kfree(u);
> -
> - return 0;
> -}
> -
> -static unsigned int xenbus_file_poll(struct file *file, poll_table *wait)
> -{
> - struct xenbus_file_priv *u = file->private_data;
> -
> - poll_wait(file, &u->read_waitq, wait);
> - if (!list_empty(&u->read_buffers))
> - return POLLIN | POLLRDNORM;
> - return 0;
> -}
> -
> -const struct file_operations xenbus_file_ops = {
> - .read = xenbus_file_read,
> - .write = xenbus_file_write,
> - .open = xenbus_file_open,
> - .release = xenbus_file_release,
> - .poll = xenbus_file_poll,
> - .llseek = no_llseek,
> -};
> diff --git a/drivers/xen/xenfs/xenfs.h b/drivers/xen/xenfs/xenfs.h
> index 5056306..6b80c77 100644
> --- a/drivers/xen/xenfs/xenfs.h
> +++ b/drivers/xen/xenfs/xenfs.h
> @@ -1,7 +1,6 @@
> #ifndef _XENFS_XENBUS_H
> #define _XENFS_XENBUS_H
>
> -extern const struct file_operations xenbus_file_ops;
> extern const struct file_operations xsd_kva_file_ops;
> extern const struct file_operations xsd_port_file_ops;
>
> --
> 1.7.7.3
>
>
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@xxxxxxxxxxxxxxxxxxx
> http://lists.xensource.com/xen-devel
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |